Merge pull request #10608 from ethereum/develop

Merge develop into breaking.
This commit is contained in:
chriseth 2020-12-15 15:24:53 +01:00 committed by GitHub
commit 3c270ca7f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 291 additions and 45 deletions

View File

@ -22,7 +22,7 @@
# (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------
FROM gcr.io/oss-fuzz-base/base-clang as base
LABEL version="6"
LABEL version="7"
ARG DEBIAN_FRONTEND=noninteractive
@ -32,7 +32,7 @@ RUN apt-get update; \
software-properties-common \
ninja-build git wget \
libbz2-dev zlib1g-dev git curl uuid-dev \
pkg-config openjdk-8-jdk liblzma-dev unzip mlton; \
pkg-config openjdk-8-jdk liblzma-dev unzip mlton m4; \
apt-get install -qy python-pip python-sphinx;
# Install cmake 3.14 (minimum requirement is cmake 3.10)
@ -113,6 +113,25 @@ RUN set -ex; \
ninja install/strip; \
rm -rf /usr/src/hera
# gmp
RUN set -ex; \
# Replace system installed libgmp static library
# with sanitized version. Do not perform apt
# remove because it removes mlton as well that
# we need for building libabicoder
rm -f /usr/lib/x86_64-linux-gnu/libgmp.*; \
rm -f /usr/include/x86_64-linux-gnu/gmp.h; \
cd /usr/src; \
wget -q 'https://gmplib.org/download/gmp/gmp-6.2.1.tar.xz' -O gmp.tar.xz; \
test "$(sha256sum gmp.tar.xz)" = "fd4829912cddd12f84181c3451cc752be224643e87fac497b69edddadc49b4f2 gmp.tar.xz"; \
tar -xf gmp.tar.xz; \
cd gmp-6.2.1; \
./configure --prefix=/usr --enable-static=yes; \
make -j; \
make install; \
rm -rf /usr/src/gmp-6.2.1; \
rm -f /usr/src/gmp.tar.xz
# libabicoder
RUN set -ex; \
cd /usr/src; \
@ -127,5 +146,4 @@ RUN set -ex; \
FROM base
COPY --from=libraries /usr/lib /usr/lib
COPY --from=libraries /usr/bin /usr/bin
COPY --from=libraries /usr/include /usr/include
COPY --from=libraries /usr/include /usr/include

View File

@ -51,9 +51,11 @@ AnalysisFramework::parseAnalyseAndReturnError(
)
{
compiler().reset();
// Do not insert license if it is already present.
bool insertLicense = _insertLicenseAndVersionPragma && _source.find("// SPDX-License-Identifier:") == string::npos;
compiler().setSources({{"",
_insertLicenseAndVersionPragma ?
"pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n" + _source :
string{_insertLicenseAndVersionPragma ? "pragma solidity >=0.0;\n" : ""} +
string{insertLicense ? "// SPDX-License-Identifier: GPL-3.0\n" : ""} +
_source
}});
compiler().setEVMVersion(solidity::test::CommonOptions::get().evmVersion());

View File

@ -134,7 +134,9 @@ bytes SolidityExecutionFramework::compileContract(
string SolidityExecutionFramework::addPreamble(string const& _sourceCode)
{
// Silence compiler version warning
string preamble = "pragma solidity >=0.0;\n// SPDX-License-Identifier: unlicensed\n";
string preamble = "pragma solidity >=0.0;\n";
if (_sourceCode.find("// SPDX-License-Identifier:") == string::npos)
preamble += "// SPDX-License-Identifier: unlicensed\n";
if (
solidity::test::CommonOptions::get().useABIEncoderV1 &&
_sourceCode.find("pragma experimental ABIEncoderV2;") == string::npos &&

View File

@ -0,0 +1,196 @@
//
//
//
//
//
//
//
//
// SPDX-License-Identifier: CC0-1.0
// This interface is designed to be compatible with the Vyper version.
/// @notice This is the Ethereum 2.0 deposit contract interface.
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
interface IDepositContract {
/// @notice A processed deposit event.
event DepositEvent(
bytes pubkey,
bytes withdrawal_credentials,
bytes amount,
bytes signature,
bytes index
);
/// @notice Submit a Phase 0 DepositData object.
/// @param pubkey A BLS12-381 public key.
/// @param withdrawal_credentials Commitment to a public key for withdrawals.
/// @param signature A BLS12-381 signature.
/// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
/// Used as a protection against malformed input.
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) external payable;
/// @notice Query the current deposit root hash.
/// @return The deposit root hash.
function get_deposit_root() external view returns (bytes32);
/// @notice Query the current deposit count.
/// @return The deposit count encoded as a little endian 64-bit number.
function get_deposit_count() external view returns (bytes memory);
}
// Based on official specification in https://eips.ethereum.org/EIPS/eip-165
interface ERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceId The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceId` and
/// `interfaceId` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceId) external pure returns (bool);
}
// This is a rewrite of the Vyper Eth2.0 deposit contract in Solidity.
// It tries to stay as close as possible to the original source code.
/// @notice This is the Ethereum 2.0 deposit contract interface.
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
contract DepositContract is IDepositContract, ERC165 {
uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32;
// NOTE: this also ensures `deposit_count` will fit into 64-bits
uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1;
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] branch;
uint256 deposit_count;
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes;
constructor() public {
// Compute hashes in empty sparse Merkle tree
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++)
zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height]));
}
function get_deposit_root() override external view returns (bytes32) {
bytes32 node;
uint size = deposit_count;
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
if ((size & 1) == 1)
node = sha256(abi.encodePacked(branch[height], node));
else
node = sha256(abi.encodePacked(node, zero_hashes[height]));
size /= 2;
}
return sha256(abi.encodePacked(
node,
to_little_endian_64(uint64(deposit_count)),
bytes24(0)
));
}
function get_deposit_count() override external view returns (bytes memory) {
return to_little_endian_64(uint64(deposit_count));
}
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) override external payable {
// Extended ABI length checks since dynamic types are used.
require(pubkey.length == 48, "DepositContract: invalid pubkey length");
require(withdrawal_credentials.length == 32, "DepositContract: invalid withdrawal_credentials length");
require(signature.length == 96, "DepositContract: invalid signature length");
// Check deposit amount
require(msg.value >= 1 ether, "DepositContract: deposit value too low");
require(msg.value % 1 gwei == 0, "DepositContract: deposit value not multiple of gwei");
uint deposit_amount = msg.value / 1 gwei;
require(deposit_amount <= type(uint64).max, "DepositContract: deposit value too high");
// Emit `DepositEvent` log
bytes memory amount = to_little_endian_64(uint64(deposit_amount));
emit DepositEvent(
pubkey,
withdrawal_credentials,
amount,
signature,
to_little_endian_64(uint64(deposit_count))
);
// Compute deposit data root (`DepositData` hash tree root)
bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));
bytes32 signature_root = sha256(abi.encodePacked(
sha256(abi.encodePacked(signature[:64])),
sha256(abi.encodePacked(signature[64:], bytes32(0)))
));
bytes32 node = sha256(abi.encodePacked(
sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
sha256(abi.encodePacked(amount, bytes24(0), signature_root))
));
// Verify computed and expected deposit data roots match
require(node == deposit_data_root, "DepositContract: reconstructed DepositData does not match supplied deposit_data_root");
// Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`)
require(deposit_count < MAX_DEPOSIT_COUNT, "DepositContract: merkle tree full");
// Add deposit data root to Merkle tree (update a single `branch` node)
deposit_count += 1;
uint size = deposit_count;
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
if ((size & 1) == 1) {
branch[height] = node;
return;
}
node = sha256(abi.encodePacked(branch[height], node));
size /= 2;
}
// As the loop should always end prematurely with the `return` statement,
// this code should be unreachable. We assert `false` just to be safe.
assert(false);
}
function supportsInterface(bytes4 interfaceId) override external pure returns (bool) {
return interfaceId == type(ERC165).interfaceId || interfaceId == type(IDepositContract).interfaceId;
}
function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
ret = new bytes(8);
bytes8 bytesValue = bytes8(value);
// Byteswapping during copying to bytes.
ret[0] = bytesValue[7];
ret[1] = bytesValue[6];
ret[2] = bytesValue[5];
ret[3] = bytesValue[4];
ret[4] = bytesValue[3];
ret[5] = bytesValue[2];
ret[6] = bytesValue[1];
ret[7] = bytesValue[0];
}
}
// ====
// compileViaYul: also
// ----
// constructor()
// supportsInterface(bytes4): 0x0 -> 0
// supportsInterface(bytes4): 0xffffffff00000000000000000000000000000000000000000000000000000000 -> false # defined to be false by ERC-165 #
// supportsInterface(bytes4): 0x01ffc9a700000000000000000000000000000000000000000000000000000000 -> true # ERC-165 id #
// supportsInterface(bytes4): 0x8564090700000000000000000000000000000000000000000000000000000000 -> true # the deposit interface id #
// get_deposit_root() -> 0xd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e
// get_deposit_count() -> 0x20, 8, 0
// # TODO: check balance and logs after each deposit #
// deposit(bytes,bytes,bytes,bytes32), 32 ether: 0 -> FAILURE # Empty input #
// get_deposit_root() -> 0xd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e
// get_deposit_count() -> 0x20, 8, 0
// deposit(bytes,bytes,bytes,bytes32), 1 ether: 0x80, 0xe0, 0x120, 0xaa4a8d0b7d9077248630f1a4701ae9764e42271d7f22b7838778411857fd349e, 0x30, 0x933ad9491b62059dd065b560d256d8957a8c402cc6e8d8ee7290ae11e8f73292, 0x67a8811c397529dac52ae1342ba58c9500000000000000000000000000000000, 0x20, 0x00f50428677c60f997aadeab24aabf7fceaef491c96a52b463ae91f95611cf71, 0x60, 0xa29d01cc8c6296a8150e515b5995390ef841dc18948aa3e79be6d7c1851b4cbb, 0x5d6ff49fa70b9c782399506a22a85193151b9b691245cebafd2063012443c132, 0x4b6c36debaedefb7b2d71b0503ffdc00150aaffd42e63358238ec888901738b8 -> # txhash: 0x7085c586686d666e8bb6e9477a0f0b09565b2060a11f1c4209d3a52295033832 #
// get_deposit_root() -> 0x2089653123d9c721215120b6db6738ba273bbc5228ac093b1f983badcdc8a438
// get_deposit_count() -> 0x20, 8, 0x0100000000000000000000000000000000000000000000000000000000000000
// deposit(bytes,bytes,bytes,bytes32), 32 ether: 0x80, 0xe0, 0x120, 0xdbd986dc85ceb382708cf90a3500f500f0a393c5ece76963ac3ed72eccd2c301, 0x30, 0xb2ce0f79f90e7b3a113ca5783c65756f96c4b4673c2b5c1eb4efc22280259441, 0x06d601211e8866dc5b50dc48a244dd7c00000000000000000000000000000000, 0x20, 0x00344b6c73f71b11c56aba0d01b7d8ad83559f209d0a4101a515f6ad54c89771, 0x60, 0x945caaf82d18e78c033927d51f452ebcd76524497b91d7a11219cb3db6a1d369, 0x7595fc095ce489e46b2ef129591f2f6d079be4faaf345a02c5eb133c072e7c56, 0x0c6c3617eee66b4b878165c502357d49485326bc6b31bc96873f308c8f19c09d -> # txhash: 0x404d8e109822ce448e68f45216c12cb051b784d068fbe98317ab8e50c58304ac #
// get_deposit_root() -> 0x40255975859377d912c53aa853245ebd939bdd2b33a28e084babdcc1ed8238ee
// get_deposit_count() -> 0x20, 8, 0x0200000000000000000000000000000000000000000000000000000000000000

View File

@ -24,9 +24,14 @@
using namespace solidity::test::abiv2fuzzer;
using namespace solidity::test;
using namespace solidity::util;
using namespace solidity;
using namespace std;
static constexpr size_t abiCoderHeapSize = 1024 * 512;
static evmc::VM evmone = evmc::VM{evmc_create_evmone()};
/// Expected output value is decimal 0
static vector<uint8_t> const expectedOutput(32, 0);
DEFINE_PROTO_FUZZER(Contract const& _contract)
{
@ -43,13 +48,60 @@ DEFINE_PROTO_FUZZER(Contract const& _contract)
string typeString = converter.isabelleTypeString();
string valueString = converter.isabelleValueString();
std::cout << typeString << std::endl;
std::cout << valueString << std::endl;
abicoder::ABICoder coder(abiCoderHeapSize);
if (!typeString.empty())
if (!typeString.empty() && converter.coderFunction())
{
auto [encodeStatus, encodedData] = coder.encode(typeString, valueString);
solAssert(encodeStatus, "Isabelle abicoder fuzzer: Encoding failed");
// Raw runtime byte code generated by solidity
bytes byteCode;
string hexEncodedInput;
try
{
// Compile contract generated by the proto fuzzer
SolidityCompilationFramework solCompilationFramework;
string contractName = ":C";
byteCode = solCompilationFramework.compileContract(contractSource, contractName);
Json::Value methodIdentifiers = solCompilationFramework.getMethodIdentifiers();
// We always call the second function from the list of alphabetically
// sorted interface functions
hexEncodedInput = (++methodIdentifiers.begin())->asString() + encodedData.substr(2, encodedData.size());
}
// Ignore stack too deep errors during compilation
catch (solidity::evmasm::StackTooDeepException const&)
{
return;
}
// We target the default EVM which is the latest
solidity::langutil::EVMVersion version;
EVMHost hostContext(version, evmone);
// Deploy contract and signal failure if deployment failed
evmc::result createResult = AbiV2Utility::deployContract(hostContext, byteCode);
solAssert(
createResult.status_code == EVMC_SUCCESS,
"Proto ABIv2 Fuzzer: Contract creation failed"
);
// Execute test function and signal failure if EVM reverted or
// did not return expected output on successful execution.
evmc::result callResult = AbiV2Utility::executeContract(
hostContext,
fromHex(hexEncodedInput),
createResult.create_address
);
// We don't care about EVM One failures other than EVMC_REVERT
solAssert(callResult.status_code != EVMC_REVERT, "Proto ABIv2 fuzzer: EVM One reverted");
if (callResult.status_code == EVMC_SUCCESS)
solAssert(
AbiV2Utility::isOutputExpected(callResult.output_data, callResult.output_size, expectedOutput),
"Proto ABIv2 fuzzer: ABIv2 coding failure found"
);
}
return;
}

View File

@ -151,7 +151,7 @@ if (OSSFUZZ)
protobuf-mutator.a
protobuf.a
abicoder
gmp
gmp.a
)
set_target_properties(abiv2_isabelle_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
target_compile_options(abiv2_isabelle_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion -Wno-suggest-destructor-override -Wno-inconsistent-missing-destructor-override)

View File

@ -44,14 +44,8 @@ message ValueType {
}
}
// bytes/string
message DynamicByteArrayType {
enum DType {
BYTES = 0;
STRING = 1;
}
required DType type = 1;
}
// bytes
message DynamicByteArrayType {}
message ArrayType {
required Type t = 1;

View File

@ -69,7 +69,7 @@ static V integerValue(unsigned _counter)
V value = V(
u256(solidity::util::keccak256(solidity::util::h256(_counter))) % u256(boost::math::tools::max_value<V>())
);
if (value % 2 == 0)
if (boost::multiprecision::is_signed_number<V>::value && value % 2 == 0)
return value * (-1);
else
return value;
@ -830,10 +830,10 @@ string TypeVisitor::visit(ArrayType const& _type)
return baseType + arrayBracket;
}
string TypeVisitor::visit(DynamicByteArrayType const& _type)
string TypeVisitor::visit(DynamicByteArrayType const&)
{
m_isLastDynParamRightPadded = true;
m_baseType = bytesArrayTypeAsString(_type);
m_baseType = "bytes";
m_structTupleString.addTypeStringToTuple(m_baseType);
return m_baseType;
}
@ -967,7 +967,7 @@ pair<string, string> AssignCheckVisitor::visit(DynamicByteArrayType const& _type
string isabelleValue = ValueGetterVisitor{}.isabelleBytesValueAsString(value);
m_valueStream.appendValue(isabelleValue);
}
DataType dataType = _type.type() == DynamicByteArrayType::BYTES ? DataType::BYTES : DataType::STRING;
DataType dataType = DataType::BYTES;
return assignAndCheckStringPair(m_varName, m_paramName, value, value, dataType);
}
@ -1127,12 +1127,6 @@ string AssignCheckVisitor::checkString(string const& _ref, string const& _value,
string checkPred;
switch (_type)
{
case DataType::STRING:
checkPred = Whiskers(R"(!bytesCompare(bytes(<varName>), <value>))")
("varName", _ref)
("value", _value)
.render();
break;
case DataType::BYTES:
checkPred = Whiskers(R"(!bytesCompare(<varName>, <value>))")
("varName", _ref)
@ -1288,10 +1282,6 @@ std::string ValueGetterVisitor::variableLengthValueAsString(
bool _isHexLiteral
)
{
// TODO: Move this to caller
// solAssert(_numBytes >= 0 && _numBytes <= s_maxDynArrayLength,
// "Proto ABIv2 fuzzer: Invalid hex length"
// );
if (_numBytes == 0)
return Whiskers(R"(<?isHex>hex</isHex>"")")
("isHex", _isHexLiteral)

View File

@ -154,6 +154,10 @@ public:
std::string contractToString(Contract const& _input);
std::string isabelleTypeString() const;
std::string isabelleValueString() const;
bool coderFunction() const
{
return m_test == Contract_Test::Contract_Test_CALLDATA_CODER;
}
private:
enum class Delimiter
{
@ -448,7 +452,6 @@ public:
enum class DataType
{
BYTES,
STRING,
VALUE,
ARRAY
};
@ -537,17 +540,6 @@ public:
else
return v;
}
static std::string bytesArrayTypeAsString(DynamicByteArrayType const& _x)
{
switch (_x.type())
{
case DynamicByteArrayType::BYTES:
return "bytes";
case DynamicByteArrayType::STRING:
return "string";
}
}
protected:
T visitValueType(ValueType const& _type)
{