diff options
author | chriseth <chris@ethereum.org> | 2017-12-14 00:23:37 +0800 |
---|---|---|
committer | Alex Beregszaszi <alex@rtfs.hu> | 2018-03-21 22:53:25 +0800 |
commit | 32c94f505901201126000eb12087251f5695acbd (patch) | |
tree | 2e398f44ddfe0784d65688f4dd4286e9befc5aeb /libsolidity/codegen | |
parent | 2cdf44f65cdd15f31293ae2fa78d9996d6219af0 (diff) | |
download | dexon-solidity-32c94f505901201126000eb12087251f5695acbd.tar.gz dexon-solidity-32c94f505901201126000eb12087251f5695acbd.tar.zst dexon-solidity-32c94f505901201126000eb12087251f5695acbd.zip |
Simple size check for old ABI decoder.
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 117 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 10 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 2 |
3 files changed, 97 insertions, 32 deletions
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 704d8da8..d9177312 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -22,11 +22,14 @@ #include <libsolidity/codegen/CompilerUtils.h> #include <libsolidity/ast/AST.h> -#include <libevmasm/Instruction.h> #include <libsolidity/codegen/ArrayUtils.h> #include <libsolidity/codegen/LValue.h> #include <libsolidity/codegen/ABIFunctions.h> +#include <libevmasm/Instruction.h> + +#include <libdevcore/Whiskers.h> + using namespace std; namespace dev @@ -159,26 +162,37 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound } } -void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory) +void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory, bool _revertOnOutOfBounds) { - // We do not check the calldata size, everything is zero-padded - + /// Stack: <source_offset> <length> if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) { // Use the new JULIA-based decoding function auto stackHeightBefore = m_context.stackHeight(); abiDecodeV2(_typeParameters, _fromMemory); - solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 1, ""); + solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2, ""); return; } //@todo this does not yet support nested dynamic arrays + if (_revertOnOutOfBounds) + { + size_t encodedSize = 0; + for (auto const& t: _typeParameters) + encodedSize += t->decodingType()->calldataEncodedSize(true); + m_context.appendInlineAssembly("{ if lt(len, " + to_string(encodedSize) + ") { revert(0, 0) } }", {"len"}); + } + + m_context << Instruction::DUP2 << Instruction::ADD; + m_context << Instruction::SWAP1; + /// Stack: <input_end> <source_offset> + // Retain the offset pointer as base_offset, the point from which the data offsets are computed. m_context << Instruction::DUP1; for (TypePointer const& parameterType: _typeParameters) { - // stack: v1 v2 ... v(k-1) base_offset current_offset + // stack: v1 v2 ... v(k-1) input_end base_offset current_offset TypePointer type = parameterType->decodingType(); solUnimplementedAssert(type, "No decoding type found."); if (type->category() == Type::Category::Array) @@ -198,13 +212,35 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem { // compute data pointer m_context << Instruction::DUP1 << Instruction::MLOAD; - m_context << Instruction::DUP3 << Instruction::ADD; - m_context << Instruction::SWAP2 << Instruction::SWAP1; + if (_revertOnOutOfBounds) + { + // Check that the data pointer is valid and that length times + // item size is still inside the range. + Whiskers templ(R"({ + if gt(ptr, 0x100000000) { revert(0, 0) } + ptr := add(ptr, base_offset) + let array_data_start := add(ptr, 0x20) + if gt(array_data_start, input_end) { revert(0, 0) } + let array_length := mload(ptr) + if or( + gt(array_length, 0x100000000), + gt(add(array_data_start, mul(array_length, <item_size>)), input_end) + ) { revert(0, 0) } + })"); + templ("item_size", to_string(arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true))); + m_context.appendInlineAssembly(templ.render(), {"input_end", "base_offset", "offset", "ptr"}); + } + else + m_context << Instruction::DUP3 << Instruction::ADD; + // stack: v1 v2 ... v(k-1) input_end base_offset current_offset v(k) + moveIntoStack(3); m_context << u256(0x20) << Instruction::ADD; } else { - m_context << Instruction::SWAP1 << Instruction::DUP2; + // Size has already been checked for this one. + moveIntoStack(2); + m_context << Instruction::DUP3; m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD; } } @@ -216,24 +252,43 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem { // put on stack: data_pointer length loadFromMemoryDynamic(IntegerType(256), !_fromMemory); - // stack: base_offset data_offset next_pointer - m_context << Instruction::SWAP1 << Instruction::DUP3 << Instruction::ADD; - // stack: base_offset next_pointer data_pointer + m_context << Instruction::SWAP1; + // stack: input_end base_offset next_pointer data_offset + if (_revertOnOutOfBounds) + m_context.appendInlineAssembly("{ if gt(data_offset, 0x100000000) { revert(0, 0) } }", {"data_offset"}); + m_context << Instruction::DUP3 << Instruction::ADD; + // stack: input_end base_offset next_pointer array_head_ptr + if (_revertOnOutOfBounds) + m_context.appendInlineAssembly( + "{ if gt(add(array_head_ptr, 0x20), input_end) { revert(0, 0) } }", + {"input_end", "base_offset", "next_ptr", "array_head_ptr"} + ); // retrieve length loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); - // stack: base_offset next_pointer length data_pointer + // stack: input_end base_offset next_pointer array_length data_pointer m_context << Instruction::SWAP2; - // stack: base_offset data_pointer length next_pointer + // stack: input_end base_offset data_pointer array_length next_pointer + if (_revertOnOutOfBounds) + { + unsigned itemSize = arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true); + m_context.appendInlineAssembly(R"({ + if or( + gt(array_length, 0x100000000), + gt(add(data_ptr, mul(array_length, )" + to_string(itemSize) + R"()), input_end) + ) { revert(0, 0) } + })", {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"}); + } } else { - // leave the pointer on the stack + // size has already been checked + // stack: input_end base_offset data_offset m_context << Instruction::DUP1; m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD; } if (arrayType.location() == DataLocation::Memory) { - // stack: base_offset calldata_ref [length] next_calldata + // stack: input_end base_offset calldata_ref [length] next_calldata // copy to memory // move calldata type up again moveIntoStack(calldataType->sizeOnStack()); @@ -241,21 +296,27 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem // fetch next pointer again moveToStackTop(arrayType.sizeOnStack()); } - // move base_offset up - moveToStackTop(1 + arrayType.sizeOnStack()); + // move input_end up + // stack: input_end base_offset calldata_ref [length] next_calldata + moveToStackTop(2 + arrayType.sizeOnStack()); + m_context << Instruction::SWAP1; + // stack: base_offset calldata_ref [length] input_end next_calldata + moveToStackTop(2 + arrayType.sizeOnStack()); m_context << Instruction::SWAP1; + // stack: calldata_ref [length] input_end base_offset next_calldata } } else { solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); loadFromMemoryDynamic(*type, !_fromMemory, true); - moveToStackTop(1 + type->sizeOnStack()); - m_context << Instruction::SWAP1; + // stack: v1 v2 ... v(k-1) input_end base_offset v(k) mem_offset + moveToStackTop(1, type->sizeOnStack()); + moveIntoStack(3, type->sizeOnStack()); } - // stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset + // stack: v1 v2 ... v(k-1) v(k) input_end base_offset next_offset } - m_context << Instruction::POP << Instruction::POP; + popStackSlots(3); } void CompilerUtils::encodeToMemory( @@ -420,15 +481,13 @@ void CompilerUtils::abiEncodeV2( void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory) { - // stack: <source_offset> + // stack: <source_offset> <length> [stack top] auto ret = m_context.pushNewTag(); + moveIntoStack(2); + // stack: <return tag> <source_offset> <length> [stack top] + m_context << Instruction::DUP2 << Instruction::ADD; m_context << Instruction::SWAP1; - if (_fromMemory) - // TODO pass correct size for the memory case - m_context << (u256(1) << 63); - else - m_context << Instruction::CALLDATASIZE; - m_context << Instruction::SWAP1; + // stack: <return tag> <end> <start> string decoderName = m_context.abiFunctions().tupleDecoder(_parameterTypes, _fromMemory); m_context.appendJumpTo(m_context.namedTag(decoderName)); m_context.adjustStackOffset(int(sizeOnStack(_parameterTypes)) - 3); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 9fc97b9e..76670d47 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -90,8 +90,12 @@ public: /// Creates code that unpacks the arguments according to their types specified by a vector of TypePointers. /// From memory if @a _fromMemory is true, otherwise from call data. - /// Expects source offset on the stack, which is removed. - void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false); + /// Calls revert if @a _revertOnOutOfBounds is true and the supplied size is shorter + /// than the static data requirements or if dynamic data pointers reach outside of the + /// area. Also has a hard cap of 0x100000000 for any given length/offset field. + /// Stack pre: <source_offset> <length> + /// Stack post: <value0> <value1> ... <valuen> + void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false, bool _revertOnOutOfBounds = false); /// Copies values (of types @a _givenTypes) given on the stack to a location in memory given /// at the stack top, encoding them according to the ABI as the given types @a _targetTypes. @@ -154,7 +158,7 @@ public: /// Decodes data from ABI encoding into internal encoding. If @a _fromMemory is set to true, /// the data is taken from memory instead of from calldata. /// Can allocate memory. - /// Stack pre: <source_offset> + /// Stack pre: <source_offset> <length> /// Stack post: <value0> <value1> ... <valuen> void abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory = false); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 480db98e..791edc65 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -278,6 +278,7 @@ void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor) m_context.appendProgramSize(); m_context << Instruction::DUP4 << Instruction::CODECOPY; m_context << Instruction::DUP2 << Instruction::ADD; + m_context << Instruction::DUP1; CompilerUtils(m_context).storeFreeMemoryPointer(); // stack: <memptr> CompilerUtils(m_context).abiDecode(FunctionType(_constructor).parameterTypes(), true); @@ -367,6 +368,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac { // Parameter for calldataUnpacker m_context << CompilerUtils::dataStartOffset; + m_context << Instruction::DUP1 << Instruction::CALLDATASIZE << Instruction::SUB; CompilerUtils(m_context).abiDecode(functionType->parameterTypes()); } m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration())); |