Potential fixes to memory leaks

This commit is contained in:
Bhargava Shastry 2021-05-12 11:09:24 +02:00
parent 248cef01e7
commit 4e171a8b61
3 changed files with 126 additions and 87 deletions

View File

@ -30,6 +30,12 @@ namespace solidity::test::fuzzer::mutator
struct SolidityCustomMutatorInterface struct SolidityCustomMutatorInterface
{ {
SolidityCustomMutatorInterface(uint8_t* _data, size_t _size, size_t _maxSize, unsigned _seed); SolidityCustomMutatorInterface(uint8_t* _data, size_t _size, size_t _maxSize, unsigned _seed);
~SolidityCustomMutatorInterface()
{
generator.reset();
}
/// Generates Solidity test program, copies it into buffer /// Generates Solidity test program, copies it into buffer
/// provided by libFuzzer and @returns size of the test program. /// provided by libFuzzer and @returns size of the test program.
size_t generate(); size_t generate();

View File

@ -33,11 +33,10 @@ using namespace solidity::test::fuzzer::mutator;
using namespace solidity::util; using namespace solidity::util;
using namespace std; using namespace std;
GeneratorBase::GeneratorBase(std::shared_ptr<SolidityGenerator> _mutator) GeneratorBase::GeneratorBase(SolidityGenerator* _mutator)
{ {
mutator = std::move(_mutator); mutator = _mutator;
state = mutator->testState(); state = mutator->testState();
uRandDist = mutator->uniformRandomDist();
} }
string GeneratorBase::visitChildren() string GeneratorBase::visitChildren()
@ -47,10 +46,10 @@ string GeneratorBase::visitChildren()
vector<std::pair<GeneratorPtr, unsigned>> randomisedChildren; vector<std::pair<GeneratorPtr, unsigned>> randomisedChildren;
for (auto const& child: generators) for (auto const& child: generators)
randomisedChildren.push_back(child); randomisedChildren.push_back(child);
shuffle(randomisedChildren.begin(), randomisedChildren.end(), *uRandDist->randomEngine); shuffle(randomisedChildren.begin(), randomisedChildren.end(), *uRandDist()->randomEngine);
for (auto const& child: randomisedChildren) for (auto const& child: randomisedChildren)
if (uRandDist->likely(child.second + 1)) if (uRandDist()->likely(child.second + 1))
for (unsigned i = 0; i < uRandDist->distributionOneToN(child.second); i++) for (unsigned i = 0; i < uRandDist()->distributionOneToN(child.second); i++)
os << std::visit(GenericVisitor{ os << std::visit(GenericVisitor{
[&](auto const& _item) { return _item->generate(); } [&](auto const& _item) { return _item->generate(); }
}, child.first); }, child.first);
@ -161,7 +160,7 @@ string PragmaGenerator::visit()
// Add preamble // Add preamble
pragmas.insert(string(s_preamble)); pragmas.insert(string(s_preamble));
// Choose either abicoder v1 or v2 but not both. // Choose either abicoder v1 or v2 but not both.
pragmas.insert(s_abiPragmas[uRandDist->distributionOneToN(s_abiPragmas.size()) - 1]); pragmas.insert(s_abiPragmas[uRandDist()->distributionOneToN(s_abiPragmas.size()) - 1]);
return boost::algorithm::join(pragmas, "\n") + "\n"; return boost::algorithm::join(pragmas, "\n") + "\n";
} }
@ -365,7 +364,7 @@ AssignmentStmtGenerator::AssignOp AssignmentStmtGenerator::assignOp(SolidityType
else else
solAssert(false, ""); solAssert(false, "");
return possibleOps[uRandDist->distributionOneToN(possibleOps.size()) - 1]; return possibleOps[uRandDist()->distributionOneToN(possibleOps.size()) - 1];
} }
string AssignmentStmtGenerator::assignOp(AssignOp _op) string AssignmentStmtGenerator::assignOp(AssignOp _op)
@ -426,7 +425,7 @@ void StatementGenerator::setup()
string StatementGenerator::visit() string StatementGenerator::visit()
{ {
bool unchecked = uRandDist->probable(s_uncheckedBlockInvProb); bool unchecked = uRandDist()->probable(s_uncheckedBlockInvProb);
bool inUnchecked = mutator->generator<BlockStmtGenerator>()->unchecked(); bool inUnchecked = mutator->generator<BlockStmtGenerator>()->unchecked();
// Do not generate nested unchecked blocks. // Do not generate nested unchecked blocks.
bool generateUncheckedBlock = unchecked && !inUnchecked; bool generateUncheckedBlock = unchecked && !inUnchecked;
@ -438,9 +437,9 @@ string StatementGenerator::visit()
vector<std::pair<GeneratorPtr, unsigned>> randomisedChildren; vector<std::pair<GeneratorPtr, unsigned>> randomisedChildren;
for (auto const& child: generators) for (auto const& child: generators)
randomisedChildren.push_back(child); randomisedChildren.push_back(child);
shuffle(randomisedChildren.begin(), randomisedChildren.end(), *uRandDist->randomEngine); shuffle(randomisedChildren.begin(), randomisedChildren.end(), *uRandDist()->randomEngine);
for (auto const& child: randomisedChildren) for (auto const& child: randomisedChildren)
if (uRandDist->likely(child.second + 1)) if (uRandDist()->likely(child.second + 1))
{ {
os << std::visit(GenericVisitor{ os << std::visit(GenericVisitor{
[](auto const& _item) { return _item->generate(); } [](auto const& _item) { return _item->generate(); }
@ -478,8 +477,9 @@ string BlockStmtGenerator::visit()
block << indentation() + "{\n"; block << indentation() + "{\n";
// Create blockscope inside current function state // Create blockscope inside current function state
auto newBlockScope = make_shared<BlockScope>();
state->currentFunctionState()->scopes.push_back( state->currentFunctionState()->scopes.push_back(
make_shared<BlockScope>() std::move(newBlockScope)
); );
state->indent(); state->indent();
block << visitChildren(); block << visitChildren();
@ -503,12 +503,12 @@ string FunctionGenerator::visit()
visibility = "external"; visibility = "external";
// Add I/O // Add I/O
if (uRandDist->likely(s_maxInputs + 1)) if (uRandDist()->likely(s_maxInputs + 1))
for (unsigned i = 0; i < uRandDist->distributionOneToN(s_maxInputs); i++) for (unsigned i = 0; i < uRandDist()->distributionOneToN(s_maxInputs); i++)
state->currentFunctionState()->addInput(TypeProvider{state}.type()); state->currentFunctionState()->addInput(TypeProvider{state}.type());
if (uRandDist->likely(s_maxOutputs + 1)) if (uRandDist()->likely(s_maxOutputs + 1))
for (unsigned i = 0; i < uRandDist->distributionOneToN(s_maxOutputs); i++) for (unsigned i = 0; i < uRandDist()->distributionOneToN(s_maxOutputs); i++)
state->currentFunctionState()->addOutput(TypeProvider{state}.type()); state->currentFunctionState()->addOutput(TypeProvider{state}.type());
ostringstream function; ostringstream function;
@ -1253,7 +1253,7 @@ string FunctionCallGenerator::visit()
if (availableFunctions.size() > 1) if (availableFunctions.size() > 1)
{ {
for (auto const& i: availableFunctions) for (auto const& i: availableFunctions)
if (uRandDist->probable(availableFunctions.size())) if (uRandDist()->probable(availableFunctions.size()))
callee = i; callee = i;
} }
else else
@ -1277,7 +1277,8 @@ shared_ptr<T> SolidityGenerator::generator()
SolidityGenerator::SolidityGenerator(unsigned _seed) SolidityGenerator::SolidityGenerator(unsigned _seed)
{ {
m_generators = {}; m_generators = {};
m_urd = make_shared<UniformRandomDistribution>(make_unique<RandomEngine>(_seed)); auto engine = make_unique<RandomEngine>(_seed);
m_urd = make_shared<UniformRandomDistribution>(std::move(engine));
m_state = make_shared<TestState>(m_urd); m_state = make_shared<TestState>(m_urd);
} }
@ -1299,6 +1300,5 @@ string SolidityGenerator::generateTestProgram()
[&](auto const& _item) { return _item->setup(); } [&](auto const& _item) { return _item->setup(); }
}, g); }, g);
string program = generator<TestCaseGenerator>()->generate(); string program = generator<TestCaseGenerator>()->generate();
destroyGenerators();
return program; return program;
} }

View File

@ -64,7 +64,7 @@ TYPELIST(VARIANTOFSHARED, COMMA(), )
>; >;
#undef VARIANTOFSHARED #undef VARIANTOFSHARED
#undef COMMA #undef COMMA
using RandomEngine = std::mt19937_64; using RandomEngine = std::minstd_rand;
using Distribution = std::uniform_int_distribution<size_t>; using Distribution = std::uniform_int_distribution<size_t>;
struct UniformRandomDistribution struct UniformRandomDistribution
@ -73,6 +73,11 @@ struct UniformRandomDistribution
randomEngine(std::move(_randomEngine)) randomEngine(std::move(_randomEngine))
{} {}
~UniformRandomDistribution()
{
randomEngine.reset();
}
/// @returns an unsigned integer in the range [1, @param _n] chosen /// @returns an unsigned integer in the range [1, @param _n] chosen
/// uniformly at random. /// uniformly at random.
[[nodiscard]] size_t distributionOneToN(size_t _n) const [[nodiscard]] size_t distributionOneToN(size_t _n) const
@ -408,6 +413,7 @@ struct SourceState
importedSources.clear(); importedSources.clear();
freeFunctions.clear(); freeFunctions.clear();
exports.clear(); exports.clear();
uRandDist.reset();
} }
/// Prints source state to @param _os. /// Prints source state to @param _os.
void print(std::ostream& _os) const; void print(std::ostream& _os) const;
@ -448,20 +454,21 @@ struct FunctionState
inputs.clear(); inputs.clear();
outputs.clear(); outputs.clear();
scopes.clear(); scopes.clear();
type.reset();
} }
void addInput(SolidityTypePtr _input) void addInput(SolidityTypePtr _input)
{ {
type->addInput(_input); type->addInput(_input);
inputs.emplace_back(_input, "i" + std::to_string(numInputs++)); inputs.emplace_back(std::move(_input), "i" + std::to_string(numInputs++));
} }
void addOutput(SolidityTypePtr _output) void addOutput(SolidityTypePtr _output)
{ {
type->addOutput(_output); type->addOutput(_output);
outputs.emplace_back(_output, "o" + std::to_string(numOutpus++)); outputs.emplace_back(std::move(_output), "o" + std::to_string(numOutpus++));
} }
void addLocal(SolidityTypePtr _local) void addLocal(SolidityTypePtr _local)
{ {
scopes.back()->variables.emplace_back(_local, "l" + std::to_string(numLocals++)); scopes.back()->variables.emplace_back(std::move(_local), "l" + std::to_string(numLocals++));
} }
std::string params(Params _p); std::string params(Params _p);
@ -487,6 +494,7 @@ struct ContractState
~ContractState() ~ContractState()
{ {
functions.clear(); functions.clear();
uRandDist.reset();
} }
void addFunction(std::shared_ptr<FunctionState> _function) void addFunction(std::shared_ptr<FunctionState> _function)
{ {
@ -517,22 +525,24 @@ struct TestState
/// @name currentSourceUnitPath. /// @name currentSourceUnitPath.
void addSourceUnit(std::string const& _path) void addSourceUnit(std::string const& _path)
{ {
sourceUnitState.emplace(_path, std::make_shared<SourceState>(uRandDist, _path)); auto sourceState = std::make_shared<SourceState>(uRandDist, _path);
sourceUnitState.emplace(_path, std::move(sourceState));
currentSourceUnitPath = _path; currentSourceUnitPath = _path;
} }
/// Adds @param _name to @name contractState updates /// Adds @param _name to @name contractState updates
/// @name currentContract. /// @name currentContract.
void addContract(std::string const& _name) void addContract(std::string const& _name)
{ {
contractState.emplace(_name, std::make_shared<ContractState>(uRandDist, _name)); auto newContractState = std::make_shared<ContractState>(uRandDist, _name);
sourceUnitState[currentSourceUnitPath]->exports[ contractState.emplace(_name, std::move(newContractState));
std::make_shared<ContractType>(_name) auto newContractType = std::make_shared<ContractType>(_name);
] = _name; sourceUnitState[currentSourceUnitPath]->exports[std::move(newContractType)] = _name;
currentContract = _name; currentContract = _name;
} }
void addFunction(std::string const& _name, bool _freeFunction) void addFunction(std::string const& _name, bool _freeFunction)
{ {
functionState.emplace(_name, std::make_shared<FunctionState>(_name, _freeFunction)); auto fState = std::make_shared<FunctionState>(_name, _freeFunction);
functionState.emplace(_name, std::move(fState));
currentFunction = _name; currentFunction = _name;
} }
std::shared_ptr<FunctionState> currentFunctionState() std::shared_ptr<FunctionState> currentFunctionState()
@ -639,6 +649,7 @@ struct TestState
sourceUnitState.clear(); sourceUnitState.clear();
contractState.clear(); contractState.clear();
functionState.clear(); functionState.clear();
uRandDist.reset();
} }
/// Prints test state to @param _os. /// Prints test state to @param _os.
void print(std::ostream& _os) const; void print(std::ostream& _os) const;
@ -686,6 +697,11 @@ struct TypeProvider
TypeProvider(std::shared_ptr<TestState> _state): state(std::move(_state)) TypeProvider(std::shared_ptr<TestState> _state): state(std::move(_state))
{} {}
~TypeProvider()
{
state.reset();
}
enum class Type: size_t enum class Type: size_t
{ {
INTEGER = 1, INTEGER = 1,
@ -729,6 +745,12 @@ struct LiteralGenerator
{ {
explicit LiteralGenerator(std::shared_ptr<TestState> _state): state(std::move(_state)) explicit LiteralGenerator(std::shared_ptr<TestState> _state): state(std::move(_state))
{} {}
~LiteralGenerator()
{
state.reset();
}
std::string operator()(std::shared_ptr<AddressType> const& _type); std::string operator()(std::shared_ptr<AddressType> const& _type);
std::string operator()(std::shared_ptr<BoolType> const& _type); std::string operator()(std::shared_ptr<BoolType> const& _type);
std::string operator()(std::shared_ptr<BytesType> const& _type); std::string operator()(std::shared_ptr<BytesType> const& _type);
@ -745,6 +767,11 @@ struct ExpressionGenerator
ExpressionGenerator(std::shared_ptr<TestState> _state): state(std::move(_state)) ExpressionGenerator(std::shared_ptr<TestState> _state): state(std::move(_state))
{} {}
~ExpressionGenerator()
{
state.reset();
}
enum class RLValueExpr: size_t enum class RLValueExpr: size_t
{ {
VARREF = 1, VARREF = 1,
@ -825,9 +852,54 @@ struct ExpressionGenerator
static constexpr unsigned s_maxNestingDepth = 30; static constexpr unsigned s_maxNestingDepth = 30;
}; };
class SolidityGenerator
{
public:
explicit SolidityGenerator(unsigned _seed);
~SolidityGenerator()
{
m_generators.clear();
m_urd.reset();
m_state.reset();
}
/// @returns the generator of type @param T.
template <typename T>
std::shared_ptr<T> generator();
/// @returns a shared ptr to underlying random
/// number distribution.
std::shared_ptr<UniformRandomDistribution> uniformRandomDist()
{
return m_urd;
}
/// @returns a pseudo randomly generated test case.
std::string generateTestProgram();
/// @returns shared ptr to global test state.
std::shared_ptr<TestState> testState()
{
return m_state;
}
private:
template <typename T>
void createGenerator()
{
auto generator = std::make_shared<T>(this);
m_generators.insert(std::move(generator));
}
template <std::size_t I = 0>
void createGenerators();
/// Sub generators
std::set<GeneratorPtr> m_generators;
/// Shared global test state
std::shared_ptr<TestState> m_state;
/// Uniform random distribution
std::shared_ptr<UniformRandomDistribution> m_urd;
};
struct GeneratorBase struct GeneratorBase
{ {
explicit GeneratorBase(std::shared_ptr<SolidityGenerator> _mutator); explicit GeneratorBase(SolidityGenerator* _mutator);
template <typename T> template <typename T>
std::shared_ptr<T> generator() std::shared_ptr<T> generator()
{ {
@ -863,7 +935,7 @@ struct GeneratorBase
/// this grammar element. /// this grammar element.
void addGenerators(std::set<std::pair<GeneratorPtr, unsigned>> _generators) void addGenerators(std::set<std::pair<GeneratorPtr, unsigned>> _generators)
{ {
generators += _generators; generators += std::move(_generators);
} }
/// Virtual method to obtain string name of generator. /// Virtual method to obtain string name of generator.
virtual std::string name() = 0; virtual std::string name() = 0;
@ -874,21 +946,25 @@ struct GeneratorBase
virtual ~GeneratorBase() virtual ~GeneratorBase()
{ {
generators.clear(); generators.clear();
state.reset();
} }
std::shared_ptr<UniformRandomDistribution> uRandDist()
{
return mutator->uniformRandomDist();
}
/// Shared pointer to the mutator instance /// Shared pointer to the mutator instance
std::shared_ptr<SolidityGenerator> mutator; SolidityGenerator* mutator;
/// Set of generators used by this generator. /// Set of generators used by this generator.
std::set<std::pair<GeneratorPtr, unsigned>> generators; std::set<std::pair<GeneratorPtr, unsigned>> generators;
/// Shared ptr to global test state. /// Shared ptr to global test state.
std::shared_ptr<TestState> state; std::shared_ptr<TestState> state;
/// Uniform random distribution
std::shared_ptr<UniformRandomDistribution> uRandDist;
}; };
class TestCaseGenerator: public GeneratorBase class TestCaseGenerator: public GeneratorBase
{ {
public: public:
explicit TestCaseGenerator(std::shared_ptr<SolidityGenerator> _mutator): explicit TestCaseGenerator(SolidityGenerator* _mutator):
GeneratorBase(std::move(_mutator)) GeneratorBase(std::move(_mutator))
{} {}
void setup() override; void setup() override;
@ -924,7 +1000,7 @@ private:
class SourceUnitGenerator: public GeneratorBase class SourceUnitGenerator: public GeneratorBase
{ {
public: public:
explicit SourceUnitGenerator(std::shared_ptr<SolidityGenerator> _mutator): explicit SourceUnitGenerator(SolidityGenerator* _mutator):
GeneratorBase(std::move(_mutator)) GeneratorBase(std::move(_mutator))
{} {}
void setup() override; void setup() override;
@ -938,7 +1014,7 @@ private:
class PragmaGenerator: public GeneratorBase class PragmaGenerator: public GeneratorBase
{ {
public: public:
explicit PragmaGenerator(std::shared_ptr<SolidityGenerator> _mutator): explicit PragmaGenerator(SolidityGenerator* _mutator):
GeneratorBase(std::move(_mutator)) GeneratorBase(std::move(_mutator))
{} {}
std::string visit() override; std::string visit() override;
@ -954,7 +1030,7 @@ private:
class ImportGenerator: public GeneratorBase class ImportGenerator: public GeneratorBase
{ {
public: public:
explicit ImportGenerator(std::shared_ptr<SolidityGenerator> _mutator): explicit ImportGenerator(SolidityGenerator* _mutator):
GeneratorBase(std::move(_mutator)) GeneratorBase(std::move(_mutator))
{} {}
std::string visit() override; std::string visit() override;
@ -964,7 +1040,7 @@ public:
class ContractGenerator: public GeneratorBase class ContractGenerator: public GeneratorBase
{ {
public: public:
explicit ContractGenerator(std::shared_ptr<SolidityGenerator> _mutator): explicit ContractGenerator(SolidityGenerator* _mutator):
GeneratorBase(std::move(_mutator)) GeneratorBase(std::move(_mutator))
{} {}
void setup() override; void setup() override;
@ -977,7 +1053,7 @@ private:
class FunctionGenerator: public GeneratorBase class FunctionGenerator: public GeneratorBase
{ {
public: public:
explicit FunctionGenerator(std::shared_ptr<SolidityGenerator> _mutator): explicit FunctionGenerator(SolidityGenerator* _mutator):
GeneratorBase(std::move(_mutator)), GeneratorBase(std::move(_mutator)),
m_freeFunction(true) m_freeFunction(true)
{} {}
@ -1000,7 +1076,7 @@ private:
class StatementGenerator: public GeneratorBase class StatementGenerator: public GeneratorBase
{ {
public: public:
explicit StatementGenerator(std::shared_ptr<SolidityGenerator> _mutator): explicit StatementGenerator(SolidityGenerator* _mutator):
GeneratorBase(std::move(_mutator)) GeneratorBase(std::move(_mutator))
{} {}
void setup() override; void setup() override;
@ -1029,7 +1105,7 @@ public:
ASSIGNMOD, ASSIGNMOD,
ASSIGNMAX ASSIGNMAX
}; };
explicit AssignmentStmtGenerator(std::shared_ptr<SolidityGenerator> _mutator): explicit AssignmentStmtGenerator(SolidityGenerator* _mutator):
GeneratorBase(std::move(_mutator)) GeneratorBase(std::move(_mutator))
{} {}
std::string visit() override; std::string visit() override;
@ -1042,7 +1118,7 @@ private:
class BlockStmtGenerator: public GeneratorBase class BlockStmtGenerator: public GeneratorBase
{ {
public: public:
explicit BlockStmtGenerator(std::shared_ptr<SolidityGenerator> _mutator): explicit BlockStmtGenerator(SolidityGenerator* _mutator):
GeneratorBase(std::move(_mutator)), GeneratorBase(std::move(_mutator)),
m_nestingDepth(0), m_nestingDepth(0),
m_unchecked(false), m_unchecked(false),
@ -1095,7 +1171,7 @@ private:
class FunctionCallGenerator: public GeneratorBase class FunctionCallGenerator: public GeneratorBase
{ {
public: public:
FunctionCallGenerator(std::shared_ptr<SolidityGenerator> _mutator): FunctionCallGenerator(SolidityGenerator* _mutator):
GeneratorBase(std::move(_mutator)) GeneratorBase(std::move(_mutator))
{} {}
std::string visit() override; std::string visit() override;
@ -1108,47 +1184,4 @@ private:
std::optional<std::string> rhs(std::vector<std::pair<SolidityTypePtr, std::string>> _functionInputTypeNames); std::optional<std::string> rhs(std::vector<std::pair<SolidityTypePtr, std::string>> _functionInputTypeNames);
std::string callStmt(std::shared_ptr<FunctionState> _callee); std::string callStmt(std::shared_ptr<FunctionState> _callee);
}; };
class SolidityGenerator: public std::enable_shared_from_this<SolidityGenerator>
{
public:
explicit SolidityGenerator(unsigned _seed);
/// @returns the generator of type @param T.
template <typename T>
std::shared_ptr<T> generator();
/// @returns a shared ptr to underlying random
/// number distribution.
std::shared_ptr<UniformRandomDistribution> uniformRandomDist()
{
return m_urd;
}
/// @returns a pseudo randomly generated test case.
std::string generateTestProgram();
/// @returns shared ptr to global test state.
std::shared_ptr<TestState> testState()
{
return m_state;
}
private:
template <typename T>
void createGenerator()
{
m_generators.insert(
std::make_shared<T>(shared_from_this())
);
}
template <std::size_t I = 0>
void createGenerators();
void destroyGenerators()
{
m_generators.clear();
}
/// Sub generators
std::set<GeneratorPtr> m_generators;
/// Shared global test state
std::shared_ptr<TestState> m_state;
/// Uniform random distribution
std::shared_ptr<UniformRandomDistribution> m_urd;
};
} }