diff options
author | Marc Mutz <marc.mutz@qt.io> | 2022-02-23 11:57:00 +0100 |
---|---|---|
committer | Marc Mutz <marc.mutz@qt.io> | 2022-02-25 08:08:21 +0000 |
commit | 5a39173c34be6fb033b8988408d0d98546db9f13 (patch) | |
tree | e38697494364865c9f731ff451ea299dbecca898 /src/corelib/text/qlocale_tools.cpp | |
parent | 64d65a645c3f64c6b317aed571366bc049c1cd25 (diff) |
Don't allocate in qt_asciiToDouble()
The sscanf implementation ensured NUL-termination of the input data,
by copying it, and appending NUL.
Since this function is ignoring trailing garbage and reports the
progress back, we could be parsing the first double in a multi-MiB
buffer. And we'd been copying and copying the buffer for every double
scanned. This is clearly not acceptable.
An alternative is to use the max-field-width feature of scanf. By
giving the size of the input data as the maximum field width in the
format string, we stop sscanf from reading more than the available
data.
This code should let everyone's alarm bells go off: a format string
constructed at run-time is really the last thing one should consider,
but I haven't found a way to pass the field width as an argument, so
bite the bullet and go through with it. Copying potentially MiBs of
data is the worse of the two evils.
Pick-to: 6.3
Fixes: QTBUG-101178
Change-Id: Ibaf8142f6b3dab4d5e3631c3cc8cc6699bceb320
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src/corelib/text/qlocale_tools.cpp')
-rw-r--r-- | src/corelib/text/qlocale_tools.cpp | 25 |
1 files changed, 8 insertions, 17 deletions
diff --git a/src/corelib/text/qlocale_tools.cpp b/src/corelib/text/qlocale_tools.cpp index c133a028c0..97c91dd3ee 100644 --- a/src/corelib/text/qlocale_tools.cpp +++ b/src/corelib/text/qlocale_tools.cpp @@ -286,7 +286,7 @@ double qt_asciiToDouble(const char *num, qsizetype numLen, bool &ok, int &proces return needleLen == haystackLen && memcmp(needle, haystack, haystackLen) == 0; }; - if (numLen == 0) { + if (numLen <= 0) { ok = false; processed = 0; return 0.0; @@ -350,23 +350,14 @@ double qt_asciiToDouble(const char *num, qsizetype numLen, bool &ok, int &proces } } #else - // need to ensure that our input is null-terminated for sscanf - // (this is a QVarLengthArray<char, 128> but this code here is too low-level for QVLA) - char reasonableBuffer[128]; - char *buffer; - if (numLen < qsizetype(sizeof(reasonableBuffer)) - 1) - buffer = reasonableBuffer; - else - buffer = static_cast<char *>(malloc(numLen + 1)); - Q_CHECK_PTR(buffer); - memcpy(buffer, num, numLen); - buffer[numLen] = '\0'; - - if (qDoubleSscanf(buffer, QT_CLOCALE, "%lf%n", &d, &processed) < 1) - processed = 0; + // ::digits10 is 19, but ::max() is 18'446'744'073'709'551'615ULL - go, figure... + constexpr auto maxDigitsForULongLong = 1 + std::numeric_limits<unsigned long long>::digits10; + // need to ensure that we don't read more than numLen of input: + char fmt[1 + maxDigitsForULongLong + 4 + 1]; + sprintf(fmt, "%s%llu%s", "%", static_cast<unsigned long long>(numLen), "lf%n"); - if (buffer != reasonableBuffer) - free(buffer); + if (qDoubleSscanf(num, QT_CLOCALE, fmt, &d, &processed) < 1) + processed = 0; if ((strayCharMode == TrailingJunkProhibited && processed != numLen) || qIsNaN(d)) { // Implementation defined nan symbol or garbage found. We don't accept it. |