1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-05-03 05:53:52 +00:00

fix: out of range string detected by the fuzzer (#905)

about an out of range string conversion not being caught properly. This
commit changes the logic from an exception to using errno and a
non-throwing alternative.

Issue detected in

https://github.com/CLIUtils/CLI11/actions/runs/5500247554/jobs/10023032108

The problem string was set up as a test.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Philip Top 2023-07-11 05:46:41 -07:00 committed by GitHub
parent ccf141c49d
commit 792d892867
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 34 deletions

View File

@ -861,7 +861,7 @@ bool integral_conversion(const std::string &input, T &output) noexcept {
if(input.empty() || input.front() == '-') {
return false;
}
char *val = nullptr;
char *val{nullptr};
errno = 0;
std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0);
if(errno == ERANGE) {
@ -904,8 +904,8 @@ bool integral_conversion(const std::string &input, T &output) noexcept {
return false;
}
/// Convert a flag into an integer value typically binary flags
inline std::int64_t to_flag_value(std::string val) {
/// Convert a flag into an integer value typically binary flags sets errno to nonzero if conversion failed
inline std::int64_t to_flag_value(std::string val) noexcept {
static const std::string trueString("true");
static const std::string falseString("false");
if(val == trueString) {
@ -933,7 +933,8 @@ inline std::int64_t to_flag_value(std::string val) {
ret = 1;
break;
default:
throw std::invalid_argument("unrecognized character");
errno = EINVAL;
return -1;
}
return ret;
}
@ -942,7 +943,11 @@ inline std::int64_t to_flag_value(std::string val) {
} else if(val == falseString || val == "off" || val == "no" || val == "disable") {
ret = -1;
} else {
ret = std::stoll(val);
char *loc_ptr{nullptr};
ret = std::strtoll(val.c_str(), &loc_ptr, 0);
if(loc_ptr != (val.c_str() + val.size()) && errno == 0) {
errno = EINVAL;
}
}
return ret;
}
@ -971,18 +976,16 @@ bool lexical_cast(const std::string &input, T &output) {
template <typename T,
enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
bool lexical_cast(const std::string &input, T &output) {
try {
auto out = to_flag_value(input);
errno = 0;
auto out = to_flag_value(input);
if(errno == 0) {
output = (out > 0);
return true;
} catch(const std::invalid_argument &) {
return false;
} catch(const std::out_of_range &) {
// if the number is out of the range of a 64 bit value then it is still a number and for this purpose is still
// valid all we care about the sign
} else if(errno == ERANGE) {
output = (input[0] != '-');
return true;
} else {
return false;
}
return true;
}
/// Floats
@ -1638,12 +1641,13 @@ inline std::string sum_string_vector(const std::vector<std::string> &values) {
double tv{0.0};
auto comp = lexical_cast(arg, tv);
if(!comp) {
try {
tv = static_cast<double>(detail::to_flag_value(arg));
} catch(const std::exception &) {
fail = true;
errno = 0;
auto fv = detail::to_flag_value(arg);
fail = (errno != 0);
if(fail) {
break;
}
tv = static_cast<double>(fv);
}
val += tv;
}

View File

@ -1431,18 +1431,15 @@ CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t
auto res = config_formatter_->to_flag(item);
bool converted{false};
if(op->get_disable_flag_override()) {
try {
auto val = detail::to_flag_value(res);
if(val == 1) {
res = op->get_flag_value(item.name, "{}");
converted = true;
}
} catch(...) {
auto val = detail::to_flag_value(res);
if(val == 1) {
res = op->get_flag_value(item.name, "{}");
converted = true;
}
}
if(!converted) {
errno = 0;
res = op->get_flag_value(item.name, res);
}

View File

@ -389,15 +389,15 @@ CLI11_NODISCARD CLI11_INLINE std::string Option::get_flag_value(const std::strin
return input_value;
}
if(default_flag_values_[static_cast<std::size_t>(ind)].second == falseString) {
try {
auto val = detail::to_flag_value(input_value);
return (val == 1) ? falseString : (val == (-1) ? trueString : std::to_string(-val));
} catch(const std::invalid_argument &) {
errno = 0;
auto val = detail::to_flag_value(input_value);
if(errno != 0) {
errno = 0;
return input_value;
}
} else {
return input_value;
return (val == 1) ? falseString : (val == (-1) ? trueString : std::to_string(-val));
}
return input_value;
}
CLI11_INLINE Option *Option::add_result(std::string s) {

View File

@ -45,3 +45,16 @@ TEST_CASE("app_fail") {
} catch(const CLI::ConstructionError & /*e*/) {
}
}
TEST_CASE("file_fail") {
CLI::FuzzApp fuzzdata;
auto app = fuzzdata.generateApp();
int index = GENERATE(range(1, 2));
auto parseData = loadFailureFile("fuzz_file_fail", index);
std::stringstream out(parseData);
try {
app->parse_from_stream(out);
} catch(const CLI::ParseError & /*e*/) {
}
}

View File

@ -201,15 +201,26 @@ TEST_CASE("StringTools: Modify3", "[helpers]") {
}
TEST_CASE("StringTools: flagValues", "[helpers]") {
errno = 0;
CHECK(-1 == CLI::detail::to_flag_value("0"));
CHECK(errno == 0);
CHECK(1 == CLI::detail::to_flag_value("t"));
CHECK(1 == CLI::detail::to_flag_value("1"));
CHECK(6 == CLI::detail::to_flag_value("6"));
CHECK(-6 == CLI::detail::to_flag_value("-6"));
CHECK(-1 == CLI::detail::to_flag_value("false"));
CHECK(1 == CLI::detail::to_flag_value("YES"));
CHECK_THROWS_AS(CLI::detail::to_flag_value("frog"), std::invalid_argument);
CHECK_THROWS_AS(CLI::detail::to_flag_value("q"), std::invalid_argument);
errno = 0;
CLI::detail::to_flag_value("frog");
CHECK(errno == EINVAL);
errno = 0;
CLI::detail::to_flag_value("q");
CHECK(errno == EINVAL);
errno = 0;
CLI::detail::to_flag_value(
"77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777");
CHECK(errno == ERANGE);
errno = 0;
CHECK(-1 == CLI::detail::to_flag_value("NO"));
CHECK(475555233 == CLI::detail::to_flag_value("475555233"));
}

View File

@ -0,0 +1 @@
nflag2=555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555"="