Hello, This patch fixes bug 4485 and adjusts related test cases to fully check Parser::Tokenizer::int64() post-conditions.
Regards, Eduard.
Fixed bug 4485: off-by-one out-of-bounds Parser::Tokenizer::int64() read errors. Also adjusted related test cases to fully check Parser::Tokenizer::int64() post-conditions. === modified file 'src/parser/Tokenizer.cc' --- src/parser/Tokenizer.cc 2016-01-01 00:12:18 +0000 +++ src/parser/Tokenizer.cc 2016-05-21 12:00:33 +0000 @@ -200,85 +200,86 @@ debugs(24, 8, "no match when trying to skip " << skippable.name); return 0; } debugs(24, 8, "skipping in " << skippable.name << " len " << suffixLen); return successTrailing(suffixLen); } /* reworked from compat/strtoll.c */ bool Parser::Tokenizer::int64(int64_t & result, int base, bool allowSign, const SBuf::size_type limit) { if (atEnd() || limit == 0) return false; const SBuf range(buf_.substr(0,limit)); //fixme: account for buf_.size() bool neg = false; const char *s = range.rawContent(); const char *end = range.rawContent() + range.length(); if (allowSign) { if (*s == '-') { neg = true; ++s; } else if (*s == '+') { ++s; } if (s >= end) return false; } - if (( base == 0 || base == 16) && *s == '0' && (s+1 <= end ) && + if (( base == 0 || base == 16) && *s == '0' && (s+1 < end ) && tolower(*(s+1)) == 'x') { s += 2; base = 16; } if (base == 0) { if ( *s == '0') { base = 8; ++s; } else { base = 10; } } if (s >= end) return false; uint64_t cutoff; cutoff = neg ? -static_cast<uint64_t>(INT64_MIN) : INT64_MAX; const int cutlim = cutoff % static_cast<int64_t>(base); cutoff /= static_cast<uint64_t>(base); int any = 0, c; int64_t acc = 0; - for (c = *s++; s <= end; c = *s++) { + do { + c = *s; if (xisdigit(c)) { c -= '0'; } else if (xisalpha(c)) { c -= xisupper(c) ? 'A' - 10 : 'a' - 10; } else { break; } if (c >= base) break; if (any < 0 || static_cast<uint64_t>(acc) > cutoff || (static_cast<uint64_t>(acc) == cutoff && c > cutlim)) any = -1; else { any = 1; acc *= base; acc += c; } - } + } while (++s < end); if (any == 0) // nothing was parsed return false; if (any < 0) { acc = neg ? INT64_MIN : INT64_MAX; errno = ERANGE; return false; } else if (neg) acc = -acc; result = acc; - return success(s - range.rawContent() - 1); + return success(s - range.rawContent()); } === modified file 'src/tests/testTokenizer.cc' --- src/tests/testTokenizer.cc 2016-01-01 00:12:18 +0000 +++ src/tests/testTokenizer.cc 2016-05-21 20:59:43 +0000 @@ -177,134 +177,142 @@ // match until the end of the sample CPPUNIT_ASSERT(t.suffix(s, all)); CPPUNIT_ASSERT_EQUAL(SBuf(), t.remaining()); // an empty buffer does not end with a token s = canary; CPPUNIT_ASSERT(!t.suffix(s, all)); CPPUNIT_ASSERT_EQUAL(canary, s); // no parameter changes // we cannot skip an empty suffix, even in an empty buffer CPPUNIT_ASSERT(!t.skipSuffix(SBuf())); } void testTokenizer::testCharacterSet() { } void testTokenizer::testTokenizerInt64() { // successful parse in base 10 { int64_t rv; Parser::Tokenizer t(SBuf("1234")); const int64_t benchmark = 1234; CPPUNIT_ASSERT(t.int64(rv, 10)); CPPUNIT_ASSERT_EQUAL(benchmark,rv); + CPPUNIT_ASSERT(t.buf().isEmpty()); } // successful parse, autodetect base { int64_t rv; Parser::Tokenizer t(SBuf("1234")); const int64_t benchmark = 1234; CPPUNIT_ASSERT(t.int64(rv)); CPPUNIT_ASSERT_EQUAL(benchmark,rv); + CPPUNIT_ASSERT(t.buf().isEmpty()); } // successful parse, autodetect base { int64_t rv; Parser::Tokenizer t(SBuf("01234")); const int64_t benchmark = 01234; CPPUNIT_ASSERT(t.int64(rv)); CPPUNIT_ASSERT_EQUAL(benchmark,rv); + CPPUNIT_ASSERT(t.buf().isEmpty()); } // successful parse, autodetect base { int64_t rv; Parser::Tokenizer t(SBuf("0x12f4")); const int64_t benchmark = 0x12f4; CPPUNIT_ASSERT(t.int64(rv)); CPPUNIT_ASSERT_EQUAL(benchmark,rv); + CPPUNIT_ASSERT(t.buf().isEmpty()); } // API mismatch: don't eat leading space { int64_t rv; Parser::Tokenizer t(SBuf(" 1234")); CPPUNIT_ASSERT(!t.int64(rv)); + CPPUNIT_ASSERT_EQUAL(SBuf(" 1234"), t.buf()); } // API mismatch: don't eat multiple leading spaces { int64_t rv; Parser::Tokenizer t(SBuf(" 1234")); CPPUNIT_ASSERT(!t.int64(rv)); + CPPUNIT_ASSERT_EQUAL(SBuf(" 1234"), t.buf()); } // trailing spaces { int64_t rv; Parser::Tokenizer t(SBuf("1234 foo")); const int64_t benchmark = 1234; CPPUNIT_ASSERT(t.int64(rv)); CPPUNIT_ASSERT_EQUAL(benchmark,rv); CPPUNIT_ASSERT_EQUAL(SBuf(" foo"), t.buf()); } // trailing nonspaces { int64_t rv; Parser::Tokenizer t(SBuf("1234foo")); const int64_t benchmark = 1234; CPPUNIT_ASSERT(t.int64(rv)); CPPUNIT_ASSERT_EQUAL(benchmark,rv); CPPUNIT_ASSERT_EQUAL(SBuf("foo"), t.buf()); } // trailing nonspaces { int64_t rv; Parser::Tokenizer t(SBuf("0x1234foo")); const int64_t benchmark = 0x1234f; CPPUNIT_ASSERT(t.int64(rv)); CPPUNIT_ASSERT_EQUAL(benchmark,rv); CPPUNIT_ASSERT_EQUAL(SBuf("oo"), t.buf()); } // overflow { int64_t rv; Parser::Tokenizer t(SBuf("1029397752385698678762234")); CPPUNIT_ASSERT(!t.int64(rv)); + CPPUNIT_ASSERT_EQUAL(SBuf("1029397752385698678762234"), t.buf()); } // buffered sub-string parsing { int64_t rv; SBuf base("1029397752385698678762234"); const int64_t benchmark = 22; Parser::Tokenizer t(base.substr(base.length()-4,2)); CPPUNIT_ASSERT_EQUAL(SBuf("22"),t.buf()); CPPUNIT_ASSERT(t.int64(rv)); CPPUNIT_ASSERT_EQUAL(benchmark,rv); + CPPUNIT_ASSERT(t.buf().isEmpty()); } // base-16, prefix { int64_t rv; SBuf base("deadbeefrow"); const int64_t benchmark=0xdeadbeef; Parser::Tokenizer t(base); CPPUNIT_ASSERT(t.int64(rv,16)); CPPUNIT_ASSERT_EQUAL(benchmark,rv); CPPUNIT_ASSERT_EQUAL(SBuf("row"),t.buf()); } }
_______________________________________________ squid-dev mailing list squid-dev@lists.squid-cache.org http://lists.squid-cache.org/listinfo/squid-dev