/src/args/fuzz/numeric_parser_fuzzer.cpp
Line | Count | Source |
1 | | #include <string> |
2 | | #include <cstdint> |
3 | | #include <args.hxx> |
4 | | |
5 | | /* |
6 | | * Numeric conversion harness for args::ValueReader. |
7 | | * |
8 | | * Repaired for OSS-Fuzz: ValueReader::operator() throws args::ParseError |
9 | | * for any input the requested type cannot represent, and the original |
10 | | * harness had no exception handling, so the first malformed byte string |
11 | | * aborted the whole target before later conversions could run. |
12 | | * |
13 | | * Each conversion is now attempted independently, with the expected |
14 | | * rejection swallowed per type, so a single input reaches every numeric |
15 | | * path: strtoll/strtoull-based integers (with range and narrowing |
16 | | * checks), sign rejection for unsigned types, trailing-whitespace |
17 | | * tolerance, and istringstream-based parsing for bool, char, and |
18 | | * floating-point types. Embedded NUL bytes are preserved on purpose: |
19 | | * the readers deliberately distinguish "12\0junk" from "12". |
20 | | */ |
21 | | |
22 | | // Accumulates converted values so the conversions cannot be optimized away. |
23 | | unsigned long g_sink = 0; |
24 | | |
25 | | template <typename T> |
26 | | static T AttemptConversion(args::ValueReader &reader, const std::string &input) |
27 | 7.45k | { |
28 | 7.45k | T destination{}; |
29 | 7.45k | try |
30 | 7.45k | { |
31 | 7.45k | reader("fuzz_test", input, destination); |
32 | 7.45k | } |
33 | 7.45k | catch (...) |
34 | 7.45k | { |
35 | | // Expected rejection for values this type cannot represent. |
36 | 4.42k | } |
37 | 7.45k | return destination; |
38 | 7.45k | } numeric_parser_fuzzer.cpp:bool AttemptConversion<bool>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 489 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:char AttemptConversion<char>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 465 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:signed char AttemptConversion<signed char>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 465 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:unsigned char AttemptConversion<unsigned char>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 465 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:short AttemptConversion<short>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 436 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:int AttemptConversion<int>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 306 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:long AttemptConversion<long>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 146 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:long long AttemptConversion<long long>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 146 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:unsigned short AttemptConversion<unsigned short>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 434 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:unsigned int AttemptConversion<unsigned int>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 345 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:unsigned long AttemptConversion<unsigned long>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 232 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:unsigned long long AttemptConversion<unsigned long long>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 232 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:float AttemptConversion<float>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 91 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:double AttemptConversion<double>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 88 | } | 37 | 497 | return destination; | 38 | 497 | } |
numeric_parser_fuzzer.cpp:long double AttemptConversion<long double>(args::ValueReader&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Line | Count | Source | 27 | 497 | { | 28 | 497 | T destination{}; | 29 | 497 | try | 30 | 497 | { | 31 | 497 | reader("fuzz_test", input, destination); | 32 | 497 | } | 33 | 497 | catch (...) | 34 | 497 | { | 35 | | // Expected rejection for values this type cannot represent. | 36 | 86 | } | 37 | 497 | return destination; | 38 | 497 | } |
|
39 | | |
40 | 497 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
41 | | // Create a string from fuzzer input, preserving embedded NULs |
42 | 497 | std::string input(reinterpret_cast<const char*>(data), size); |
43 | | |
44 | 497 | args::ValueReader reader; |
45 | | |
46 | | // Character-ish types (istringstream-based paths) |
47 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<bool>(reader, input)); |
48 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<char>(reader, input)); |
49 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<signed char>(reader, input)); |
50 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<unsigned char>(reader, input)); |
51 | | |
52 | | // Test signed integer types (strtoll-based path, narrowing checks) |
53 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<short>(reader, input)); |
54 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<int>(reader, input)); |
55 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<long>(reader, input)); |
56 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<long long>(reader, input)); |
57 | | |
58 | | // Test unsigned integer types (strtoull-based path, sign rejection) |
59 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<unsigned short>(reader, input)); |
60 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<unsigned int>(reader, input)); |
61 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<unsigned long>(reader, input)); |
62 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<unsigned long long>(reader, input)); |
63 | | |
64 | | // Test floating point types (istringstream-based path) |
65 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<float>(reader, input)); |
66 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<double>(reader, input)); |
67 | 497 | g_sink += static_cast<unsigned long>(AttemptConversion<long double>(reader, input)); |
68 | | |
69 | 497 | return 0; |
70 | 497 | } |