Skip to content

Commit

Permalink
Fix bugs in integer parsing (#1041)
Browse files Browse the repository at this point in the history
* `ParseHexdigit` allowed `g` as a hex character

* Overflow checking can't just check `old_value > new_value`, that fails
  for many cases.
  • Loading branch information
binji authored Mar 15, 2019
1 parent 8ab4730 commit 08629b8
Show file tree
Hide file tree
Showing 2 changed files with 241 additions and 12 deletions.
18 changes: 10 additions & 8 deletions src/literal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -534,10 +534,10 @@ Result ParseHexdigit(char c, uint32_t* out) {
if (static_cast<unsigned int>(c - '0') <= 9) {
*out = c - '0';
return Result::Ok;
} else if (static_cast<unsigned int>(c - 'a') <= 6) {
} else if (static_cast<unsigned int>(c - 'a') < 6) {
*out = 10 + (c - 'a');
return Result::Ok;
} else if (static_cast<unsigned int>(c - 'A') <= 6) {
} else if (static_cast<unsigned int>(c - 'A') < 6) {
*out = 10 + (c - 'A');
return Result::Ok;
}
Expand All @@ -554,20 +554,23 @@ Result ParseUint64(const char* s, const char* end, uint64_t* out) {
if (s == end) {
return Result::Error;
}
constexpr uint64_t kMaxDiv16 = UINT64_MAX / 16;
constexpr uint64_t kMaxMod16 = UINT64_MAX % 16;
for (; s < end; ++s) {
uint32_t digit;
if (*s == '_') {
continue;
}
CHECK_RESULT(ParseHexdigit(*s, &digit));
uint64_t old_value = value;
value = value * 16 + digit;
// Check for overflow.
if (old_value > value) {
if (value > kMaxDiv16 || (value == kMaxDiv16 && digit > kMaxMod16)) {
return Result::Error;
}
value = value * 16 + digit;
}
} else {
constexpr uint64_t kMaxDiv10 = UINT64_MAX / 10;
constexpr uint64_t kMaxMod10 = UINT64_MAX % 10;
for (; s < end; ++s) {
if (*s == '_') {
continue;
Expand All @@ -576,12 +579,11 @@ Result ParseUint64(const char* s, const char* end, uint64_t* out) {
if (digit > 9) {
return Result::Error;
}
uint64_t old_value = value;
value = value * 10 + digit;
// Check for overflow.
if (old_value > value) {
if (value > kMaxDiv10 || (value == kMaxDiv10 && digit > kMaxMod10)) {
return Result::Error;
}
value = value * 10 + digit;
}
}
if (s != end) {
Expand Down
235 changes: 231 additions & 4 deletions src/test-literal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,261 @@ using namespace wabt;

namespace {

enum ParseIntTypeCombo {
UnsignedOnly,
SignedAndUnsigned,
Both,
};

template <typename T, typename F>
void AssertIntEquals(T expected,
const char* s,
F&& parse_int,
ParseIntTypeCombo parse_type = Both) {
const char* const end = s + strlen(s);
T actual;
if (parse_type == UnsignedOnly || parse_type == Both) {
ASSERT_EQ(Result::Ok,
parse_int(s, end, &actual, ParseIntType::UnsignedOnly))
<< s;
ASSERT_EQ(expected, actual);
} else {
ASSERT_EQ(Result::Error,
parse_int(s, end, &actual, ParseIntType::UnsignedOnly))
<< s;
}

if (parse_type == SignedAndUnsigned || parse_type == Both) {
ASSERT_EQ(Result::Ok,
parse_int(s, end, &actual, ParseIntType::SignedAndUnsigned))
<< s;
ASSERT_EQ(expected, actual);
} else {
ASSERT_EQ(Result::Error,
parse_int(s, end, &actual, ParseIntType::SignedAndUnsigned))
<< s;
}
}

void AssertInt32Equals(uint32_t expected,
const char* s,
ParseIntTypeCombo parse_type = Both) {
AssertIntEquals(expected, s, ParseInt32, parse_type);
}

void AssertInt64Equals(uint64_t expected,
const char* s,
ParseIntTypeCombo parse_type = Both) {
AssertIntEquals(expected, s, ParseInt64, parse_type);
}

void AssertInt32Fails(const char* s) {
const char* const end = s + strlen(s);
uint32_t actual;
ASSERT_EQ(Result::Error,
ParseInt32(s, end, &actual, ParseIntType::SignedAndUnsigned))
<< s;
ASSERT_EQ(Result::Error,
ParseInt32(s, end, &actual, ParseIntType::UnsignedOnly))
<< s;
}

void AssertInt64Fails(const char* s) {
const char* const end = s + strlen(s);
uint64_t actual;
ASSERT_EQ(Result::Error,
ParseInt64(s, end, &actual, ParseIntType::SignedAndUnsigned))
<< s;
ASSERT_EQ(Result::Error,
ParseInt64(s, end, &actual, ParseIntType::UnsignedOnly))
<< s;
}

void AssertUint64Equals(uint64_t expected, const char* s) {
uint64_t actual;
ASSERT_EQ(Result::Ok, ParseUint64(s, s + strlen(s), &actual)) << s;
ASSERT_EQ(expected, actual);
}

void AssertUint64Fails(const char* s) {
uint64_t actual_bits;
ASSERT_EQ(Result::Error, ParseUint64(s, s + strlen(s), &actual_bits)) << s;
}

void AssertHexFloatEquals(uint32_t expected_bits, const char* s) {
uint32_t actual_bits;
ASSERT_EQ(Result::Ok,
ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits));
ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
<< s;
ASSERT_EQ(expected_bits, actual_bits);
}

void AssertHexFloatFails(const char* s) {
uint32_t actual_bits;
ASSERT_EQ(Result::Error,
ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits));
ParseFloat(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
<< s;
}

void AssertHexDoubleEquals(uint64_t expected_bits, const char* s) {
uint64_t actual_bits;
ASSERT_EQ(Result::Ok,
ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits));
ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
<< s;
ASSERT_EQ(expected_bits, actual_bits);
}

void AssertHexDoubleFails(const char* s) {
uint64_t actual_bits;
ASSERT_EQ(Result::Error,
ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits));
ParseDouble(LiteralType::Hexfloat, s, s + strlen(s), &actual_bits))
<< s;
}

} // end anonymous namespace

TEST(ParseInt32, Both) {
AssertInt32Equals(0, "0");
AssertInt32Equals(1000, "1000");
AssertInt32Equals(123456789, "123456789");
AssertInt32Equals(2147483647, "2147483647");
AssertInt32Equals(4294967295u, "4294967295");
AssertInt32Equals(0xcafef00du, "0xcafef00d");
AssertInt32Equals(0x7fffffff, "0x7fffffff");
AssertInt32Equals(0x80000000u, "0x80000000");
AssertInt32Equals(0xffffffffu, "0xffffffff");
}

TEST(ParseInt32, SignedAndUnsigned) {
AssertInt32Equals(2147483648, "-2147483648", SignedAndUnsigned);
AssertInt32Equals(-0x80000000u, "-0x80000000", SignedAndUnsigned);
AssertInt32Equals(4294967295u, "-1", SignedAndUnsigned);
AssertInt32Equals(-1, "-0x1", SignedAndUnsigned);
AssertInt32Equals(1, "+1", SignedAndUnsigned);
AssertInt32Equals(-0xabcd, "-0xABCD", SignedAndUnsigned);
AssertInt32Equals(0xabcd, "+0xabcd", SignedAndUnsigned);
}

TEST(ParseInt32, Invalid) {
AssertInt32Fails("");
AssertInt32Fails("-100hello");
AssertInt32Fails("0XABCDEF");
AssertInt32Fails("0xgabba");
AssertInt32Fails("two");
}

TEST(ParseInt32, Underscores) {
AssertInt32Equals(123456789, "12_345_6789", Both);
AssertInt32Equals(123456789, "+12_345_6789", SignedAndUnsigned);
AssertInt32Equals(-123456789, "-12345_6789", SignedAndUnsigned);
AssertInt32Equals(19, "1______9", Both);
AssertInt32Equals(0xabcd, "0xa_b_c_d", Both);
AssertInt32Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned);
AssertInt32Equals(-0xabcd, "-0xa_b_c_d", SignedAndUnsigned);
}

TEST(ParseInt32, Overflow) {
AssertInt32Fails("4294967296");
AssertInt32Fails("-2147483649");
AssertInt32Fails("0x100000000");
AssertInt32Fails("-0x80000001");
AssertInt32Fails("1231231231231231231231");
}

TEST(ParseInt64, Both) {
AssertInt64Equals(0, "0");
AssertInt64Equals(1000, "1000");
AssertInt64Equals(123456789, "123456789");
AssertInt64Equals(9223372036854775807ull, "9223372036854775807");
AssertInt64Equals(18446744073709551615ull, "18446744073709551615");
AssertInt64Equals(0x7fffffffffffffffull, "0x7fffffffffffffff");
AssertInt64Equals(0x8000000000000000ull, "0x8000000000000000");
AssertInt64Equals(0xffffffffffffffffull, "0xffffffffffffffff");
}

TEST(ParseInt64, SignedAndUnsigned) {
AssertInt64Equals(9223372036854775808ull, "-9223372036854775808",
SignedAndUnsigned);
AssertInt64Equals(18446744073709551615ull, "-1", SignedAndUnsigned);
AssertInt64Equals(-1, "-0x1", SignedAndUnsigned);
AssertInt64Equals(1, "+1", SignedAndUnsigned);
AssertInt64Equals(-0x0bcdefabcdefabcdull, "-0x0BCDEFABCDEFABCD",
SignedAndUnsigned);
AssertInt64Equals(0xabcdefabcdefabcdull, "+0xabcdefabcdefabcd",
SignedAndUnsigned);
}

TEST(ParseInt64, Invalid) {
AssertInt64Fails("");
AssertInt64Fails("-100hello");
AssertInt64Fails("0XABCDEF");
AssertInt64Fails("0xgabba");
AssertInt64Fails("two");
}

TEST(ParseInt64, Underscores) {
AssertInt64Equals(123456789, "12_345_6789", Both);
AssertInt64Equals(123456789, "+12_345_6789", SignedAndUnsigned);
AssertInt64Equals(-123456789, "-12345_6789", SignedAndUnsigned);
AssertInt64Equals(19, "1______9", Both);
AssertInt64Equals(0xabcd, "0xa_b_c_d", Both);
AssertInt64Equals(0xabcd, "+0xa_b_c_d", SignedAndUnsigned);
AssertInt64Equals(-0xabcd, "-0xa_b_c_d", SignedAndUnsigned);
}

TEST(ParseInt64, Overflow) {
AssertInt64Fails("18446744073709551616");
AssertInt64Fails("-9223372036854775809");
AssertInt32Fails("0x10000000000000000");
AssertInt32Fails("-0x80000000000000001");
AssertInt64Fails("1231231231231231231231");
}

TEST(ParseUint64, Basic) {
AssertUint64Equals(0, "0");
AssertUint64Equals(1000, "1000");
AssertUint64Equals(123456789, "123456789");
AssertUint64Equals(1844674407370955161ull, "1844674407370955161");
AssertUint64Equals(18446744073709551615ull, "18446744073709551615");

AssertUint64Equals(0, "0x0");
AssertUint64Equals(0x1000, "0x1000");
AssertUint64Equals(0x123456789, "0x123456789");
AssertUint64Equals(0xabcdef, "0xabcdef");
AssertUint64Equals(0xffffffffffffffull, "0xffffffffffffff");
AssertUint64Equals(0xfffffffffffffffull, "0xfffffffffffffff");

AssertUint64Equals(0xabcdefabcdefabcdull, "0xabcdefabcdefabcd");
}

TEST(ParseUint64, NoOctal) {
AssertUint64Equals(100, "0100");
AssertUint64Equals(888, "0000888");
}

TEST(ParseUint64, Invalid) {
AssertUint64Fails("");
AssertUint64Fails("-100");
AssertUint64Fails("0XABCDEF");
AssertUint64Fails("0xgabba");
AssertUint64Fails("two");
}

TEST(ParseUint64, Underscores) {
AssertUint64Equals(123456789, "12_345_6789");
AssertUint64Equals(19, "1______9");
AssertUint64Equals(0xabcd, "0xa_b_c_d");
}

TEST(ParseUint64, Overflow) {
AssertUint64Fails("0x10000000000000000");
AssertUint64Fails("18446744073709551616");
AssertUint64Fails("62857453058642199420");
AssertUint64Fails("82000999361882825820");
AssertUint64Fails("126539114687237086210");
AssertUint64Fails("10000000000000000000000000000000000000000");
}

TEST(ParseFloat, NonCanonical) {
AssertHexFloatEquals(0x3f800000, "0x00000000000000000000001.0p0");
AssertHexFloatEquals(0x3f800000, "0x1.00000000000000000000000p0");
Expand Down

0 comments on commit 08629b8

Please sign in to comment.