mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Simple size check for old ABI decoder.
This commit is contained in:
		
							parent
							
								
									2cdf44f65c
								
							
						
					
					
						commit
						32c94f5059
					
				| @ -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); | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -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())); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user