Stick to constraint parsing convention and refactor fuzzer harness.

This commit is contained in:
Bhargava Shastry 2022-02-09 12:00:15 +01:00
parent 5535bf90e2
commit 3e88f7f85a
4 changed files with 73 additions and 52 deletions

View File

@ -18,8 +18,10 @@
#include <test/tools/ossfuzz/lpsolver/FuzzerSolverInterface.h> #include <test/tools/ossfuzz/lpsolver/FuzzerSolverInterface.h>
#include <algorithm>
#include <cstddef> #include <cstddef>
#include <iostream> #include <iostream>
#include <optional>
#include <sstream> #include <sstream>
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
@ -28,13 +30,16 @@
using namespace solidity::test::fuzzer::lpsolver; using namespace solidity::test::fuzzer::lpsolver;
using namespace std; using namespace std;
using Constraint = pair<bool, vector<int>>;
using Constraints = vector<Constraint>;
// Prototype as we can't use the FuzzerInterface.h header. // 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* _data, size_t _size);
namespace namespace
{ {
#ifdef DEBUG #ifdef DEBUG
void printConstraints(vector<pair<bool, vector<int>>> _constraints) void printConstraints(Constraints _constraints)
{ {
for (auto& i: _constraints) for (auto& i: _constraints)
{ {
@ -46,65 +51,85 @@ void printConstraints(vector<pair<bool, vector<int>>> _constraints)
} }
#endif #endif
bool validConstraints(vector<pair<bool, vector<int>>> _constraints) bool validInput(string const& _input)
{ {
return all_of(
_input.begin(),
_input.end(),
[](unsigned char _c) { return isdigit(_c) || (_c == ',') || (_c == '-') || (_c == '\n'); }
);
}
optional<Constraints> parseConstraints(istringstream& _input)
{
Constraints constraints;
for (string line; getline(_input, line); )
{
istringstream lineStream;
lineStream.str(line);
Constraint constraint;
bool first = true;
for (string field; getline(lineStream, field, ','); )
{
int val = 0;
try
{
val = stoi(field);
}
// Fuzzer can sometimes supply invalid input to stoi that needs to be
// rejected.
catch (invalid_argument const&)
{
return nullopt;
}
if (first)
{
constraint.first = static_cast<bool>(val);
first = false;
}
else
constraint.second.emplace_back(val);
}
constraints.emplace_back(constraint);
}
// Zero input constraints is an invalid input // Zero input constraints is an invalid input
if (_constraints.size() < 1) if (constraints.size() < 1)
return false; return nullopt;
// Incomplete constraints are invalid // Incomplete constraints are invalid
for (auto c: _constraints) for (auto c: constraints)
if (c.second.empty()) if (c.second.empty())
return false; return nullopt;
return true; return constraints;
} }
} }
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
{ {
// Parse CSV input
istringstream input; istringstream input;
input.str(string(reinterpret_cast<char const*>(_data), _size)); input.str(string(reinterpret_cast<char const*>(_data), _size));
if (validInput(input.str()))
vector<pair<bool, vector<int>>> constraints;
for (string line; getline(input, line); )
{ {
istringstream lineStream; // Parse CSV input
lineStream.str(line); auto constraints = parseConstraints(input);
pair<bool, vector<int>> constraint; if (constraints.has_value())
bool first = true;
for (string field; getline(lineStream, field, ','); )
{ {
if (first) // TODO: Z3 on constraints provided by fuzzer interface and comparing its outcome
// with LP solver.
FuzzerSolverInterface solverWithoutModels(/*supportModels=*/false);
FuzzerSolverInterface solverWithModels(/*supportModels=*/true);
solverWithoutModels.addConstraints(constraints.value());
string resultWithoutModels = solverWithoutModels.checkResult();
solverWithModels.addConstraints(constraints.value());
string resultWithModels = solverWithModels.checkResult();
if (resultWithoutModels != resultWithModels)
{ {
constraint.first = static_cast<bool>(stoi(field)); cout << resultWithoutModels << endl;
first = false; cout << resultWithModels << endl;
solAssert(false, "LP result without models did not match with result with models.");
} }
else
constraint.second.emplace_back(stoi(field));
} }
constraints.emplace_back(constraint);
}
if (!validConstraints(constraints))
return 0;
else
{
// TODO: Z3 on constraints provided by fuzzer interface and comparing its outcome
// with LP solver.
FuzzerSolverInterface solverWithoutModels(/*supportModels=*/false);
FuzzerSolverInterface solverWithModels(/*supportModels=*/true);
solverWithoutModels.addConstraints(constraints);
string resultWithoutModels = solverWithoutModels.checkResult();
solverWithModels.addConstraints(constraints);
string resultWithModels = solverWithModels.checkResult();
if (resultWithoutModels != resultWithModels)
{
cout << resultWithoutModels << endl;
cout << resultWithModels << endl;
solAssert(false, "LP result without models did not match with result with models.");
}
return 0;
} }
return 0;
} }

View File

@ -33,7 +33,7 @@ string ConstraintGenerator::generate()
constraint += to_string(zeroOrOne()); constraint += to_string(zeroOrOne());
for (int j = 0; j < numFactors(); j++) for (int j = 0; j < numFactors(); j++)
if (bernoulliDist(s_piecewiseConstantProb)) if (bernoulliDist(s_piecewiseConstantProb))
constraint += "," + to_string(randomMinusOneToOne()); constraint += ",0";
else else
constraint += "," + to_string(randomInteger()); constraint += "," + to_string(randomInteger());
constraint += "\n"; constraint += "\n";

View File

@ -90,7 +90,7 @@ struct ConstraintGenerator
/// Largest value of a factor in linear constraint /// Largest value of a factor in linear constraint
static constexpr int s_maxFactor = 100; static constexpr int s_maxFactor = 100;
/// Probability that a factor in the range of [-1, 1] is chosen /// Probability that a factor in the range of [-1, 1] is chosen
static constexpr double s_piecewiseConstantProb = 0.25; static constexpr double s_piecewiseConstantProb = 0.75;
}; };
} }

View File

@ -32,11 +32,7 @@ LinearExpression FuzzerSolverInterface::linearExpression(vector<int> _factors)
LinearExpression lexp; LinearExpression lexp;
lexp.resize(_factors.size()); lexp.resize(_factors.size());
for (auto&& [index, value]: _factors | ranges::views::enumerate) for (auto&& [index, value]: _factors | ranges::views::enumerate)
// Move constant term to RHS. lexp[index] = rational{value};
if (index == 0)
lexp[index] = -rational{value};
else
lexp[index] = rational{value};
return lexp; return lexp;
} }