QByteArray::toDouble: fix buffer overflow reads on fromRawData()

If Qt was not compiled with libdouble-conversion, sscanf() requires
null-termination, which fromRawData() does not require. This could be
fixed by making QByteArray pass a reallocated copy if it is operating on
raw data, but fixing qt_asciiToDouble() means we catch all cases and we
optimize for the common case of not-horribly-long strings.

Fixes: QTBUG-85580
Change-Id: Iea47e0f8fc8b40378df7fffd16246f6163b01442
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
(cherry picked from commit efd3c7bf2427c8237857e56ecd51b8da3ce43a6e)
This commit is contained in:
Thiago Macieira 2020-07-23 09:43:52 -07:00
parent 58af0a3db5
commit 11740acbca

View File

@ -280,6 +280,11 @@ void qt_doubleToAscii(double d, QLocaleData::DoubleForm form, int precision, cha
double qt_asciiToDouble(const char *num, int numLen, bool &ok, int &processed, double qt_asciiToDouble(const char *num, int numLen, bool &ok, int &processed,
StrayCharacterMode strayCharMode) StrayCharacterMode strayCharMode)
{ {
auto string_equals = [](const char *needle, const char *haystack, qsizetype haystackLen) {
qsizetype needleLen = strlen(needle);
return needleLen == haystackLen && memcmp(needle, haystack, haystackLen) == 0;
};
if (*num == '\0') { if (*num == '\0') {
ok = false; ok = false;
processed = 0; processed = 0;
@ -291,10 +296,10 @@ double qt_asciiToDouble(const char *num, int numLen, bool &ok, int &processed,
// We have to catch NaN before because we need NaN as marker for "garbage" in the // We have to catch NaN before because we need NaN as marker for "garbage" in the
// libdouble-conversion case and, in contrast to libdouble-conversion or sscanf, we don't allow // libdouble-conversion case and, in contrast to libdouble-conversion or sscanf, we don't allow
// "-nan" or "+nan" // "-nan" or "+nan"
if (qstrcmp(num, "nan") == 0) { if (string_equals("nan", num, numLen)) {
processed = 3; processed = 3;
return qt_snan(); return qt_snan();
} else if ((num[0] == '-' || num[0] == '+') && qstrcmp(num + 1, "nan") == 0) { } else if (string_equals("+nan", num, numLen) || string_equals("-nan", num, numLen)) {
processed = 0; processed = 0;
ok = false; ok = false;
return 0.0; return 0.0;
@ -302,13 +307,13 @@ double qt_asciiToDouble(const char *num, int numLen, bool &ok, int &processed,
// Infinity values are implementation defined in the sscanf case. In the libdouble-conversion // Infinity values are implementation defined in the sscanf case. In the libdouble-conversion
// case we need infinity as overflow marker. // case we need infinity as overflow marker.
if (qstrcmp(num, "+inf") == 0) { if (string_equals("+inf", num, numLen)) {
processed = 4; processed = 4;
return qt_inf(); return qt_inf();
} else if (qstrcmp(num, "inf") == 0) { } else if (string_equals("inf", num, numLen)) {
processed = 3; processed = 3;
return qt_inf(); return qt_inf();
} else if (qstrcmp(num, "-inf") == 0) { } else if (string_equals("-inf", num, numLen)) {
processed = 4; processed = 4;
return -qt_inf(); return -qt_inf();
} }
@ -337,9 +342,23 @@ double qt_asciiToDouble(const char *num, int numLen, bool &ok, int &processed,
} }
} }
#else #else
if (qDoubleSscanf(num, QT_CLOCALE, "%lf%n", &d, &processed) < 1) // 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));
memcpy(buffer, num, numLen);
buffer[numLen] = '\0';
if (qDoubleSscanf(buffer, QT_CLOCALE, "%lf%n", &d, &processed) < 1)
processed = 0; processed = 0;
if (buffer != reasonableBuffer)
free(buffer);
if ((strayCharMode == TrailingJunkProhibited && processed != numLen) || qIsNaN(d)) { if ((strayCharMode == TrailingJunkProhibited && processed != numLen) || qIsNaN(d)) {
// Implementation defined nan symbol or garbage found. We don't accept it. // Implementation defined nan symbol or garbage found. We don't accept it.
processed = 0; processed = 0;