diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index 265533397..e9e95d108 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -9,6 +9,7 @@ add_dependencies(ossfuzz ) if (OSSFUZZ) + add_subdirectory(lpsolver) add_custom_target(ossfuzz_proto) add_dependencies(ossfuzz_proto sol_proto_ossfuzz @@ -23,6 +24,13 @@ if (OSSFUZZ) endif() if (OSSFUZZ) + add_executable(lpsolver_ossfuzz + LPSolverFuzzer.cpp + LPSolverCustomMutatorInterface.cpp + ) + target_link_libraries(lpsolver_ossfuzz PRIVATE solutil lpsolvergen) + set_target_properties(lpsolver_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) + add_executable(solc_ossfuzz solc_ossfuzz.cpp ../fuzzer_common.cpp diff --git a/test/tools/ossfuzz/LPSolverCustomMutatorInterface.cpp b/test/tools/ossfuzz/LPSolverCustomMutatorInterface.cpp new file mode 100644 index 000000000..87c68aad0 --- /dev/null +++ b/test/tools/ossfuzz/LPSolverCustomMutatorInterface.cpp @@ -0,0 +1,71 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include + +#include + +using namespace std; +using namespace solidity::test::fuzzer::lpsolver; + +// Prototype as we can't use the FuzzerInterface.h header. +extern "C" size_t LLVMFuzzerMutate(uint8_t* _data, size_t _size, size_t _maxSize); +extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* _data, size_t size, size_t _maxSize, unsigned int seed); + +namespace +{ +/// Define LP Solver's custom mutator by implementing libFuzzer's +/// custom mutator external interface. +extern "C" size_t LLVMFuzzerCustomMutator( + uint8_t* _data, + size_t _size, + size_t _maxSize, + unsigned int _seed +) +{ + solAssert(_data, "libFuzzerInterface: libFuzzer supplied bad buffer"); + if (_maxSize <= _size || _size == 0) + return LLVMFuzzerMutate(_data, _size, _maxSize); + return LPSolverCustomMutatorInterface{_data, _size, _maxSize, _seed}.generate(); +} +} + +LPSolverCustomMutatorInterface::LPSolverCustomMutatorInterface( + uint8_t* _data, + size_t _size, + size_t _maxSize, + unsigned int _seed +): + data(_data), + size(_size), + maxMutantSize(_maxSize), + generator(make_shared(_seed)) +{} + +size_t LPSolverCustomMutatorInterface::generate() +{ + string testCase = generator->generate(); + solAssert( + !testCase.empty() && data, + "Solc custom mutator: Invalid mutant or memory pointer" + ); + size_t mutantSize = min(testCase.size(), maxMutantSize - 1); + mempcpy(data, testCase.data(), mutantSize); + return mutantSize; +} diff --git a/test/tools/ossfuzz/LPSolverCustomMutatorInterface.h b/test/tools/ossfuzz/LPSolverCustomMutatorInterface.h new file mode 100644 index 000000000..eb9177641 --- /dev/null +++ b/test/tools/ossfuzz/LPSolverCustomMutatorInterface.h @@ -0,0 +1,46 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Implements libFuzzer's custom mutator interface for LP Solver fuzzer. + */ + +#pragma once + +#include + +#include + +namespace solidity::test::fuzzer::lpsolver +{ +struct LPSolverCustomMutatorInterface +{ + LPSolverCustomMutatorInterface(uint8_t* _data, size_t _size, size_t _maxSize, unsigned _seed); + /// Generates LP Solver constraints, copies it into buffer + /// provided by libFuzzer and @returns size of the test program. + size_t generate(); + + /// Raw pointer to libFuzzer provided input + uint8_t* data; + /// Size of libFuzzer provided input + size_t size; + /// Maximum length of mutant specified by libFuzzer + size_t maxMutantSize; + /// Constraint generator handle + std::shared_ptr generator; +}; +} diff --git a/test/tools/ossfuzz/LPSolverFuzzer.cpp b/test/tools/ossfuzz/LPSolverFuzzer.cpp new file mode 100644 index 000000000..9779458cb --- /dev/null +++ b/test/tools/ossfuzz/LPSolverFuzzer.cpp @@ -0,0 +1,32 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include + +using namespace std; + +// Prototype as we can't use the FuzzerInterface.h header. +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size); + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* , size_t ) +{ + // TODO: Invoke LP solver and Z3 on constraints provided by fuzzer interface, + // comparing their outcomes. + return 0; +} diff --git a/test/tools/ossfuzz/lpsolver/CMakeLists.txt b/test/tools/ossfuzz/lpsolver/CMakeLists.txt new file mode 100644 index 000000000..167aa6e45 --- /dev/null +++ b/test/tools/ossfuzz/lpsolver/CMakeLists.txt @@ -0,0 +1,12 @@ +set(sources + ConstraintGenerator.cpp + ConstraintGenerator.h + FuzzerSolverInterface.cpp + FuzzerSolverInterface.h +) +add_library(lpsolvergen) +target_sources(lpsolvergen + PUBLIC + ${sources} +) +target_link_libraries(lpsolvergen PUBLIC solutil) diff --git a/test/tools/ossfuzz/lpsolver/ConstraintGenerator.cpp b/test/tools/ossfuzz/lpsolver/ConstraintGenerator.cpp new file mode 100644 index 000000000..852202378 --- /dev/null +++ b/test/tools/ossfuzz/lpsolver/ConstraintGenerator.cpp @@ -0,0 +1,41 @@ +/* + This file is part of solidity. + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +using namespace std; +using namespace solidity::test::fuzzer::lpsolver; + +ConstraintGenerator::ConstraintGenerator(unsigned int _seed) +{ + prng = make_shared(_seed); +} + +string ConstraintGenerator::generate() +{ + string constraint; + for (int i = 0; i < numConstraints(); i++) + { + string sep; + for (int j = 0; j < numFactors(); j++) + { + constraint += sep + to_string(randomInteger()); + if (sep.empty()) + sep = ", "; + } + constraint += "\n"; + } + return constraint; +} diff --git a/test/tools/ossfuzz/lpsolver/ConstraintGenerator.h b/test/tools/ossfuzz/lpsolver/ConstraintGenerator.h new file mode 100644 index 000000000..1e1719b3b --- /dev/null +++ b/test/tools/ossfuzz/lpsolver/ConstraintGenerator.h @@ -0,0 +1,65 @@ +/* + This file is part of solidity. + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/* + * Generates an integer matrix of dimension n x m + */ + +#pragma once + +#include +#include +#include +#include + +namespace solidity::test::fuzzer::lpsolver +{ + +using RandomEngine = std::mt19937; +using Distribution = std::uniform_int_distribution; + +struct ConstraintGenerator +{ + explicit ConstraintGenerator(unsigned int _seed); + + /// @returns generated constraint. + std::string generate(); + + /// @returns random number of factors. + int numFactors() + { + return Distribution(s_minFactors, s_maxFactors)(*prng); + } + + /// @returns random number of constraints. + int numConstraints() + { + return Distribution(s_minConstraints, s_maxConstraints)(*prng); + } + + /// @returns an integer chosen uniformly at random. + int randomInteger() + { + return Distribution(std::numeric_limits::min(), std::numeric_limits::max())(*prng); + } + + std::shared_ptr prng; + + static constexpr int s_minFactors = 2; + static constexpr int s_maxFactors = 10; + static constexpr int s_minConstraints = 1; + static constexpr int s_maxConstraints = 10; +}; + +} diff --git a/test/tools/ossfuzz/lpsolver/FuzzerSolverInterface.cpp b/test/tools/ossfuzz/lpsolver/FuzzerSolverInterface.cpp new file mode 100644 index 000000000..efd9911eb --- /dev/null +++ b/test/tools/ossfuzz/lpsolver/FuzzerSolverInterface.cpp @@ -0,0 +1,66 @@ +/* + This file is part of solidity. + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +using namespace solidity::test::fuzzer::lpsolver; +using namespace solidity::util; +using namespace std; + +FuzzerSolverInterface::FuzzerSolverInterface() +{ + m_solvingState.variableNames.emplace_back(""); +} + +LinearExpression FuzzerSolverInterface::constant(rational _value) +{ + return LinearExpression::factorForVariable(0, _value); +} + +LinearExpression FuzzerSolverInterface::variable( + rational _factor, + string const& _variable +) +{ + return LinearExpression::factorForVariable(variableIndex(_variable), _factor); +} + +void FuzzerSolverInterface::addLEConstraint(LinearExpression _lhs) +{ + m_solvingState.constraints.push_back({move(_lhs), false}); +} + +void FuzzerSolverInterface::addEQConstraint(LinearExpression _lhs) +{ + m_solvingState.constraints.push_back({move(_lhs), true}); +} + +solution FuzzerSolverInterface::check() +{ + return m_solver.check(m_solvingState); +} + +size_t FuzzerSolverInterface::variableIndex(string const& _name) +{ + if (m_solvingState.variableNames.empty()) + m_solvingState.variableNames.emplace_back(""); + auto index = findOffset(m_solvingState.variableNames, _name); + if (!index) + { + index = m_solvingState.variableNames.size(); + m_solvingState.variableNames.emplace_back(_name); + } + return *index; +} diff --git a/test/tools/ossfuzz/lpsolver/FuzzerSolverInterface.h b/test/tools/ossfuzz/lpsolver/FuzzerSolverInterface.h new file mode 100644 index 000000000..655926ad1 --- /dev/null +++ b/test/tools/ossfuzz/lpsolver/FuzzerSolverInterface.h @@ -0,0 +1,70 @@ +/* + This file is part of solidity. + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Implements the Fuzzer-Solver interface. + */ + +#pragma once + +#include +#include + +#include + +namespace solidity::test::fuzzer::lpsolver +{ + +using solution = std::pair< + solidity::util::LPResult, + std::map +>; + +class FuzzerSolverInterface +{ +public: + FuzzerSolverInterface(); + + /// @returns constant rational. + solidity::util::LinearExpression constant(solidity::util::rational _rationalConstant); + + /// @returns linear expression that equals zero. + solidity::util::LinearExpression zero() + { + return constant(0); + } + + /// @returns product of a rational factor and variable. + solidity::util::LinearExpression variable( + solidity::util::rational _factor, + std::string const& _variable + ); + + /// Adds less-than-equal-zero constraint to solver. + void addLEConstraint(solidity::util::LinearExpression _lhs); + + /// Adds equal-to-zero constraint to solver. + void addEQConstraint(solidity::util::LinearExpression _lhs); + + /// Queries LP solver and @returns solution. + solution check(); + +private: + /// Adds variable if necessary to LP solver state and @returns index of variable. + size_t variableIndex(std::string const& _name); + + solidity::util::LPSolver m_solver; + solidity::util::SolvingState m_solvingState; +}; +}