fuzz: Accept options in FUZZ_TARGET macro

* This allows to reduce the number of total macros.
* Also, adding a new option no longer requires doubling the number of
  macros in the worst case.
This commit is contained in:
MarcoFalke 2023-07-11 14:00:51 +02:00
parent 357e3f6aa4
commit fa36ad8b09
No known key found for this signature in database
3 changed files with 34 additions and 26 deletions

View File

@ -54,20 +54,25 @@ const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS =
return g_args;
};
std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>>& FuzzTargets()
struct FuzzTarget {
const TypeTestOneInput test_one_input;
const FuzzTargetOptions opts;
};
auto& FuzzTargets()
{
static std::map<std::string_view, std::tuple<TypeTestOneInput, TypeInitialize, TypeHidden>> g_fuzz_targets;
static std::map<std::string_view, FuzzTarget> g_fuzz_targets;
return g_fuzz_targets;
}
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init, TypeHidden hidden)
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts)
{
const auto it_ins = FuzzTargets().try_emplace(name, std::move(target), std::move(init), hidden);
const auto it_ins{FuzzTargets().try_emplace(name, FuzzTarget /* temporary can be dropped in C++20 */ {std::move(target), std::move(opts)})};
Assert(it_ins.second);
}
static std::string_view g_fuzz_target;
static TypeTestOneInput* g_test_one_input{nullptr};
static const TypeTestOneInput* g_test_one_input{nullptr};
void initialize()
{
@ -84,22 +89,22 @@ void initialize()
bool should_exit{false};
if (std::getenv("PRINT_ALL_FUZZ_TARGETS_AND_ABORT")) {
for (const auto& t : FuzzTargets()) {
if (std::get<2>(t.second)) continue;
std::cout << t.first << std::endl;
for (const auto& [name, t] : FuzzTargets()) {
if (t.opts.hidden) continue;
std::cout << name << std::endl;
}
should_exit = true;
}
if (const char* out_path = std::getenv("WRITE_ALL_FUZZ_TARGETS_AND_ABORT")) {
std::cout << "Writing all fuzz target names to '" << out_path << "'." << std::endl;
std::ofstream out_stream{out_path, std::ios::binary};
for (const auto& t : FuzzTargets()) {
if (std::get<2>(t.second)) continue;
out_stream << t.first << std::endl;
for (const auto& [name, t] : FuzzTargets()) {
if (t.opts.hidden) continue;
out_stream << name << std::endl;
}
should_exit= true;
should_exit = true;
}
if (should_exit){
if (should_exit) {
std::exit(EXIT_SUCCESS);
}
if (const auto* env_fuzz{std::getenv("FUZZ")}) {
@ -117,8 +122,8 @@ void initialize()
std::exit(EXIT_FAILURE);
}
Assert(!g_test_one_input);
g_test_one_input = &std::get<0>(it->second);
std::get<1>(it->second)();
g_test_one_input = &it->second.test_one_input;
it->second.opts.init();
}
#if defined(PROVIDE_FUZZ_MAIN_FUNCTION)

View File

@ -21,25 +21,28 @@
using FuzzBufferType = Span<const uint8_t>;
using TypeTestOneInput = std::function<void(FuzzBufferType)>;
using TypeInitialize = std::function<void()>;
using TypeHidden = bool;
struct FuzzTargetOptions {
std::function<void()> init{[] {}};
bool hidden{false};
};
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, TypeInitialize init, TypeHidden hidden);
void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target, FuzzTargetOptions opts);
inline void FuzzFrameworkEmptyInitFun() {}
#define FUZZ_TARGET(name) \
FUZZ_TARGET_INIT(name, FuzzFrameworkEmptyInitFun)
#if defined(__clang__)
#define FUZZ_TARGET(...) _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"") DETAIL_FUZZ(__VA_ARGS__) _Pragma("clang diagnostic pop")
#else
#define FUZZ_TARGET(...) DETAIL_FUZZ(__VA_ARGS__)
#endif
#define FUZZ_TARGET_INIT(name, init_fun) \
FUZZ_TARGET_INIT_HIDDEN(name, init_fun, false)
FUZZ_TARGET(name, .init = init_fun)
#define FUZZ_TARGET_INIT_HIDDEN(name, init_fun, hidden) \
#define DETAIL_FUZZ(name, ...) \
void name##_fuzz_target(FuzzBufferType); \
struct name##_Before_Main { \
name##_Before_Main() \
{ \
FuzzFrameworkRegisterTarget(#name, name##_fuzz_target, init_fun, hidden); \
FuzzFrameworkRegisterTarget(#name, name##_fuzz_target, {__VA_ARGS__}); \
} \
} const static g_##name##_before_main; \
void name##_fuzz_target(FuzzBufferType buffer)

View File

@ -186,7 +186,7 @@ void Test(const std::string& str)
void test_init() {}
FUZZ_TARGET_INIT_HIDDEN(script_assets_test_minimizer, test_init, /*hidden=*/true)
FUZZ_TARGET(script_assets_test_minimizer, .init = test_init, .hidden = true)
{
if (buffer.size() < 2 || buffer.back() != '\n' || buffer[buffer.size() - 2] != ',') return;
const std::string str((const char*)buffer.data(), buffer.size() - 2);