mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #105 from chriseth/fix_calldataUnpacker
Bugfix in calldata unpacker.
This commit is contained in:
commit
0bedebe9b5
@ -210,13 +210,10 @@ void Compiler::appendConstructor(FunctionDefinition const& _constructor)
|
|||||||
m_context << eth::Instruction::DUP1;
|
m_context << eth::Instruction::DUP1;
|
||||||
m_context.appendProgramSize();
|
m_context.appendProgramSize();
|
||||||
m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY;
|
m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY;
|
||||||
m_context << eth::Instruction::ADD;
|
m_context << eth::Instruction::DUP2 << eth::Instruction::ADD;
|
||||||
CompilerUtils(m_context).storeFreeMemoryPointer();
|
CompilerUtils(m_context).storeFreeMemoryPointer();
|
||||||
appendCalldataUnpacker(
|
// stack: <memptr>
|
||||||
FunctionType(_constructor).parameterTypes(),
|
appendCalldataUnpacker(FunctionType(_constructor).parameterTypes(), true);
|
||||||
true,
|
|
||||||
CompilerUtils::freeMemoryPointer + 0x20
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_constructor.accept(*this);
|
_constructor.accept(*this);
|
||||||
}
|
}
|
||||||
@ -267,6 +264,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
|
|||||||
CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
|
CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
|
||||||
m_context << callDataUnpackerEntryPoints.at(it.first);
|
m_context << callDataUnpackerEntryPoints.at(it.first);
|
||||||
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
||||||
|
m_context << CompilerUtils::dataStartOffset;
|
||||||
appendCalldataUnpacker(functionType->parameterTypes());
|
appendCalldataUnpacker(functionType->parameterTypes());
|
||||||
m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration()));
|
m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration()));
|
||||||
m_context << returnTag;
|
m_context << returnTag;
|
||||||
@ -274,23 +272,17 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::appendCalldataUnpacker(
|
void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory)
|
||||||
TypePointers const& _typeParameters,
|
|
||||||
bool _fromMemory,
|
|
||||||
u256 _startOffset
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
// We do not check the calldata size, everything is zero-paddedd
|
// We do not check the calldata size, everything is zero-padded
|
||||||
|
|
||||||
//@todo this does not yet support nested dynamic arrays
|
//@todo this does not yet support nested dynamic arrays
|
||||||
|
|
||||||
if (_startOffset == u256(-1))
|
// Retain the offset pointer as base_offset, the point from which the data offsets are computed.
|
||||||
_startOffset = u256(CompilerUtils::dataStartOffset);
|
m_context << eth::Instruction::DUP1;
|
||||||
|
|
||||||
m_context << _startOffset;
|
|
||||||
for (TypePointer const& type: _typeParameters)
|
for (TypePointer const& type: _typeParameters)
|
||||||
{
|
{
|
||||||
// stack: v1 v2 ... v(k-1) mem_offset
|
// stack: v1 v2 ... v(k-1) base_offset current_offset
|
||||||
switch (type->category())
|
switch (type->category())
|
||||||
{
|
{
|
||||||
case Type::Category::Array:
|
case Type::Category::Array:
|
||||||
@ -309,9 +301,9 @@ void Compiler::appendCalldataUnpacker(
|
|||||||
solAssert(arrayType.location() == DataLocation::Memory, "");
|
solAssert(arrayType.location() == DataLocation::Memory, "");
|
||||||
// compute data pointer
|
// compute data pointer
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD;
|
m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD;
|
||||||
//@todo once we support nested arrays, this offset needs to be dynamic.
|
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
||||||
m_context << _startOffset << eth::Instruction::ADD;
|
m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
|
||||||
m_context << eth::Instruction::SWAP1 << u256(0x20) << eth::Instruction::ADD;
|
m_context << u256(0x20) << eth::Instruction::ADD;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -321,14 +313,14 @@ void Compiler::appendCalldataUnpacker(
|
|||||||
{
|
{
|
||||||
// put on stack: data_pointer length
|
// put on stack: data_pointer length
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
|
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
|
||||||
// stack: data_offset next_pointer
|
// stack: base_offset data_offset next_pointer
|
||||||
//@todo once we support nested arrays, this offset needs to be dynamic.
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
||||||
m_context << eth::Instruction::SWAP1 << _startOffset << eth::Instruction::ADD;
|
// stack: base_offset next_pointer data_pointer
|
||||||
// stack: next_pointer data_pointer
|
|
||||||
// retrieve length
|
// retrieve length
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true);
|
CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true);
|
||||||
// stack: next_pointer length data_pointer
|
// stack: base_offset next_pointer length data_pointer
|
||||||
m_context << eth::Instruction::SWAP2;
|
m_context << eth::Instruction::SWAP2;
|
||||||
|
// stack: base_offset data_pointer length next_pointer
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -338,7 +330,7 @@ void Compiler::appendCalldataUnpacker(
|
|||||||
}
|
}
|
||||||
if (arrayType.location() == DataLocation::Memory)
|
if (arrayType.location() == DataLocation::Memory)
|
||||||
{
|
{
|
||||||
// stack: calldata_ref [length] next_calldata
|
// stack: base_offset calldata_ref [length] next_calldata
|
||||||
// copy to memory
|
// copy to memory
|
||||||
// move calldata type up again
|
// move calldata type up again
|
||||||
CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack());
|
CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack());
|
||||||
@ -346,15 +338,21 @@ void Compiler::appendCalldataUnpacker(
|
|||||||
// fetch next pointer again
|
// fetch next pointer again
|
||||||
CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack());
|
CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack());
|
||||||
}
|
}
|
||||||
|
// move base_offset up
|
||||||
|
CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack());
|
||||||
|
m_context << eth::Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString());
|
solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString());
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true);
|
CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true);
|
||||||
|
CompilerUtils(m_context).moveToStackTop(1 + type->sizeOnStack());
|
||||||
|
m_context << eth::Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
|
// stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset
|
||||||
}
|
}
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
|
void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
|
||||||
|
@ -85,12 +85,8 @@ private:
|
|||||||
void appendFunctionSelector(ContractDefinition const& _contract);
|
void appendFunctionSelector(ContractDefinition const& _contract);
|
||||||
/// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
|
/// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
|
||||||
/// From memory if @a _fromMemory is true, otherwise from call data.
|
/// From memory if @a _fromMemory is true, otherwise from call data.
|
||||||
/// Expects source offset on the stack.
|
/// Expects source offset on the stack, which is removed.
|
||||||
void appendCalldataUnpacker(
|
void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false);
|
||||||
TypePointers const& _typeParameters,
|
|
||||||
bool _fromMemory = false,
|
|
||||||
u256 _startOffset = u256(-1)
|
|
||||||
);
|
|
||||||
void appendReturnValuePacker(TypePointers const& _typeParameters);
|
void appendReturnValuePacker(TypePointers const& _typeParameters);
|
||||||
|
|
||||||
void registerStateVariables(ContractDefinition const& _contract);
|
void registerStateVariables(ContractDefinition const& _contract);
|
||||||
|
@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE(location_test)
|
|||||||
AssemblyItems items = compileContract(sourceCode);
|
AssemblyItems items = compileContract(sourceCode);
|
||||||
vector<SourceLocation> locations =
|
vector<SourceLocation> locations =
|
||||||
vector<SourceLocation>(17, SourceLocation(2, 75, n)) +
|
vector<SourceLocation>(17, SourceLocation(2, 75, n)) +
|
||||||
vector<SourceLocation>(26, SourceLocation(20, 72, n)) +
|
vector<SourceLocation>(28, SourceLocation(20, 72, n)) +
|
||||||
vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} +
|
vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} +
|
||||||
vector<SourceLocation>(4, SourceLocation(58, 67, n)) +
|
vector<SourceLocation>(4, SourceLocation(58, 67, n)) +
|
||||||
vector<SourceLocation>(3, SourceLocation(20, 72, n));
|
vector<SourceLocation>(3, SourceLocation(20, 72, n));
|
||||||
|
@ -5354,6 +5354,25 @@ BOOST_AUTO_TEST_CASE(fixed_arrays_as_return_type)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(calldata_offset)
|
||||||
|
{
|
||||||
|
// This tests a specific bug that was caused by not using the correct memory offset in the
|
||||||
|
// calldata unpacker.
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract CB
|
||||||
|
{
|
||||||
|
address[] _arr;
|
||||||
|
string public last = "nd";
|
||||||
|
function CB(address[] guardians)
|
||||||
|
{
|
||||||
|
_arr = guardians;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "CB", encodeArgs(u256(0x20)));
|
||||||
|
BOOST_CHECK(callContractFunction("last()", encodeArgs()) == encodeDyn(string("nd")));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user