From 34a5bd402d9f87896b805f7d03f71a80c2a0ae03 Mon Sep 17 00:00:00 2001 From: winsvega Date: Mon, 23 Feb 2015 12:34:29 +0300 Subject: [PATCH 1/9] Quadratic complexity test --- test/stSolidityTestFiller.json | 105 +++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 30 deletions(-) diff --git a/test/stSolidityTestFiller.json b/test/stSolidityTestFiller.json index 973c52a8daa..28b93587274 100644 --- a/test/stSolidityTestFiller.json +++ b/test/stSolidityTestFiller.json @@ -76,14 +76,6 @@ "//" : " if (!testCryptographicFunctions()) ", "//" : " res = hash(int(res) + int(0x00000f0000000000000000000000000000000000000000000000000000000000)); ", "//" : " ", - "//" : " //Tested 27.01.2015 ", - "//" : " //should run out of gas ", - "//" : " //if (!testInfiniteLoop()) ", - "//" : " // res = hash(int(res) + int(0x000f000000000000000000000000000000000000000000000000000000000000)); ", - "//" : " // ", - "//" : " //should run out of gas ", - "//" : " //if (!testRecursiveMethods()) ", - "//" : " // res = hash(int(res) + int(0x0000000000000000000000000000000000000000000000000000000000000000)); ", "//" : " } ", "//" : " ", "//" : " function testCryptographicFunctions() returns (bool res) ", @@ -155,25 +147,6 @@ "//" : " ", "//" : " } ", "//" : " ", - "//" : " function testInfiniteLoop() returns (bool res) ", - "//" : " { ", - "//" : " res = false; ", - "//" : " while(true){} ", - "//" : " return true; ", - "//" : " } ", - "//" : " ", - "//" : " function testRecursiveMethods() returns (bool res) ", - "//" : " { ", - "//" : " res = false; ", - "//" : " testRecursiveMethods2(); ", - "//" : " return true; ", - "//" : " } ", - "//" : " function testRecursiveMethods2() ", - "//" : " { ", - "//" : " testRecursiveMethods(); ", - "//" : "} ", - "//" : " ", - "//" : " ", "//" : " function testContractSuicide() returns (bool res) ", "//" : " { ", "//" : " TestContract a = new TestContract(); ", @@ -219,7 +192,6 @@ "//" : " } ", "//" : " } ", "//" : " ", - "//" : " ", "//" : " if (i == 0) ", "//" : " return true; ", "//" : " ", @@ -349,7 +321,7 @@ { "//" : "testRecursiveMethods()", "data" : "0x981a3165", - "gasLimit" : "7000", + "gasLimit" : "2000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -476,7 +448,7 @@ } }, - "AmbigiousMethod" : { + "AmbiguousMethod" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -489,6 +461,23 @@ { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "100000", + "//" : "contract contract1 ", + "//" : "{ ", + "//" : " uint value; ", + "//" : " function run() ", + "//" : " { ", + "//" : " value = 225; ", + "//" : " } ", + "//" : "} ", + "//" : " ", + "//" : "contract contract2 ", + "//" : "{ ", + "//" : " uint value2; ", + "//" : " function run() ", + "//" : " { ", + "//" : " value2 = 335; ", + "//" : " } ", + "//" : "} ", "code" : "0x60003560e060020a90048063c040622614601557005b601b6021565b60006000f35b61014f60008190555056", "nonce" : "0", "storage" : { @@ -506,5 +495,61 @@ "to" : "a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "value" : "1" } + }, + + "QuadraticComplexity" : { + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "45678256", + "currentGasLimit" : "100000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10000000", + "//" : "contract caller ", + "//" : "{ ", + "//" : " int value; ", + "//" : " function run(int count) ", + "//" : " { ", + "//" : " value = count; ", + "//" : " address a = 0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b; ", + "//" : " while(count > 0) ", + "//" : " { ", + "//" : " a.call('just', 'call'); ", + "//" : " count = count - 1; ", + "//" : " } ", + "//" : " } ", + "//" : "} ", + "code" : "0x60003560e060020a9004806361a4770614601557005b601e6004356024565b60006000f35b60008160008190555073b94f5374fce5edbc8e2a8697c15331677e6ebf0b90505b600082131560bf5780600160a060020a03166000600060007f6a7573740000000000000000000000000000000000000000000000000000000081526004017f63616c6c000000000000000000000000000000000000000000000000000000008152602001600060008560155a03f150506001820391506045565b505056", + "nonce" : "0", + "storage" : { + } + }, + + "b94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000", + "code" : "{ (CALLDATACOPY 0 0 32) (SSTORE 0 (MLOAD 0)) }", + "code" : "{ (CALLDATACOPY 0 0 32) }", + "nonce" : "0", + "storage" : { + } + } + }, + "transaction" : + { + "//" : "run(int256)", + "data" : "0x61a47706000000000000000000000000000000000000000000000000000000000000c350", + "gasLimit" : "904+68*x+e", + "gasLimit" : "3500000", + "gasPrice" : "1", + "nonce" : "0", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "a94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "1" + } } } From d1d9b2856ab79ba094e65c3a72741211b6d855c9 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 22 Feb 2015 19:15:41 +0100 Subject: [PATCH 2/9] Implementation of index access. --- libsolidity/CompilerContext.cpp | 7 ++- libsolidity/ExpressionCompiler.cpp | 74 ++++++++++++++++++++---------- libsolidity/Types.cpp | 19 +++++++- libsolidity/Types.h | 1 + test/SolidityEndToEndTest.cpp | 27 +++++++++++ 5 files changed, 101 insertions(+), 27 deletions(-) diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index 01a71d7c985..8d32a1a5365 100644 --- a/libsolidity/CompilerContext.cpp +++ b/libsolidity/CompilerContext.cpp @@ -40,7 +40,12 @@ void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaratio void CompilerContext::addStateVariable(VariableDeclaration const& _declaration) { m_stateVariables[&_declaration] = m_stateVariablesSize; - m_stateVariablesSize += _declaration.getType()->getStorageSize(); + bigint newSize = bigint(m_stateVariablesSize) + _declaration.getType()->getStorageSize(); + if (newSize >= bigint(1) << 256) + BOOST_THROW_EXCEPTION(TypeError() + << errinfo_comment("State variable does not fit in storage.") + << errinfo_sourceLocation(_declaration.getLocation())); + m_stateVariablesSize = u256(newSize); } void CompilerContext::startFunction(Declaration const& _function) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 94f65b93f63..dcd411c3eb2 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -534,19 +534,24 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) { solAssert(member == "length", "Illegal array member."); auto const& type = dynamic_cast(*_memberAccess.getExpression().getType()); - solAssert(type.isByteArray(), "Non byte arrays not yet implemented here."); - switch (type.getLocation()) + if (!type.isDynamicallySized()) { - case ArrayType::Location::CallData: - m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; - break; - case ArrayType::Location::Storage: - m_context << eth::Instruction::SLOAD; - break; - default: - solAssert(false, "Unsupported array location."); - break; + CompilerUtils(m_context).popStackElement(type); + m_context << type.getLength(); } + else + switch (type.getLocation()) + { + case ArrayType::Location::CallData: + m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; + break; + case ArrayType::Location::Storage: + m_context << eth::Instruction::SLOAD; + break; + default: + solAssert(false, "Unsupported array location."); + break; + } break; } default: @@ -559,19 +564,40 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) _indexAccess.getBaseExpression().accept(*this); Type const& baseType = *_indexAccess.getBaseExpression().getType(); - solAssert(baseType.getCategory() == Type::Category::Mapping, ""); - Type const& keyType = *dynamic_cast(baseType).getKeyType(); - m_context << u256(0); - solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); - appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression()); - solAssert(baseType.getSizeOnStack() == 1, - "Unexpected: Not exactly one stack slot taken by subscriptable expression."); - m_context << eth::Instruction::SWAP1; - appendTypeMoveToMemory(IntegerType(256)); - m_context << u256(0) << eth::Instruction::SHA3; - - m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _indexAccess.getType()); - m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); + if (baseType.getCategory() == Type::Category::Mapping) + { + Type const& keyType = *dynamic_cast(baseType).getKeyType(); + m_context << u256(0); + solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); + appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression()); + solAssert(baseType.getSizeOnStack() == 1, + "Unexpected: Not exactly one stack slot taken by subscriptable expression."); + m_context << eth::Instruction::SWAP1; + appendTypeMoveToMemory(IntegerType(256)); + m_context << u256(0) << eth::Instruction::SHA3; + m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _indexAccess.getType()); + m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); + } + else if (baseType.getCategory() == Type::Category::Array) + { + ArrayType const& arrayType = dynamic_cast(baseType); + solAssert(arrayType.getLocation() == ArrayType::Location::Storage, + "TODO: Index acces only implemented for storage arrays."); + solAssert(!arrayType.isDynamicallySized(), + "TODO: Index acces only implemented for fixed-size arrays."); + solAssert(!arrayType.isByteArray(), + "TODO: Index acces not implemented for byte arrays."); + solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); + // TODO: for dynamically-sized arrays, update the length for each write + // TODO: do we want to check the index? + _indexAccess.getIndexExpression()->accept(*this); + m_context << arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL + << eth::Instruction::ADD; + m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _indexAccess.getType()); + m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); + } + else + solAssert(false, "Index access only allowed for mappings or arrays."); return false; } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index c0be0d93efb..adcd2e54250 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -555,6 +555,19 @@ bool ArrayType::operator==(Type const& _other) const return other.m_location == m_location; } +u256 ArrayType::getStorageSize() const +{ + if (isDynamicallySized()) + return 1; + else + { + bigint size = bigint(getLength()) * getBaseType()->getStorageSize(); + if (size >= bigint(1) << 256) + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Array too large for storage.")); + return max(1, u256(size)); + } +} + unsigned ArrayType::getSizeOnStack() const { if (m_location == Location::CallData) @@ -665,10 +678,12 @@ bool StructType::operator==(Type const& _other) const u256 StructType::getStorageSize() const { - u256 size = 0; + bigint size = 0; for (pair const& member: getMembers()) size += member.second->getStorageSize(); - return max(1, size); + if (size >= bigint(1) << 256) + BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Struct too large for storage.")); + return max(1, u256(size)); } bool StructType::canLiveOutsideStorage() const diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 0d24b7221a5..9961f03a364 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -303,6 +303,7 @@ class ArrayType: public Type virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual bool operator==(const Type& _other) const override; virtual bool isDynamicallySized() const { return m_hasDynamicLength; } + virtual u256 getStorageSize() const override; virtual unsigned getSizeOnStack() const override; virtual std::string toString() const override; virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; } diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 20bc81599df..0c4c79aca62 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -2667,6 +2667,33 @@ BOOST_AUTO_TEST_CASE(bytes_in_arguments) == encodeArgs(12, (8 + 9) * 3, 13, u256(innercalldata1.length()))); } +BOOST_AUTO_TEST_CASE(fixed_arrays_in_storage) +{ + char const* sourceCode = R"( + contract c { + struct Data { uint x; uint y; } + Data[2**10] data; + uint[2**10 + 3] ids; + function setIDStatic(uint id) { ids[2] = id; } + function setID(uint index, uint id) { ids[index] = id; } + function setData(uint index, uint x, uint y) { data[index].x = x; data[index].y = y; } + function getID(uint index) returns (uint) { return ids[index]; } + function getData(uint index) returns (uint x, uint y) { x = data[index].x; y = data[index].y; } + function getLengths() returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("setIDStatic(uint256)", 11) == bytes()); + BOOST_CHECK(callContractFunction("getID(uint256)", 2) == encodeArgs(11)); + BOOST_CHECK(callContractFunction("setID(uint256,uint256)", 7, 8) == bytes()); + BOOST_CHECK(callContractFunction("getID(uint256)", 7) == encodeArgs(8)); + BOOST_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 7, 8, 9) == bytes()); + BOOST_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 8, 10, 11) == bytes()); + BOOST_CHECK(callContractFunction("getData(uint256)", 7) == encodeArgs(8, 9)); + BOOST_CHECK(callContractFunction("getData(uint256)", 8) == encodeArgs(10, 11)); + BOOST_CHECK(callContractFunction("getLengths()") == encodeArgs(u256(1) << 10, (u256(1) << 10) + 3)); +} + BOOST_AUTO_TEST_SUITE_END() } From de537d5de3a3d48d31df20d11cfbfc975862701d Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 23 Feb 2015 01:31:05 +0100 Subject: [PATCH 3/9] Index and length access for dynamic arrays. --- libsolidity/AST.cpp | 12 +++++++++++- libsolidity/ExpressionCompiler.cpp | 18 ++++++++---------- test/SolidityEndToEndTest.cpp | 29 +++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 179461152fc..c37e8c374a4 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -603,7 +603,17 @@ void MemberAccess::checkTypeRequirements() if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " "visible in " + type.toString())); - m_isLValue = (type.getCategory() == Type::Category::Struct); + // This should probably move somewhere else. + if (type.getCategory() == Type::Category::Struct) + m_isLValue = true; + else if (type.getCategory() == Type::Category::Array) + { + auto const& arrayType(dynamic_cast(type)); + m_isLValue = (*m_memberName == "length" && + arrayType.getLocation() != ArrayType::Location::CallData && arrayType.isDynamicallySized()); + } + else + m_isLValue = false; } void IndexAccess::checkTypeRequirements() diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index dcd411c3eb2..a54915bc7f7 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -546,7 +546,8 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; break; case ArrayType::Location::Storage: - m_context << eth::Instruction::SLOAD; + m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _memberAccess.getType()); + m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); break; default: solAssert(false, "Unsupported array location."); @@ -583,13 +584,10 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) ArrayType const& arrayType = dynamic_cast(baseType); solAssert(arrayType.getLocation() == ArrayType::Location::Storage, "TODO: Index acces only implemented for storage arrays."); - solAssert(!arrayType.isDynamicallySized(), - "TODO: Index acces only implemented for fixed-size arrays."); - solAssert(!arrayType.isByteArray(), - "TODO: Index acces not implemented for byte arrays."); + solAssert(!arrayType.isByteArray(), "TODO: Index acces not implemented for byte arrays."); solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); - // TODO: for dynamically-sized arrays, update the length for each write - // TODO: do we want to check the index? + if (arrayType.isDynamicallySized()) + CompilerUtils(m_context).computeHashStatic(); _indexAccess.getIndexExpression()->accept(*this); m_context << arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL << eth::Instruction::ADD; @@ -1075,7 +1073,7 @@ void ExpressionCompiler::LValue::retrieveValue(Location const& _location, bool _ { case LValueType::Stack: { - unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); + unsigned stackPos = m_context->baseToCurrentStackOffset(m_baseStackOffset); if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep.")); @@ -1124,7 +1122,7 @@ void ExpressionCompiler::LValue::storeValue(Type const& _sourceType, Location co { case LValueType::Stack: { - unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1; + unsigned stackDiff = m_context->baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1; if (stackDiff > 16) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep.")); @@ -1227,7 +1225,7 @@ void ExpressionCompiler::LValue::setToZero(Location const& _location) const { case LValueType::Stack: { - unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); + unsigned stackDiff = m_context->baseToCurrentStackOffset(m_baseStackOffset); if (stackDiff > 16) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep.")); diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 0c4c79aca62..6f85aec9e5b 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -2694,6 +2694,35 @@ BOOST_AUTO_TEST_CASE(fixed_arrays_in_storage) BOOST_CHECK(callContractFunction("getLengths()") == encodeArgs(u256(1) << 10, (u256(1) << 10) + 3)); } +BOOST_AUTO_TEST_CASE(dynamic_arrays_in_storage) +{ + char const* sourceCode = R"( + contract c { + struct Data { uint x; uint y; } + Data[] data; + uint[] ids; + function setIDStatic(uint id) { ids[2] = id; } + function setID(uint index, uint id) { ids[index] = id; } + function setData(uint index, uint x, uint y) { data[index].x = x; data[index].y = y; } + function getID(uint index) returns (uint) { return ids[index]; } + function getData(uint index) returns (uint x, uint y) { x = data[index].x; y = data[index].y; } + function getLengths() returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } + function setLengths(uint l1, uint l2) { data.length = l1; ids.length = l2; } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("setIDStatic(uint256)", 11) == bytes()); + BOOST_CHECK(callContractFunction("getID(uint256)", 2) == encodeArgs(11)); + BOOST_CHECK(callContractFunction("setID(uint256,uint256)", 7, 8) == bytes()); + BOOST_CHECK(callContractFunction("getID(uint256)", 7) == encodeArgs(8)); + BOOST_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 7, 8, 9) == bytes()); + BOOST_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 8, 10, 11) == bytes()); + BOOST_CHECK(callContractFunction("getData(uint256)", 7) == encodeArgs(8, 9)); + BOOST_CHECK(callContractFunction("getData(uint256)", 8) == encodeArgs(10, 11)); + BOOST_CHECK(callContractFunction("setLengths(uint256,uint256)", 48, 49) == bytes()); + BOOST_CHECK(callContractFunction("getLengths()") == encodeArgs(48, 49)); +} + BOOST_AUTO_TEST_SUITE_END() } From 32f0c4f8e643706fa56b4ffb231e7ac16a26dc3b Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 23 Feb 2015 18:21:17 +0100 Subject: [PATCH 4/9] Out-of-bounds checking. --- libsolidity/ExpressionCompiler.cpp | 24 +++++++++++++-- test/SolidityEndToEndTest.cpp | 49 ++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index a54915bc7f7..183864ec7a7 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -586,11 +586,29 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) "TODO: Index acces only implemented for storage arrays."); solAssert(!arrayType.isByteArray(), "TODO: Index acces not implemented for byte arrays."); solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); + + _indexAccess.getIndexExpression()->accept(*this); + // retrieve length if (arrayType.isDynamicallySized()) + m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD; + else + m_context << arrayType.getLength(); + // stack: + // check out-of-bounds access + m_context << eth::Instruction::DUP2 << eth::Instruction::LT; + eth::AssemblyItem legalAccess = m_context.appendConditionalJump(); + // out-of-bounds access throws exception (just STOP for now) + m_context << eth::Instruction::STOP; + + m_context << legalAccess; + // stack: + m_context << arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL; + if (arrayType.isDynamicallySized()) + { + m_context << eth::Instruction::SWAP1; CompilerUtils(m_context).computeHashStatic(); - _indexAccess.getIndexExpression()->accept(*this); - m_context << arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL - << eth::Instruction::ADD; + } + m_context << eth::Instruction::ADD; m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _indexAccess.getType()); m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); } diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 6f85aec9e5b..f4369211008 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -2711,6 +2711,9 @@ BOOST_AUTO_TEST_CASE(dynamic_arrays_in_storage) } )"; compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("getLengths()") == encodeArgs(0, 0)); + BOOST_CHECK(callContractFunction("setLengths(uint256,uint256)", 48, 49) == bytes()); + BOOST_CHECK(callContractFunction("getLengths()") == encodeArgs(48, 49)); BOOST_CHECK(callContractFunction("setIDStatic(uint256)", 11) == bytes()); BOOST_CHECK(callContractFunction("getID(uint256)", 2) == encodeArgs(11)); BOOST_CHECK(callContractFunction("setID(uint256,uint256)", 7, 8) == bytes()); @@ -2719,8 +2722,50 @@ BOOST_AUTO_TEST_CASE(dynamic_arrays_in_storage) BOOST_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 8, 10, 11) == bytes()); BOOST_CHECK(callContractFunction("getData(uint256)", 7) == encodeArgs(8, 9)); BOOST_CHECK(callContractFunction("getData(uint256)", 8) == encodeArgs(10, 11)); - BOOST_CHECK(callContractFunction("setLengths(uint256,uint256)", 48, 49) == bytes()); - BOOST_CHECK(callContractFunction("getLengths()") == encodeArgs(48, 49)); +} + +BOOST_AUTO_TEST_CASE(fixed_out_of_bounds_array_access) +{ + char const* sourceCode = R"( + contract c { + uint[4] data; + function set(uint index, uint value) returns (bool) { data[index] = value; return true; } + function get(uint index) returns (uint) { return data[index]; } + function length() returns (uint) { return data.length; } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("length()") == encodeArgs(4)); + BOOST_CHECK(callContractFunction("set(uint256,uint256)", 3, 4) == encodeArgs(true)); + BOOST_CHECK(callContractFunction("set(uint256,uint256)", 4, 5) == bytes()); + BOOST_CHECK(callContractFunction("set(uint256,uint256)", 400, 5) == bytes()); + BOOST_CHECK(callContractFunction("get(uint256)", 3) == encodeArgs(4)); + BOOST_CHECK(callContractFunction("get(uint256)", 4) == bytes()); + BOOST_CHECK(callContractFunction("get(uint256)", 400) == bytes()); + BOOST_CHECK(callContractFunction("length()") == encodeArgs(4)); +} + +BOOST_AUTO_TEST_CASE(dynamic_out_of_bounds_array_access) +{ + char const* sourceCode = R"( + contract c { + uint[] data; + function enlarge(uint amount) returns (uint) { return data.length += amount; } + function set(uint index, uint value) returns (bool) { data[index] = value; return true; } + function get(uint index) returns (uint) { return data[index]; } + function length() returns (uint) { return data.length; } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("length()") == encodeArgs(0)); + BOOST_CHECK(callContractFunction("get(uint256)", 3) == bytes()); + BOOST_CHECK(callContractFunction("enlarge(uint256)", 4) == encodeArgs(4)); + BOOST_CHECK(callContractFunction("length()") == encodeArgs(4)); + BOOST_CHECK(callContractFunction("set(uint256,uint256)", 3, 4) == encodeArgs(true)); + BOOST_CHECK(callContractFunction("get(uint256)", 3) == encodeArgs(4)); + BOOST_CHECK(callContractFunction("length()") == encodeArgs(4)); + BOOST_CHECK(callContractFunction("set(uint256,uint256)", 4, 8) == bytes()); + BOOST_CHECK(callContractFunction("length()") == encodeArgs(4)); } BOOST_AUTO_TEST_SUITE_END() From 89f07a521f583d877e46a65542e85d402fbb6757 Mon Sep 17 00:00:00 2001 From: winsvega Date: Mon, 23 Feb 2015 22:11:48 +0300 Subject: [PATCH 5/9] Solidity Tests fixing cosmetics --- test/stSolidityTestFiller.json | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/stSolidityTestFiller.json b/test/stSolidityTestFiller.json index 28b93587274..25262e59432 100644 --- a/test/stSolidityTestFiller.json +++ b/test/stSolidityTestFiller.json @@ -524,16 +524,15 @@ "//" : " } ", "//" : " } ", "//" : "} ", - "code" : "0x60003560e060020a9004806361a4770614601557005b601e6004356024565b60006000f35b60008160008190555073b94f5374fce5edbc8e2a8697c15331677e6ebf0b90505b600082131560bf5780600160a060020a03166000600060007f6a7573740000000000000000000000000000000000000000000000000000000081526004017f63616c6c000000000000000000000000000000000000000000000000000000008152602001600060008560155a03f150506001820391506045565b505056", - "nonce" : "0", - "storage" : { + "code" : "0x60003560e060020a9004806361a4770614601557005b601e6004356024565b60006000f35b60008160008190555073b94f5374fce5edbc8e2a8697c15331677e6ebf0b90505b600082131560bf5780600160a060020a03166000600060007f6a7573740000000000000000000000000000000000000000000000000000000081526004017f63616c6c000000000000000000000000000000000000000000000000000000008152602001600060008560155a03f150506001820391506045565b505056", + "nonce" : "0", + "storage" : { } }, "b94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "100000", - "code" : "{ (CALLDATACOPY 0 0 32) (SSTORE 0 (MLOAD 0)) }", - "code" : "{ (CALLDATACOPY 0 0 32) }", + "code" : "{ (CALLDATACOPY 0 0 32) }", "nonce" : "0", "storage" : { } From e08e87bf1bcc1831d09744c424fd9e54c914f580 Mon Sep 17 00:00:00 2001 From: winsvega Date: Mon, 23 Feb 2015 22:14:34 +0300 Subject: [PATCH 6/9] Solidity Tests cosmetics2 --- test/stSolidityTestFiller.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/stSolidityTestFiller.json b/test/stSolidityTestFiller.json index 25262e59432..01e9508f7af 100644 --- a/test/stSolidityTestFiller.json +++ b/test/stSolidityTestFiller.json @@ -530,7 +530,7 @@ } }, - "b94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "b94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "100000", "code" : "{ (CALLDATACOPY 0 0 32) }", "nonce" : "0", @@ -540,9 +540,9 @@ }, "transaction" : { - "//" : "run(int256)", + "//" : "run(int256)", "data" : "0x61a47706000000000000000000000000000000000000000000000000000000000000c350", - "gasLimit" : "904+68*x+e", + "gasLimit" : "904+68*x+e", "gasLimit" : "3500000", "gasPrice" : "1", "nonce" : "0", From 73d7430c73da7f703ed5b05e34b3e1dde4cbc1dd Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 23 Feb 2015 22:10:35 +0100 Subject: [PATCH 7/9] fixed contract creation transaction --- libweb3jsonrpc/WebThreeStubServerBase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 6ab7018996a..c304e3c030d 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -701,10 +701,10 @@ std::string WebThreeStubServerBase::eth_transact(Json::Value const& _json) { std::string ret; TransactionSkeleton t = toTransaction(_json); - if (t.creation) - ret = right160(sha3(rlpList(t.from, client()->countAt(t.from))));; if (!t.from) t.from = m_accounts->getDefaultTransactAccount(); + if (t.creation) + ret = right160(sha3(rlpList(t.from, client()->countAt(t.from))));; if (!t.gasPrice) t.gasPrice = 10 * dev::eth::szabo; if (!t.gas) From 3767677b277e0eced9d782f01456926e228b7030 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 23 Feb 2015 23:45:04 +0100 Subject: [PATCH 8/9] explicit operator bool for hash type --- alethzero/MainWin.cpp | 2 +- libdevcore/FixedHash.h | 2 +- libdevcrypto/CryptoPP.cpp | 2 +- libethcore/ProofOfWork.h | 2 +- libethereum/EthereumHost.h | 2 +- libethereum/Transaction.h | 2 +- libweb3jsonrpc/WebThreeStubServerBase.cpp | 2 +- libwhisper/Common.cpp | 2 +- mix/ClientModel.cpp | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 39bc0ad43d2..9ee3f36e39c 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1629,7 +1629,7 @@ void Main::on_net_triggered() { web3()->setIdealPeerCount(ui->idealPeers->value()); web3()->setNetworkPreferences(netPrefs()); - ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : 0); + ethereum()->setNetworkId(m_privateChain.size() ? sha3(m_privateChain.toStdString()) : h256()); // TODO: p2p // if (m_networkConfig.size()/* && ui->usePast->isChecked()*/) // web3()->restoreNetwork(bytesConstRef((byte*)m_networkConfig.data(), m_networkConfig.size())); diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 561f2f40577..2b4e6bc0869 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -80,7 +80,7 @@ class FixedHash operator Arith() const { return fromBigEndian(m_data); } /// @returns true iff this is the empty hash. - operator bool() const { return ((Arith)*this) != 0; } + explicit operator bool() const { return ((Arith)*this) != 0; } // The obvious comparison operators. bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; } diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 2d0170fb92e..43993e0c56a 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -134,7 +134,7 @@ bool Secp256k1::verify(Signature const& _signature, bytesConstRef _message) bool Secp256k1::verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed) { // todo: verify w/o recovery (if faster) - return _p == _hashed ? recover(_sig, _message) : recover(_sig, sha3(_message).ref()); + return (bool)_p == _hashed ? (bool)recover(_sig, _message) : (bool)recover(_sig, sha3(_message).ref()); } Public Secp256k1::recover(Signature _signature, bytesConstRef _message) diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index 64ce502afbe..c3c3f192bad 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -82,7 +82,7 @@ template std::pair ProofOfWorkEngine::mine(h256 const& _root, u256 const& _difficulty, unsigned _msTimeout, bool _continue, bool _turbo) { std::pair ret; - static std::mt19937_64 s_eng((time(0) + (unsigned)m_last)); + static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()))); u256 s = (m_last = h256::random(s_eng)); bigint d = (bigint(1) << 256) / _difficulty; diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index c732f2dc1f8..dfa928675e6 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -94,7 +94,7 @@ class EthereumHost: public p2p::HostCapability, Worker h256Set neededBlocks(h256Set const& _exclude); /// Check to see if the network peer-state initialisation has happened. - bool isInitialised() const { return m_latestBlockSent; } + bool isInitialised() const { return (bool)m_latestBlockSent; } /// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first. bool ensureInitialised(); diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 0f88a4bc0fa..a9044970f16 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -81,7 +81,7 @@ class Transaction Address safeSender() const noexcept; /// @returns true if transaction is non-null. - operator bool() const { return m_type != NullTransaction; } + explicit operator bool() const { return m_type != NullTransaction; } /// @returns true if transaction is contract-creation. bool isCreation() const { return m_type == ContractCreation; } diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index c304e3c030d..dbf3b2ec9c7 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -704,7 +704,7 @@ std::string WebThreeStubServerBase::eth_transact(Json::Value const& _json) if (!t.from) t.from = m_accounts->getDefaultTransactAccount(); if (t.creation) - ret = right160(sha3(rlpList(t.from, client()->countAt(t.from))));; + ret = toJS(right160(sha3(rlpList(t.from, client()->countAt(t.from)))));; if (!t.gasPrice) t.gasPrice = 10 * dev::eth::szabo; if (!t.gas) diff --git a/libwhisper/Common.cpp b/libwhisper/Common.cpp index f17ad638b7f..c29ac6bf6c7 100644 --- a/libwhisper/Common.cpp +++ b/libwhisper/Common.cpp @@ -71,7 +71,7 @@ bool TopicFilter::matches(Envelope const& _e) const for (unsigned i = 0; i < t.size(); ++i) { for (auto et: _e.topic()) - if (((t[i].first ^ et) & t[i].second) == 0) + if (((t[i].first ^ et) & t[i].second) == CollapsedTopicPart()) goto NEXT_TOPICPART; // failed to match topicmask against any topics: move on to next mask goto NEXT_TOPICMASK; diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 45198c11466..110bdc1416b 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -370,7 +370,7 @@ void ClientModel::onNewTransaction() QString function; QString returned; - bool creation = tr.contractAddress != 0; + bool creation = (bool)tr.contractAddress; //TODO: handle value transfer FixedHash<4> functionHash; @@ -403,7 +403,7 @@ void ClientModel::onNewTransaction() if (creation) returned = QString::fromStdString(toJS(tr.contractAddress)); - Address contractAddress = tr.address != 0 ? tr.address : tr.contractAddress; + Address contractAddress = (bool)tr.address ? tr.address : tr.contractAddress; auto contractAddressIter = m_contractNames.find(contractAddress); if (contractAddressIter != m_contractNames.end()) { From 1b284c5b8b705be4a19acacb84809589673174ff Mon Sep 17 00:00:00 2001 From: arkpar Date: Tue, 24 Feb 2015 00:11:29 +0100 Subject: [PATCH 9/9] fixed test buid --- test/SolidityEndToEndTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 20bc81599df..b63e9ad466c 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -2137,7 +2137,7 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data) callContractFunctionWithValue("deposit(hash256)", value, id); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data == encodeArgs(m_sender, id, value, true)); + BOOST_CHECK(m_logs[0].data == encodeArgs((u160)m_sender, id, value, true)); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(address,hash256,uint256,bool)"))); }