/* 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 #include #include #include using namespace solidity::test::fuzzer; using namespace solidity::test::fuzzer::mutator; using namespace solidity::util; using namespace std; GeneratorBase::GeneratorBase(std::shared_ptr _mutator) { mutator = std::move(_mutator); state = mutator->testState(); uRandDist = mutator->uniformRandomDist(); } string GeneratorBase::visitChildren() { ostringstream os; // Randomise visit order vector> randomisedChildren; for (auto const& child: generators) randomisedChildren.push_back(child); shuffle(randomisedChildren.begin(), randomisedChildren.end(), *uRandDist->randomEngine); for (auto const& child: randomisedChildren) if (uRandDist->likely(child.second + 1)) for (unsigned i = 0; i < uRandDist->distributionOneToN(child.second); i++) os << std::visit(GenericVisitor{ [&](auto const& _item) { return _item->generate(); } }, child.first); return os.str(); } void SourceState::print(std::ostream& _os) const { for (auto const& import: importedSources) _os << "Imports: " << import << std::endl; } set TestState::sourceUnitPaths() const { set keys; boost::copy(sourceUnitState | boost::adaptors::map_keys, std::inserter(keys, keys.begin())); return keys; } string TestState::randomPath(set const& _sourceUnitPaths) const { auto it = _sourceUnitPaths.begin(); /// Advance iterator by n where 0 <= n <= sourceUnitPaths.size() - 1 size_t increment = uRandDist->distributionOneToN(_sourceUnitPaths.size()) - 1; solAssert( increment >= 0 && increment < _sourceUnitPaths.size(), "Solc custom mutator: Invalid increment" ); advance(it, increment); return *it; } string TestState::randomPath() const { solAssert(!empty(), "Solc custom mutator: Null test state"); return randomPath(sourceUnitPaths()); } void TestState::print(std::ostream& _os) const { _os << "Printing test state" << std::endl; for (auto const& item: sourceUnitState) { _os << "Source path: " << item.first << std::endl; item.second->print(_os); } } string TestState::randomNonCurrentPath() const { /// To obtain a source path that is not the currently visited /// source unit itself, we require at least one other source /// unit to be previously visited. solAssert(size() >= 2, "Solc custom mutator: Invalid test state"); set filteredSourcePaths; string currentPath = currentSourceUnitPath; set sourcePaths = sourceUnitPaths(); copy_if( sourcePaths.begin(), sourcePaths.end(), inserter(filteredSourcePaths, filteredSourcePaths.begin()), [currentPath](string const& _item) { return _item != currentPath; } ); return randomPath(filteredSourcePaths); } void TestCaseGenerator::setup() { addGenerators({ {mutator->generator(), s_maxSourceUnits} }); } string TestCaseGenerator::visit() { return visitChildren(); } void SourceUnitGenerator::setup() { addGenerators({ {mutator->generator(), s_maxImports}, {mutator->generator(), 1} }); } string SourceUnitGenerator::visit() { state->addSource(); ostringstream os; os << "\n" << "==== Source: " << state->currentPath() << " ====" << "\n"; os << visitChildren(); return os.str(); } string PragmaGenerator::visit() { set pragmas = uRandDist->subset(s_genericPragmas); // Choose either abicoder v1 or v2 but not both. pragmas.insert(s_abiPragmas[uRandDist->distributionOneToN(s_abiPragmas.size()) - 1]); return boost::algorithm::join(pragmas, "\n") + "\n"; } string ImportGenerator::visit() { /* * Case 1: No source units defined * Case 2: One source unit defined * Case 3: At least two source units defined */ ostringstream os; string importPath; // Import a different source unit if at least // two source units available. if (state->size() > 1) importPath = state->randomNonCurrentPath(); // Do not reimport already imported source unit if (!importPath.empty() && !state->sourceUnitState[state->currentPath()]->sourcePathImported(importPath)) { os << "import " << "\"" << importPath << "\";\n"; state->sourceUnitState[state->currentPath()]->addImportedSourcePath(importPath); } return os.str(); } template shared_ptr SolidityGenerator::generator() { for (auto& g: m_generators) if (holds_alternative>(g)) return get>(g); solAssert(false, ""); } SolidityGenerator::SolidityGenerator(unsigned _seed) { m_generators = {}; m_urd = make_shared(make_unique(_seed)); m_state = make_shared(m_urd); } template void SolidityGenerator::createGenerators() { if constexpr (I < std::variant_size_v) { createGenerator>(); createGenerators(); } } string SolidityGenerator::generateTestProgram() { createGenerators(); for (auto& g: m_generators) std::visit(GenericVisitor{ [&](auto const& _item) { return _item->setup(); } }, g); string program = generator()->generate(); destroyGenerators(); return program; }