Fix overflow check

This commit is contained in:
Victor Zverovich 2017-11-12 06:58:11 -08:00
parent 686ff942af
commit 18a0b94b0e
3 changed files with 47 additions and 16 deletions

View File

@ -2040,26 +2040,26 @@ constexpr bool is_name_start(Char c) {
// first character is a digit and presence of a non-digit character at the end. // first character is a digit and presence of a non-digit character at the end.
// it: an iterator pointing to the beginning of the input range. // it: an iterator pointing to the beginning of the input range.
template <typename Iterator, typename ErrorHandler> template <typename Iterator, typename ErrorHandler>
constexpr unsigned parse_nonnegative_int(Iterator &it, ErrorHandler& handler) { constexpr unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) {
assert('0' <= *it && *it <= '9'); assert('0' <= *it && *it <= '9');
unsigned value = 0; unsigned value = 0;
// Convert to unsigned to prevent a warning.
unsigned max_int = (std::numeric_limits<int>::max)();
unsigned big = max_int / 10;
do { do {
unsigned new_value = value * 10 + (*it - '0'); // Check for overflow.
if (value > big) {
value = max_int + 1;
break;
}
value = value * 10 + (*it - '0');
// Workaround for MSVC "setup_exception stack overflow" error: // Workaround for MSVC "setup_exception stack overflow" error:
auto next = it; auto next = it;
++next; ++next;
it = next; it = next;
// Check if value wrapped around.
if (new_value < value) {
value = (std::numeric_limits<unsigned>::max)();
break;
}
value = new_value;
} while ('0' <= *it && *it <= '9'); } while ('0' <= *it && *it <= '9');
// Convert to unsigned to prevent a warning.
unsigned max_int = (std::numeric_limits<int>::max)();
if (value > max_int) if (value > max_int)
handler.on_error("number is too big"); eh.on_error("number is too big");
return value; return value;
} }
@ -2145,6 +2145,8 @@ class specs_setter : public error_handler {
explicit constexpr specs_setter(basic_format_specs<Char> &specs): explicit constexpr specs_setter(basic_format_specs<Char> &specs):
specs_(specs) {} specs_(specs) {}
constexpr specs_setter(const specs_setter &other) : specs_(other.specs_) {}
constexpr void on_align(alignment align) { specs_.align_ = align; } constexpr void on_align(alignment align) { specs_.align_ = align; }
constexpr void on_fill(Char fill) { specs_.fill_ = fill; } constexpr void on_fill(Char fill) { specs_.fill_ = fill; }
constexpr void on_plus() { specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; } constexpr void on_plus() { specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; }
@ -2177,6 +2179,9 @@ class specs_checker : public Handler {
constexpr specs_checker(const Handler& handler, internal::type arg_type) constexpr specs_checker(const Handler& handler, internal::type arg_type)
: Handler(handler), arg_type_(arg_type) {} : Handler(handler), arg_type_(arg_type) {}
constexpr specs_checker(const specs_checker &other)
: Handler(other), arg_type_(other.arg_type_) {}
constexpr void on_align(alignment align) { constexpr void on_align(alignment align) {
if (align == ALIGN_NUMERIC) if (align == ALIGN_NUMERIC)
require_numeric_argument('='); require_numeric_argument('=');
@ -2330,6 +2335,9 @@ class dynamic_specs_handler :
dynamic_format_specs<char_type> &specs, ParseContext &ctx) dynamic_format_specs<char_type> &specs, ParseContext &ctx)
: specs_setter<char_type>(specs), specs_(specs), context_(ctx) {} : specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
constexpr dynamic_specs_handler(const dynamic_specs_handler &other)
: specs_setter(other), specs_(other.specs_), context_(other.context_) {}
template <typename Id> template <typename Id>
constexpr void on_dynamic_width(Id arg_id) { constexpr void on_dynamic_width(Id arg_id) {
specs_.width_ref = make_arg_ref(arg_id); specs_.width_ref = make_arg_ref(arg_id);
@ -2636,8 +2644,7 @@ class format_string_checker : public ErrorHandler {
template <typename Char, typename ErrorHandler, typename... Args> template <typename Char, typename ErrorHandler, typename... Args>
constexpr bool check_format_string( constexpr bool check_format_string(
basic_string_view<Char> s, ErrorHandler eh = ErrorHandler()) { basic_string_view<Char> s, ErrorHandler eh = ErrorHandler()) {
format_string_checker<Char, ErrorHandler, Args...> format_string_checker<Char, ErrorHandler, Args...> checker(eh, s.end());
checker(std::move(eh), s.end());
parse_format_string(s.begin(), checker); parse_format_string(s.begin(), checker);
return true; return true;
} }

View File

@ -1821,7 +1821,15 @@ TEST(FormatTest, UdlTemplate) {
struct test_error_handler { struct test_error_handler {
const char *&error; const char *&error;
constexpr void on_error(const char *message) { error = message; } constexpr test_error_handler(const char *&err): error(err) {}
constexpr test_error_handler(const test_error_handler &other)
: error(other.error) {}
constexpr void on_error(const char *message) {
if (!error)
error = message;
}
}; };
constexpr size_t len(const char *s) { constexpr size_t len(const char *s) {
@ -1831,7 +1839,7 @@ constexpr size_t len(const char *s) {
return len; return len;
} }
constexpr bool eq(const char *s1, const char *s2) { constexpr bool equal(const char *s1, const char *s2) {
if (!s1 && !s2) if (!s1 && !s2)
return true; return true;
while (*s1 && *s1 == *s2) { while (*s1 && *s1 == *s2) {
@ -1848,7 +1856,7 @@ constexpr bool test_error(const char *fmt, const char *expected_error) {
using ref = std::reference_wrapper<test_error_handler>; using ref = std::reference_wrapper<test_error_handler>;
fmt::internal::check_format_string<char, test_error_handler, Args...>( fmt::internal::check_format_string<char, test_error_handler, Args...>(
string_view(fmt, len(fmt)), eh); string_view(fmt, len(fmt)), eh);
return eq(actual_error, expected_error); return equal(actual_error, expected_error);
} }
#define EXPECT_ERROR(fmt, error, ...) \ #define EXPECT_ERROR(fmt, error, ...) \
@ -1860,4 +1868,5 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{0:s", "unknown format specifier", Date); EXPECT_ERROR("{0:s", "unknown format specifier", Date);
EXPECT_ERROR("{0:=5", "unknown format specifier", char); EXPECT_ERROR("{0:=5", "unknown format specifier", char);
EXPECT_ERROR("{foo", "missing '}' in format string", int); EXPECT_ERROR("{foo", "missing '}' in format string", int);
EXPECT_ERROR("{10000000000}", "number is too big");
} }

View File

@ -838,3 +838,18 @@ TEST(UtilTest, IsEnumConvertibleToInt) {
EXPECT_TRUE(fmt::internal::convert_to_int<TestEnum>::enable_conversion); EXPECT_TRUE(fmt::internal::convert_to_int<TestEnum>::enable_conversion);
} }
#endif #endif
TEST(UtilTest, ParseNonnegativeInt) {
if (std::numeric_limits<int>::max() != (1 << 31)) {
fmt::print("Skipping parse_nonnegative_int test\n");
return;
}
const char *s = "10000000000";
EXPECT_THROW_MSG(
parse_nonnegative_int(s, fmt::internal::error_handler()),
fmt::format_error, "number is too big");
s = "2147483649";
EXPECT_THROW_MSG(
parse_nonnegative_int(s, fmt::internal::error_handler()),
fmt::format_error, "number is too big");
}