Skip to content

Commit

Permalink
Fuzz fail (#1097)
Browse files Browse the repository at this point in the history
fix failing fuzz case involving binary string with a '\x' in it.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
phlptp and pre-commit-ci[bot] authored Nov 30, 2024
1 parent 8260578 commit 063b2c9
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 7 deletions.
2 changes: 1 addition & 1 deletion fuzz/cli11_app_fuzz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {

try {
if(pstring_start > 0) {
app->parse(parseString.substr(pstring_start, std::string::npos));
app->parse(parseString.substr(pstring_start));
} else {
app->parse(parseString);
}
Expand Down
16 changes: 13 additions & 3 deletions include/CLI/impl/Config_inl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -542,8 +542,9 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
continue;
}

std::string value = detail::ini_join(
opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, literalQuote);
auto results = opt->reduced_results();
std::string value =
detail::ini_join(results, arraySeparator, arrayStart, arrayEnd, stringQuote, literalQuote);

bool isDefault = false;
if(value.empty() && default_also) {
Expand All @@ -560,7 +561,16 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
}

if(!value.empty()) {

if(opt->get_expected_max() > 1 && detail::is_binary_escaped_string(value) && results.size() == 1 &&
!results[0].empty()) {
if(results[0].front() == '[' && results[0].back() == ']') {
// this is a condition which could be misinterpreted
results[0].insert(0, 1, results[0].front());
results[0].push_back(results[0].back());
value = detail::ini_join(
results, arraySeparator, arrayStart, arrayEnd, stringQuote, literalQuote);
}
}
if(!opt->get_fnames().empty()) {
try {
value = opt->get_flag_value(single_name, value);
Expand Down
13 changes: 10 additions & 3 deletions include/CLI/impl/Option_inl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,13 +665,20 @@ CLI11_INLINE int Option::_add_result(std::string &&result, std::vector<std::stri
if((allow_extra_args_ || get_expected_max() > 1) && !result.empty() && result.front() == '[' &&
result.back() == ']') { // this is now a vector string likely from the default or user entry
result.pop_back();

for(auto &var : CLI::detail::split_up(result.substr(1), ',')) {
result.erase(result.begin());
bool skipSection{false};
for(auto &var : CLI::detail::split_up(result, ',')) {
if(var == result) {
skipSection = true;
break;
}
if(!var.empty()) {
result_count += _add_result(std::move(var), res);
}
}
return result_count;
if(!skipSection) {
return result_count;
}
}
if(delimiter_ == '\0') {
res.push_back(std::move(result));
Expand Down
7 changes: 7 additions & 0 deletions include/CLI/impl/StringTools_inl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,13 @@ CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escap
stream << std::hex << static_cast<unsigned int>(static_cast<unsigned char>(c));
std::string code = stream.str();
escaped_string += std::string("\\x") + (code.size() < 2 ? "0" : "") + code;
} else if(c == 'x' || c == 'X') {
// need to check for inadvertent binary sequences
if(!escaped_string.empty() && escaped_string.back() == '\\') {
escaped_string += std::string("\\x") + (c == 'x' ? "78" : "58");
} else {
escaped_string.push_back(c);
}

} else {
escaped_string.push_back(c);
Expand Down
30 changes: 30 additions & 0 deletions tests/FuzzFailTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,33 @@ TEST_CASE("fuzz_config_test1") {
CHECK(app->get_option_no_throw("--new_flag") != nullptr);
CHECK(app->get_option_no_throw("--new_vector") != nullptr);
}

// this test uses the same tests as above just with a full roundtrip test
TEST_CASE("app_roundtrip_custom") {
CLI::FuzzApp fuzzdata;
CLI::FuzzApp fuzzdata2;
auto app = fuzzdata.generateApp();
auto app2 = fuzzdata2.generateApp();
int index = GENERATE(range(1, 3));
std::string optionString, flagString;
auto parseData = loadFailureFile("round_trip_custom", index);
std::size_t pstring_start{0};
pstring_start = fuzzdata.add_custom_options(app.get(), parseData);

if(pstring_start > 0) {
app->parse(parseData.substr(pstring_start));
} else {
app->parse(parseData);
}

// should be able to write the config to a file and read from it again
std::string configOut = app->config_to_str();
app->clear();
std::stringstream out(configOut);
if(pstring_start > 0) {
fuzzdata2.add_custom_options(app2.get(), parseData);
}
app2->parse_from_stream(out);
auto result = fuzzdata2.compare(fuzzdata);
CHECK(result);
}
36 changes: 36 additions & 0 deletions tests/HelpersTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,42 @@ TEST_CASE("StringTools: binaryEscapseConversion2", "[helpers]") {
CHECK(rstring == testString);
}

TEST_CASE("StringTools: binaryEscapseConversion_withX", "[helpers]") {
std::string testString("hippy\\x35mm\\XF3_helpX26fox19");
testString.push_back(0);
testString.push_back(0);
testString.push_back(0);
testString.push_back(56);
testString.push_back(-112);
testString.push_back(-112);
testString.push_back(39);
testString.push_back(97);
std::string estring = CLI::detail::binary_escape_string(testString);
CHECK(CLI::detail::is_binary_escaped_string(estring));
std::string rstring = CLI::detail::extract_binary_string(estring);
CHECK(rstring == testString);
}

TEST_CASE("StringTools: binaryEscapseConversion_withBrackets", "[helpers]") {

std::string vstr = R"raw('B"([\xb0\x0a\xb0/\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0\xb0])"')raw";
std::string testString("[");
testString.push_back(-80);
testString.push_back('\n');
testString.push_back(-80);
testString.push_back('/');
for(int ii = 0; ii < 13; ++ii) {
testString.push_back(-80);
}
testString.push_back(']');

std::string estring = CLI::detail::binary_escape_string(testString);
CHECK(CLI::detail::is_binary_escaped_string(estring));
CHECK(estring == vstr);
std::string rstring = CLI::detail::extract_binary_string(estring);
CHECK(rstring == testString);
}

TEST_CASE("StringTools: binaryStrings", "[helpers]") {
std::string rstring = "B\"()\"";
CHECK(CLI::detail::extract_binary_string(rstring).empty());
Expand Down
Binary file added tests/fuzzFail/round_trip_custom1
Binary file not shown.
1 change: 1 addition & 0 deletions tests/fuzzFail/round_trip_custom2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--vM=[��/�������������]
Expand Down

0 comments on commit 063b2c9

Please sign in to comment.