Fix CLI parameters in fuzzers

This CL changes the prefix of CLI parameters in AST and SPIRV-Tools
fuzzers from `--` to `-` to make these fuzzers compatible with ClusterFuzz.
Additionally, a `tint_` prefix was added to all CLI arguments to prevent their
name collisions with LibFuzzer arguments.

Change-Id: Id2e087e59f04b495d5a7edb3b62d55de652c1acd
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/58226
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Alastair Donaldson <afdx@google.com>
Commit-Queue: Alastair Donaldson <afdx@google.com>
This commit is contained in:
Vasyl Teliman 2021-07-16 10:26:34 +00:00 committed by Tint LUCI CQ
parent 365af046ca
commit 979a0b4446
6 changed files with 109 additions and 72 deletions

View File

@ -19,6 +19,7 @@
#include <limits>
#include <sstream>
#include <string>
#include <utility>
namespace tint {
namespace fuzzers {
@ -31,28 +32,28 @@ This is a fuzzer for the Tint compiler that works by mutating the AST.
Below is a list of all supported parameters for this fuzzer. You may want to
run it with -help=1 to check out libfuzzer parameters.
--enable_all_mutations=
-tint_enable_all_mutations=
If `false`, the fuzzer will only apply mutations from a
randomly selected subset of mutation types. Otherwise,
all mutation types will be considered. This must be one
of `true` or `false` (without `). By default it's `false`.
--fuzzing_target=
-tint_fuzzing_target=
Specifies the shading language to target during fuzzing.
This must be one or a combination of `wgsl`, `spv`, `hlsl`,
`msl` (without `) separated by commas. By default it's
`wgsl,msl,hlsl,spv`.
--help
-tint_help
Show this message. Note that there is also a -help=1
parameter that will display libfuzzer's help message.
--mutation_batch_size=
-tint_mutation_batch_size=
The number of mutations to apply in a single libfuzzer
mutation session. This must be a numeric value that fits
in type `uint32_t`. By default it's 5.
--record_mutations=
-tint_record_mutations=
Whether to record applied mutations in the protobuf
message. This is useful to debug the fuzzer and during
metamorphic fuzzing. The value must be one of `true` or
@ -106,31 +107,33 @@ bool ParseFuzzingTarget(const char* value, FuzzingTarget* out) {
} // namespace
CliParams ParseCliParams(int argc, const char* const* argv) {
CliParams ParseCliParams(int* argc, char** argv) {
CliParams cli_params;
auto help = false;
for (int i = 1; i < argc; ++i) {
for (int i = *argc - 1; i > 0; --i) {
auto param = argv[i];
if (HasPrefix(param, "--record_mutations=")) {
if (!ParseBool(param + sizeof("--record_mutations=") - 1,
auto recognized_parameter = true;
if (HasPrefix(param, "-tint_record_mutations=")) {
if (!ParseBool(param + sizeof("-tint_record_mutations=") - 1,
&cli_params.record_mutations)) {
InvalidParam(param);
}
} else if (HasPrefix(param, "--enable_all_mutations=")) {
if (!ParseBool(param + sizeof("--enable_all_mutations=") - 1,
} else if (HasPrefix(param, "-tint_enable_all_mutations=")) {
if (!ParseBool(param + sizeof("-tint_enable_all_mutations=") - 1,
&cli_params.enable_all_mutations)) {
InvalidParam(param);
}
} else if (HasPrefix(param, "--mutation_batch_size=")) {
if (!ParseUint32(param + sizeof("--mutation_batch_size=") - 1,
} else if (HasPrefix(param, "-tint_mutation_batch_size=")) {
if (!ParseUint32(param + sizeof("-tint_mutation_batch_size=") - 1,
&cli_params.mutation_batch_size)) {
InvalidParam(param);
}
} else if (HasPrefix(param, "--fuzzing_target=")) {
} else if (HasPrefix(param, "-tint_fuzzing_target=")) {
auto result = FuzzingTarget::kNone;
std::stringstream ss(param + sizeof("--fuzzing_target=") - 1);
std::stringstream ss(param + sizeof("-tint_fuzzing_target=") - 1);
for (std::string value; std::getline(ss, value, ',');) {
auto tmp = FuzzingTarget::kNone;
if (!ParseFuzzingTarget(value.c_str(), &tmp)) {
@ -144,8 +147,21 @@ CliParams ParseCliParams(int argc, const char* const* argv) {
}
cli_params.fuzzing_target = result;
} else if (!strcmp(param, "--help")) {
} else if (!strcmp(param, "-tint_help")) {
help = true;
} else {
recognized_parameter = false;
}
if (recognized_parameter) {
// Remove the recognized parameter from the list of all parameters by
// swapping it with the last one. This will suppress warnings in the
// libFuzzer about unrecognized parameters. By default, libFuzzer thinks
// that all user-defined parameters start with two dashes. However, we are
// forced to use a single one to make the fuzzer compatible with the
// ClusterFuzz.
std::swap(argv[i], argv[*argc - 1]);
*argc -= 1;
}
}

View File

@ -39,7 +39,7 @@ inline FuzzingTarget operator&(FuzzingTarget a, FuzzingTarget b) {
return static_cast<FuzzingTarget>(static_cast<int>(a) & static_cast<int>(b));
}
/// CLI parameters accepted by the fuzzer. Type --help in the CLI to see the
/// CLI parameters accepted by the fuzzer. Type -tint_help in the CLI to see the
/// help message
struct CliParams {
/// Whether to record applied mutations.
@ -60,12 +60,13 @@ struct CliParams {
/// @brief Parses CLI parameters.
///
/// This function will exit the process with non-zero return code if some
/// parameters are invalid.
/// parameters are invalid. This function will remove recognized parameters from
/// `argv` and adjust `argc` accordingly.
///
/// @param argc - the total number of parameters.
/// @param argv - array of all CLI parameters.
/// @return parsed parameters.
CliParams ParseCliParams(int argc, const char* const* argv);
CliParams ParseCliParams(int* argc, char** argv);
} // namespace ast_fuzzer
} // namespace fuzzers

View File

@ -34,7 +34,7 @@ CliParams cli_params{};
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
// Parse CLI parameters. `ParseCliParams` will call `exit` if some parameter
// is invalid.
cli_params = ParseCliParams(*argc, *argv);
cli_params = ParseCliParams(argc, *argv);
return 0;
}

View File

@ -32,59 +32,59 @@ namespace {
const char* const kMutatorParameters = R"(
Mutators' parameters:
--donors=
-tint_donors=
A path to the text file with a list of paths to the
SPIR-V donor files. Check out the doc for the spirv-fuzz
to learn more about donor binaries. Donors are not used
by default.
--enable_all_fuzzer_passes=
-tint_enable_all_fuzzer_passes=
Whether to use all fuzzer passes or a randomly selected subset
of them. This must be one of `true` or `false` (without `).
By default it's `false`.
--enable_all_reduce_passes=
-tint_enable_all_reduce_passes=
Whether to use all reduction passes or a randomly selected subset
of them. This must be one of `true` or `false` (without `).
By default it's `false`.
--opt_batch_size=
-tint_opt_batch_size=
The maximum number of spirv-opt optimizations that
will be applied in a single mutation session (i.e.
a call to LLVMFuzzerCustomMutator). This must fit in
uint32_t. By default it's 6.
--reduction_batch_size=
-tint_reduction_batch_size=
The maximum number of spirv-reduce reductions that
will be applied in a single mutation session (i.e.
a call to LLVMFuzzerCustomMutator). This must fit in
uint32_t. By default it's 3.
--repeated_pass_strategy=
-tint_repeated_pass_strategy=
The strategy that will be used to recommend the next fuzzer
pass. This must be one of `simple`, `looped` or `random`
(without `). By default it's `simple`. Check out the doc for
spirv-fuzz to learn more.
--transformation_batch_size=
-tint_transformation_batch_size=
The maximum number of spirv-fuzz transformations
that will be applied during a single mutation
session (i.e. a call to LLVMFuzzerCustomMutator).
This must fit in uint32_t. By default it's 3.
--validate_after_each_fuzzer_pass=
-tint_validate_after_each_fuzzer_pass=
Whether to validate SPIR-V binary after each fuzzer pass.
This must be one of `true` or `false` (without `).
By default it's `true`. Switch this to `false` if you experience
bad performance.
--validate_after_each_opt_pass=
-tint_validate_after_each_opt_pass=
Whether to validate SPIR-V binary after each optimization pass.
This must be one of `true` or `false` (without `).
By default it's `true`. Switch this to `false` if you experience
bad performance.
--validate_after_each_reduce_pass=
-tint_validate_after_each_reduce_pass=
Whether to validate SPIR-V binary after each reduction pass.
This must be one of `true` or `false` (without `).
By default it's `true`. Switch this to `false` if you experience
@ -105,7 +105,7 @@ run it with -help=1 to check out libfuzzer parameters.
Fuzzer parameters:
--error_dir
-tint_error_dir
The directory that will be used to output invalid SPIR-V
binaries to. This is especially useful during debugging
mutators. The directory must have the following subdirectories:
@ -117,22 +117,22 @@ Fuzzer parameters:
the mutators.
By default invalid files are not printed out.
--fuzzing_target
-tint_fuzzing_target
The type of backend to target during fuzzing. This must
be one or a combination of `wgsl`, `spv`, `msl` or `hlsl`
(without `) separated by commas. By default it's
`wgsl,spv,msl,hlsl`.
--help
-tint_help
Show this message. Note that there is also a -help=1
parameter that will display libfuzzer's help message.
--mutator_cache_size=
-tint_mutator_cache_size=
The maximum size of the cache that stores
mutation sessions. This must fit in uint32_t.
By default it's 20.
--mutator_type=
-tint_mutator_type=
Determines types of the mutators to run. This must be one or
a combination of `fuzz`, `opt`, `reduce` (without `) separated by
comma. If a combination is specified, each element in the
@ -287,80 +287,83 @@ bool HasPrefix(const char* str, const char* prefix) {
return strncmp(str, prefix, strlen(prefix)) == 0;
}
void ParseMutatorCliParam(const char* param,
bool ParseMutatorCliParam(const char* param,
const char* help_message,
MutatorCliParams* out) {
if (HasPrefix(param, "--transformation_batch_size=")) {
if (!ParseUint32(param + sizeof("--transformation_batch_size=") - 1,
if (HasPrefix(param, "-tint_transformation_batch_size=")) {
if (!ParseUint32(param + sizeof("-tint_transformation_batch_size=") - 1,
&out->transformation_batch_size)) {
InvalidParameter(help_message, param);
}
} else if (HasPrefix(param, "--reduction_batch_size=")) {
if (!ParseUint32(param + sizeof("--reduction_batch_size=") - 1,
} else if (HasPrefix(param, "-tint_reduction_batch_size=")) {
if (!ParseUint32(param + sizeof("-tint_reduction_batch_size=") - 1,
&out->reduction_batch_size)) {
InvalidParameter(help_message, param);
}
} else if (HasPrefix(param, "--opt_batch_size=")) {
if (!ParseUint32(param + sizeof("--opt_batch_size=") - 1,
} else if (HasPrefix(param, "-tint_opt_batch_size=")) {
if (!ParseUint32(param + sizeof("-tint_opt_batch_size=") - 1,
&out->opt_batch_size)) {
InvalidParameter(help_message, param);
}
} else if (HasPrefix(param, "--donors=")) {
out->donors = ParseDonors(param + sizeof("--donors=") - 1);
} else if (HasPrefix(param, "--repeated_pass_strategy=")) {
} else if (HasPrefix(param, "-tint_donors=")) {
out->donors = ParseDonors(param + sizeof("-tint_donors=") - 1);
} else if (HasPrefix(param, "-tint_repeated_pass_strategy=")) {
if (!ParseRepeatedPassStrategy(
param + sizeof("--repeated_pass_strategy=") - 1,
param + sizeof("-tint_repeated_pass_strategy=") - 1,
&out->repeated_pass_strategy)) {
InvalidParameter(help_message, param);
}
} else if (HasPrefix(param, "--enable_all_fuzzer_passes=")) {
if (!ParseBool(param + sizeof("--enable_all_fuzzer_passes=") - 1,
} else if (HasPrefix(param, "-tint_enable_all_fuzzer_passes=")) {
if (!ParseBool(param + sizeof("-tint_enable_all_fuzzer_passes=") - 1,
&out->enable_all_fuzzer_passes)) {
InvalidParameter(help_message, param);
}
} else if (HasPrefix(param, "--enable_all_reduce_passes=")) {
if (!ParseBool(param + sizeof("--enable_all_reduce_passes=") - 1,
} else if (HasPrefix(param, "-tint_enable_all_reduce_passes=")) {
if (!ParseBool(param + sizeof("-tint_enable_all_reduce_passes=") - 1,
&out->enable_all_reduce_passes)) {
InvalidParameter(help_message, param);
}
} else if (HasPrefix(param, "--validate_after_each_opt_pass=")) {
if (!ParseBool(param + sizeof("--validate_after_each_opt_pass=") - 1,
} else if (HasPrefix(param, "-tint_validate_after_each_opt_pass=")) {
if (!ParseBool(param + sizeof("-tint_validate_after_each_opt_pass=") - 1,
&out->validate_after_each_opt_pass)) {
InvalidParameter(help_message, param);
}
} else if (HasPrefix(param, "--validate_after_each_fuzzer_pass=")) {
if (!ParseBool(param + sizeof("--validate_after_each_fuzzer_pass=") - 1,
} else if (HasPrefix(param, "-tint_validate_after_each_fuzzer_pass=")) {
if (!ParseBool(param + sizeof("-tint_validate_after_each_fuzzer_pass=") - 1,
&out->validate_after_each_fuzzer_pass)) {
InvalidParameter(help_message, param);
}
} else if (HasPrefix(param, "--validate_after_each_reduce_pass=")) {
if (!ParseBool(param + sizeof("--validate_after_each_reduce_pass=") - 1,
} else if (HasPrefix(param, "-tint_validate_after_each_reduce_pass=")) {
if (!ParseBool(param + sizeof("-tint_validate_after_each_reduce_pass=") - 1,
&out->validate_after_each_reduce_pass)) {
InvalidParameter(help_message, param);
}
} else {
return false;
}
return true;
}
} // namespace
FuzzerCliParams ParseFuzzerCliParams(int argc, const char* const* argv) {
FuzzerCliParams ParseFuzzerCliParams(int* argc, char** argv) {
FuzzerCliParams cli_params;
const auto* help_message = kFuzzerHelpMessage;
auto help = false;
for (int i = 0; i < argc; ++i) {
for (int i = *argc - 1; i > 0; --i) {
auto param = argv[i];
ParseMutatorCliParam(param, help_message, &cli_params.mutator_params);
auto recognized_param = true;
if (HasPrefix(param, "--mutator_cache_size=")) {
if (!ParseUint32(param + sizeof("--mutator_cache_size=") - 1,
if (HasPrefix(param, "-tint_mutator_cache_size=")) {
if (!ParseUint32(param + sizeof("-tint_mutator_cache_size=") - 1,
&cli_params.mutator_cache_size)) {
InvalidParameter(help_message, param);
}
} else if (HasPrefix(param, "--mutator_type=")) {
} else if (HasPrefix(param, "-tint_mutator_type=")) {
auto result = MutatorType::kNone;
std::stringstream ss(param + sizeof("--mutator_type=") - 1);
std::stringstream ss(param + sizeof("-tint_mutator_type=") - 1);
for (std::string value; std::getline(ss, value, ',');) {
auto out = MutatorType::kNone;
if (!ParseMutatorType(value.c_str(), &out)) {
@ -374,10 +377,10 @@ FuzzerCliParams ParseFuzzerCliParams(int argc, const char* const* argv) {
}
cli_params.mutator_type = result;
} else if (HasPrefix(param, "--fuzzing_target=")) {
} else if (HasPrefix(param, "-tint_fuzzing_target=")) {
auto result = FuzzingTarget::kNone;
std::stringstream ss(param + sizeof("--fuzzing_target=") - 1);
std::stringstream ss(param + sizeof("-tint_fuzzing_target=") - 1);
for (std::string value; std::getline(ss, value, ',');) {
auto tmp = FuzzingTarget::kNone;
if (!ParseFuzzingTarget(value.c_str(), &tmp)) {
@ -391,10 +394,24 @@ FuzzerCliParams ParseFuzzerCliParams(int argc, const char* const* argv) {
}
cli_params.fuzzing_target = result;
} else if (HasPrefix(param, "--error_dir=")) {
cli_params.error_dir = param + sizeof("--error_dir=") - 1;
} else if (!strcmp(param, "--help")) {
} else if (HasPrefix(param, "-tint_error_dir=")) {
cli_params.error_dir = param + sizeof("-tint_error_dir=") - 1;
} else if (!strcmp(param, "-tint_help")) {
help = true;
} else {
recognized_param = ParseMutatorCliParam(param, help_message,
&cli_params.mutator_params);
}
if (recognized_param) {
// Remove the recognized parameter from the list of all parameters by
// swapping it with the last one. This will suppress warnings in the
// libFuzzer about unrecognized parameters. By default, libFuzzer thinks
// that all user-defined parameters start with two dashes. However, we are
// forced to use a single one to make the fuzzer compatible with the
// ClusterFuzz.
std::swap(argv[i], argv[*argc - 1]);
*argc -= 1;
}
}

View File

@ -102,7 +102,8 @@ struct MutatorCliParams {
bool validate_after_each_reduce_pass = true;
};
/// Parameters specific to the fuzzer. Type `--help` in the CLI to learn more.
/// Parameters specific to the fuzzer. Type `-tint_help` in the CLI to learn
/// more.
struct FuzzerCliParams {
/// The size of the cache that records ongoing mutation sessions.
uint32_t mutator_cache_size = 20;
@ -138,13 +139,15 @@ struct MutatorDebuggerCliParams {
/// Parses CLI parameters for the fuzzer. This function exits with an error code
/// and a message is printed to the console if some parameter has invalid
/// format. You can pass `--help` to check out all available parameters.
/// format. You can pass `-tint_help` to check out all available parameters.
/// This function will remove recognized parameters from the `argv` and adjust
/// the `argc` accordingly.
///
/// @param argc - the number of parameters (identical to the `argc` in `main`
/// function).
/// @param argv - array of C strings of parameters.
/// @return the parsed parameters.
FuzzerCliParams ParseFuzzerCliParams(int argc, const char* const* argv);
FuzzerCliParams ParseFuzzerCliParams(int* argc, char** argv);
/// Parses CLI parameters for the mutator debugger. This function exits with an
/// error code and a message is printed to the console if some parameter has

View File

@ -38,7 +38,7 @@ struct Context {
Context* context = nullptr;
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
auto params = ParseFuzzerCliParams(*argc, *argv);
auto params = ParseFuzzerCliParams(argc, *argv);
auto mutator_cache =
params.mutator_cache_size
? std::make_unique<MutatorCache>(params.mutator_cache_size)