diff options
-rw-r--r-- | src/corelib/io/qipaddress.cpp | 28 | ||||
-rw-r--r-- | src/corelib/io/qstorageinfo_unix.cpp | 16 | ||||
-rw-r--r-- | src/corelib/text/qlocale.cpp | 28 | ||||
-rw-r--r-- | src/corelib/text/qlocale_tools.cpp | 20 | ||||
-rw-r--r-- | src/corelib/text/qlocale_tools_p.h | 5 | ||||
-rw-r--r-- | src/corelib/text/qlocale_win.cpp | 8 | ||||
-rw-r--r-- | src/corelib/text/qstring.cpp | 6 | ||||
-rw-r--r-- | src/corelib/time/qtimezoneprivate_tz.cpp | 12 | ||||
-rw-r--r-- | src/corelib/tools/qhash.cpp | 2 | ||||
-rw-r--r-- | src/corelib/tools/qversionnumber.cpp | 8 | ||||
-rw-r--r-- | tests/auto/corelib/text/qlocale/tst_qlocale.cpp | 14 |
11 files changed, 80 insertions, 67 deletions
diff --git a/src/corelib/io/qipaddress.cpp b/src/corelib/io/qipaddress.cpp index feed38bef6..444a38a319 100644 --- a/src/corelib/io/qipaddress.cpp +++ b/src/corelib/io/qipaddress.cpp @@ -60,12 +60,12 @@ static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptL ptr[1] != '.' && ptr[1] != '\0') return false; - auto [ll, endptr] = qstrntoull(ptr, stop - ptr, 0); + auto [ll, used] = qstrntoull(ptr, stop - ptr, 0); quint32 x = ll; - if (!endptr || endptr == ptr || ll != x) + if (used <= 0 || ll != x) return false; - if (*endptr == '.' || dotCount == 3) { + if (ptr[used] == '.' || dotCount == 3) { if (x & ~0xff) return false; address <<= 8; @@ -80,13 +80,13 @@ static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptL } address |= x; - if (dotCount == 3 || *endptr == '\0') - return *endptr == '\0'; - if (*endptr != '.') + if (dotCount == 3 || ptr[used] == '\0') + return ptr[used] == '\0'; + if (ptr[used] != '.') return false; ++dotCount; - ptr = endptr + 1; + ptr += used + 1; } return false; } @@ -174,16 +174,16 @@ const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end continue; } - auto [ll, endptr] = qstrntoull(ptr, stop - ptr, 16); + auto [ll, used] = qstrntoull(ptr, stop - ptr, 16); quint16 x = ll; // Reject malformed fields: // - failed to parse // - too many hex digits - if (!endptr || endptr > ptr + 4) + if (used <= 0 || used > 4) return begin + (ptr - buffer.data()); - if (*endptr == '.') { + if (ptr[used] == '.') { // this could be an IPv4 address // it's only valid in the last element if (pos != 12) @@ -203,11 +203,11 @@ const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end address[pos++] = x >> 8; address[pos++] = x & 0xff; - if (*endptr == '\0') + if (ptr[used] == '\0') break; - if (*endptr != ':') - return begin + (endptr - buffer.data()); - ptr = endptr + 1; + if (ptr[used] != ':') + return begin + (used + ptr - buffer.data()); + ptr += used + 1; } return pos == 16 ? nullptr : end; } diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp index be3bf0252d..d815708ab0 100644 --- a/src/corelib/io/qstorageinfo_unix.cpp +++ b/src/corelib/io/qstorageinfo_unix.cpp @@ -459,27 +459,31 @@ inline bool QStorageIterator::next() return false; mnt.mount_id = r.result; - r = qstrntoll(r.endptr, stop - r.endptr, 10); + ptr += r.used; + r = qstrntoll(ptr, stop - ptr, 10); if (!r.ok()) return false; int parent_id = r.result; Q_UNUSED(parent_id); - r = qstrntoll(r.endptr, stop - r.endptr, 10); + ptr += r.used; + r = qstrntoll(ptr, stop - ptr, 10); if (!r.ok()) return false; - if (*r.endptr != ':') + ptr += r.used; + if (*ptr != ':') return false; int rdevmajor = r.result; - r = qstrntoll(r.endptr + 1, stop - r.endptr - 1, 10); + ++ptr; // Skip over the ':' + r = qstrntoll(ptr, stop - ptr, 10); if (!r.ok()) return false; mnt.rdev = makedev(rdevmajor, r.result); - if (*r.endptr != ' ') + ptr += r.used; + if (*ptr != ' ') return false; - ptr = const_cast<char *>(r.endptr); mnt.subvolume = ++ptr; ptr = parseMangledPath(ptr); if (!ptr) diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index 97d84eac4e..23534cbb58 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -4145,20 +4145,20 @@ qulonglong QLocaleData::stringToUnsLongLong(QStringView str, int base, bool *ok, qlonglong QLocaleData::bytearrayToLongLong(QByteArrayView num, int base, bool *ok) { - auto [l, endptr] = qstrntoll(num.data(), num.size(), base); - if (!endptr) { + const qsizetype len = num.size(); + auto [l, used] = qstrntoll(num.data(), len, base); + if (used <= 0) { if (ok != nullptr) *ok = false; return 0; } - const char *const stop = num.end(); - if (endptr < stop && *endptr != '\0') { - while (endptr < stop && ascii_isspace(*endptr)) - ++endptr; + if (used < len && num[used] != '\0') { + while (used < len && ascii_isspace(num[used])) + ++used; } - if (endptr < stop && *endptr != '\0') { + if (used < len && num[used] != '\0') { // we stopped at a non-digit character after converting some digits if (ok != nullptr) *ok = false; @@ -4172,20 +4172,20 @@ qlonglong QLocaleData::bytearrayToLongLong(QByteArrayView num, int base, bool *o qulonglong QLocaleData::bytearrayToUnsLongLong(QByteArrayView num, int base, bool *ok) { - auto [l, endptr] = qstrntoull(num.data(), num.size(), base); - if (!endptr) { + const qsizetype len = num.size(); + auto [l, used] = qstrntoull(num.data(), len, base); + if (used <= 0) { if (ok != nullptr) *ok = false; return 0; } - const char *const stop = num.end(); - if (endptr < stop && *endptr != '\0') { - while (endptr < stop && ascii_isspace(*endptr)) - ++endptr; + if (used < len && num[used] != '\0') { + while (used < len && ascii_isspace(num[used])) + ++used; } - if (endptr < stop && *endptr != '\0') { + if (used < len && num[used] != '\0') { if (ok != nullptr) *ok = false; return 0; diff --git a/src/corelib/text/qlocale_tools.cpp b/src/corelib/text/qlocale_tools.cpp index 5d439f2b26..ac4c870802 100644 --- a/src/corelib/text/qlocale_tools.cpp +++ b/src/corelib/text/qlocale_tools.cpp @@ -194,7 +194,7 @@ void qt_doubleToAscii(double d, QLocaleData::DoubleForm form, int precision, auto r = qstrntoll(target.data() + eSign + 1, length - eSign - 1, 10); decpt = r.result + 1; Q_ASSERT(r.ok()); - Q_ASSERT(r.endptr - target.data() <= length); + Q_ASSERT(r.used + eSign + 1 <= length); } else { // No 'e' found, so it's the 'f' form. Variants of snprintf generate numbers with // potentially multiple digits before the '.', but without decimal exponent then. So we @@ -282,9 +282,9 @@ QSimpleParsedNumber<double> qt_asciiToDouble(const char *num, qsizetype numLen, char c2 = lowered(num[offset + 1]); char c3 = lowered(num[offset + 2]); if (c == 'i' && c2 == 'n' && c3 == 'f') - return { negative ? -qt_inf() : qt_inf(), num + offset + 3 }; + return { negative ? -qt_inf() : qt_inf(), offset + 3 }; else if (c == 'n' && c2 == 'a' && c3 == 'n' && !hasSign) - return { qt_qnan(), num + 3 }; + return { qt_qnan(), 3 }; return {}; } } @@ -313,7 +313,7 @@ QSimpleParsedNumber<double> qt_asciiToDouble(const char *num, qsizetype numLen, return {}; } else { // Overflow. That's not OK, but we still return infinity. - return { d, nullptr }; + return { d, -processed }; } } #else @@ -342,7 +342,7 @@ QSimpleParsedNumber<double> qt_asciiToDouble(const char *num, qsizetype numLen, return {}; } } - return { d, nullptr }; + return { d, -processed }; } #endif // !defined(QT_NO_DOUBLECONVERSION) && !defined(QT_BOOTSTRAPPED) @@ -360,7 +360,7 @@ QSimpleParsedNumber<double> qt_asciiToDouble(const char *num, qsizetype numLen, } } } - return { d, num + processed }; + return { d, processed }; } /* Detect base if 0 and, if base is hex or bin, skip over 0x/0b prefixes */ @@ -430,7 +430,7 @@ QSimpleParsedNumber<qulonglong> qstrntoull(const char *begin, qsizetype size, in const auto res = std::from_chars(prefix.next, stop, result, prefix.base); if (res.ec != std::errc{}) return { }; - return { result, res.ptr == prefix.next ? begin : res.ptr }; + return { result, res.ptr == prefix.next ? 0 : res.ptr - begin }; } QSimpleParsedNumber<qlonglong> qstrntoll(const char *begin, qsizetype size, int base) @@ -458,12 +458,12 @@ QSimpleParsedNumber<qlonglong> qstrntoll(const char *begin, qsizetype size, int unsigned long long check = 0; res = std::from_chars(prefix.next, stop, check, prefix.base); if (res.ec == std::errc{} && check + std::numeric_limits<long long>::min() == 0) - return { std::numeric_limits<long long>::min(), res.ptr }; + return { std::numeric_limits<long long>::min(), res.ptr - begin }; return { }; } if (res.ec != std::errc{}) return { }; - return { negate ? -result : result, res.ptr }; + return { negate ? -result : result, res.ptr - begin }; } template <typename Char> @@ -558,7 +558,7 @@ double qstrntod(const char *s00, qsizetype len, const char **se, bool *ok) { auto r = qt_asciiToDouble(s00, len, TrailingJunkAllowed); if (se) - *se = r.endptr ? r.endptr : s00; + *se = s00 + (r.used < 0 ? -r.used : r.used); if (ok) *ok = r.ok(); return r.result; diff --git a/src/corelib/text/qlocale_tools_p.h b/src/corelib/text/qlocale_tools_p.h index c01bf617da..61c4061f1a 100644 --- a/src/corelib/text/qlocale_tools_p.h +++ b/src/corelib/text/qlocale_tools_p.h @@ -29,8 +29,9 @@ enum StrayCharacterMode { template <typename T> struct QSimpleParsedNumber { T result; - const char *endptr; - bool ok() { return endptr; } + // When used < 0, -used is how much was used, but it was an error. + qsizetype used; + bool ok() const { return used > 0; } }; // API note: this function can't process a number with more than 2.1 billion digits diff --git a/src/corelib/text/qlocale_win.cpp b/src/corelib/text/qlocale_win.cpp index 598de83b0e..1a31d8e90c 100644 --- a/src/corelib/text/qlocale_win.cpp +++ b/src/corelib/text/qlocale_win.cpp @@ -1075,8 +1075,8 @@ static QString winIso639LangName(LCID id) if (!lang_code.isEmpty()) { const QByteArray latin1 = std::move(lang_code).toLatin1(); - const auto [i, endptr] = qstrntoull(latin1.data(), latin1.size(), 16); - if (endptr && *endptr == '\0') { + const auto [i, used] = qstrntoull(latin1.data(), latin1.size(), 16); + if (used > 0 && latin1[used] == '\0') { switch (i) { case 0x814: result = u"nn"_s; // Nynorsk @@ -1117,8 +1117,8 @@ static QByteArray getWinLocaleName(LCID id) if (result == "C" || (!result.isEmpty() && qt_splitLocaleName(QString::fromLocal8Bit(result)))) { // See if we have a Windows locale code instead of a locale name: - auto [id, ok] = qstrntoll(result.data(), result.size(), 0); - if (!ok || id == 0 || id < INT_MIN || id > INT_MAX) // Assume real locale name + auto [id, used] = qstrntoll(result.data(), result.size(), 0); + if (used <= 0 || id == 0 || id < INT_MIN || id > INT_MAX) // Assume real locale name return result; return winLangCodeToIsoName(int(id)); } diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index 3ec9682935..ae0f60b49c 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -6901,9 +6901,9 @@ static int parse_field_width(const char *&c, qsizetype size) // can't be negative - started with a digit // contains at least one digit - auto [result, endp] = qstrntoull(c, size, 10); - c = endp; - if (!endp) + auto [result, used] = qstrntoull(c, size, 10); + c += used; + if (used <= 0) return false; // preserve Qt 5.5 behavior of consuming all digits, no matter how many while (c < stop && qIsDigit(*c)) diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp index d7c529ad10..9056fe79cc 100644 --- a/src/corelib/time/qtimezoneprivate_tz.cpp +++ b/src/corelib/time/qtimezoneprivate_tz.cpp @@ -394,26 +394,26 @@ static int parsePosixTime(const char *begin, const char *end) const int maxHour = 137; // POSIX's extended range. auto r = qstrntoll(begin, end - begin, 10); hour = r.result; - if (!r.ok() || hour < -maxHour || hour > maxHour || r.endptr > begin + 2) + if (!r.ok() || hour < -maxHour || hour > maxHour || r.used > 2) return INT_MIN; - begin = r.endptr; + begin += r.used; if (begin < end && *begin == ':') { // minutes ++begin; r = qstrntoll(begin, end - begin, 10); min = r.result; - if (!r.ok() || min < 0 || min > 59 || r.endptr > begin + 2) + if (!r.ok() || min < 0 || min > 59 || r.used > 2) return INT_MIN; - begin = r.endptr; + begin += r.used; if (begin < end && *begin == ':') { // seconds ++begin; r = qstrntoll(begin, end - begin, 10); sec = r.result; - if (!r.ok() || sec < 0 || sec > 59 || r.endptr > begin + 2) + if (!r.ok() || sec < 0 || sec > 59 || r.used > 2) return INT_MIN; - begin = r.endptr; + begin += r.used; } } diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index a97fc50f3a..59d84f5094 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -113,7 +113,7 @@ private: const char *seedstr = getenv("QT_HASH_SEED"); if (seedstr) { auto r = qstrntoll(seedstr, strlen(seedstr), 10); - if (r.endptr == seedstr + strlen(seedstr)) { + if (r.used > 0 && size_t(r.used) == strlen(seedstr)) { if (r.result) { // can't use qWarning here (reentrancy) fprintf(stderr, "QT_HASH_SEED: forced seed value is not 0; ignored.\n"); diff --git a/src/corelib/tools/qversionnumber.cpp b/src/corelib/tools/qversionnumber.cpp index f44d5e4e8c..dcb2a3ad64 100644 --- a/src/corelib/tools/qversionnumber.cpp +++ b/src/corelib/tools/qversionnumber.cpp @@ -409,12 +409,12 @@ static QVersionNumber from_string(QLatin1StringView string, qsizetype *suffixInd do { // parsing as unsigned so a minus sign is rejected - auto [value, end] = qstrntoull(start, endOfString - start, 10); - if (!end || value > qulonglong(std::numeric_limits<int>::max())) + auto [value, used] = qstrntoull(start, endOfString - start, 10); + if (used <= 0 || value > qulonglong(std::numeric_limits<int>::max())) break; seg.append(int(value)); - start = end + 1; - lastGoodEnd = end; + start += used + 1; + lastGoodEnd = start - 1; } while (start < endOfString && *lastGoodEnd == '.'); if (suffixIndex) diff --git a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp index f29b8a96ff..593b98dd60 100644 --- a/tests/auto/corelib/text/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/text/qlocale/tst_qlocale.cpp @@ -1246,6 +1246,10 @@ void tst_QLocale::strtod_data() QTest::newRow("12456789012") << QString("12456789012") << 12456789012.0 << 11 << true; QTest::newRow("1.2456789012e10") << QString("1.2456789012e10") << 12456789012.0 << 15 << true; + // Overflow - fails but reports right length: + QTest::newRow("1e2000") << QString("1e2000") << qInf() << 6 << false; + QTest::newRow("-1e2000") << QString("-1e2000") << -qInf() << 7 << false; + // starts with junk, fails QTest::newRow("a0") << QString("a0") << 0.0 << 0 << false; QTest::newRow("a0.") << QString("a0.") << 0.0 << 0 << false; @@ -1277,6 +1281,10 @@ void tst_QLocale::strtod_data() QTest::newRow("12456789012f") << QString("12456789012f") << 12456789012.0 << 11 << true; QTest::newRow("1.2456789012e10g") << QString("1.2456789012e10g") << 12456789012.0 << 15 << true; + // Overflow, ends with cruft - fails but reports right length: + QTest::newRow("1e2000 cruft") << QString("1e2000 cruft") << qInf() << 6 << false; + QTest::newRow("-1e2000 cruft") << QString("-1e2000 cruft") << -qInf() << 7 << false; + // "0x" prefix, success but only for the "0" before "x" QTest::newRow("0x0") << QString("0x0") << 0.0 << 1 << true; QTest::newRow("0x0.") << QString("0x0.") << 0.0 << 1 << true; @@ -1309,9 +1317,9 @@ void tst_QLocale::strtod() QCOMPARE(actualOk, ok); QCOMPARE(static_cast<int>(end - numData.constData()), processed); - // make sure neither QByteArray, QString or QLocale also work - // (but they don't support incomplete parsing) - if (processed == num_str.size() || processed == 0) { + // Make sure QByteArray, QString and QLocale also work. + // (They don't support incomplete parsing, and give 0 for overflow.) + if (ok && (processed == num_str.size() || processed == 0)) { actualOk = false; QCOMPARE(num_str.toDouble(&actualOk), num); QCOMPARE(actualOk, ok); |