Skip to content

Commit 2d79205

Browse files
Fix some minor fuzzing issues. (#1138)
One in the fuzz check handling of NaN's and a pathway to generate a HorribleError. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent f871b62 commit 2d79205

File tree

6 files changed

+63
-6
lines changed

6 files changed

+63
-6
lines changed

fuzz/fuzzApp.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,18 @@ bool FuzzApp::compare(const FuzzApp &other) const {
193193
}
194194

195195
if(vv1 != other.vv1) {
196-
return false;
196+
if(vv1.size() != other.vv1.size()) {
197+
return false;
198+
}
199+
// need to check if they are both nan
200+
for(std::size_t index = 0; index < vv1.size(); ++index) {
201+
if(vv1[index] != other.vv1[index]) {
202+
if(std::isnan(vv1[index]) && std::isnan(other.vv1[index])) {
203+
continue;
204+
}
205+
return false;
206+
}
207+
}
197208
}
198209
if(vstr != other.vstr) {
199210
return false;

include/CLI/impl/App_inl.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1951,7 +1951,7 @@ App::_parse_arg(std::vector<std::string> &args, detail::Classifier current_type,
19511951

19521952
// now check for '.' notation of subcommands
19531953
auto dotloc = arg_name.find_first_of('.', 1);
1954-
if(dotloc != std::string::npos) {
1954+
if(dotloc != std::string::npos && dotloc < arg_name.size() - 1) {
19551955
// using dot notation is equivalent to single argument subcommand
19561956
auto *sub = _find_subcommand(arg_name.substr(0, dotloc), true, false);
19571957
if(sub != nullptr) {
@@ -1972,7 +1972,12 @@ App::_parse_arg(std::vector<std::string> &args, detail::Classifier current_type,
19721972
args.push_back(nval);
19731973
current_type = detail::Classifier::SHORT;
19741974
}
1975-
auto val = sub->_parse_arg(args, current_type, true);
1975+
std::string dummy1, dummy2;
1976+
bool val = false;
1977+
if(current_type == detail::Classifier::SHORT || detail::split_long(args.back(), dummy1, dummy2)) {
1978+
val = sub->_parse_arg(args, current_type, true);
1979+
}
1980+
19761981
if(val) {
19771982
if(!sub->silent_) {
19781983
parsed_subcommands_.push_back(sub);

tests/FuzzFailTest.cpp

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,14 +306,13 @@ TEST_CASE("fuzz_config_modifier_test1") {
306306
CHECK(opt3->get_multi_option_policy() == CLI::MultiOptionPolicy::Sum);
307307
}
308308

309-
// this test uses the same tests as above just with a full roundtrip test
309+
// this test enables the custom option creation operation
310310
TEST_CASE("app_roundtrip_custom") {
311311
CLI::FuzzApp fuzzdata;
312312
CLI::FuzzApp fuzzdata2;
313313
auto app = fuzzdata.generateApp();
314314
auto app2 = fuzzdata2.generateApp();
315-
int index = GENERATE(range(1, 4));
316-
std::string optionString, flagString;
315+
int index = GENERATE(range(1, 5));
317316
auto parseData = loadFailureFile("round_trip_custom", index);
318317
std::size_t pstring_start{0};
319318
pstring_start = fuzzdata.add_custom_options(app.get(), parseData);
@@ -335,3 +334,43 @@ TEST_CASE("app_roundtrip_custom") {
335334
auto result = fuzzdata2.compare(fuzzdata);
336335
CHECK(result);
337336
}
337+
338+
// this test
339+
TEST_CASE("app_roundtrip_parse_normal_fail") {
340+
// this is mostly checking that no unexpected errors occur
341+
// like HorribleErrors
342+
CLI::FuzzApp fuzzdata;
343+
auto app = fuzzdata.generateApp();
344+
int index = GENERATE(range(1, 3));
345+
std::string optionString, flagString;
346+
auto parseData = loadFailureFile("parse_fail_check", index);
347+
std::size_t pstring_start{0};
348+
pstring_start = fuzzdata.add_custom_options(app.get(), parseData);
349+
350+
try {
351+
if(pstring_start > 0) {
352+
app->parse(parseData.substr(pstring_start));
353+
} else {
354+
app->parse(parseData);
355+
}
356+
} catch(const CLI::HorribleError & /*he*/) {
357+
CHECK(false);
358+
return;
359+
} catch(const CLI::ParseError & /*e*/) {
360+
CHECK(true);
361+
return;
362+
}
363+
try {
364+
// should be able to write the config to a file and read from it again
365+
std::string configOut = app->config_to_str();
366+
app->clear();
367+
std::stringstream out(configOut);
368+
app->parse_from_stream(out);
369+
} catch(const CLI::HorribleError & /*he*/) {
370+
CHECK(false);
371+
return;
372+
} catch(const CLI::ParseError & /*e*/) {
373+
CHECK(false);
374+
return;
375+
}
376+
}

tests/fuzzFail/parse_fail_check1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
'--sub1.!-vA '

tests/fuzzFail/parse_fail_check2

42 Bytes
Binary file not shown.

tests/fuzzFail/round_trip_custom4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--vC=-vA --vopt1=nan

0 commit comments

Comments
 (0)