diff options
| author | Marc Mutz <marc.mutz@qt.io> | 2024-01-30 11:15:13 +0100 |
|---|---|---|
| committer | Marc Mutz <marc.mutz@qt.io> | 2024-02-01 18:42:13 +0100 |
| commit | 78f8dfc5427457783ceef7d85885cddbec035ebe (patch) | |
| tree | 7694617760a483108bb954e390ba306994d6b085 /src/corelib/tools/qbitarray.cpp | |
| parent | 5ba0982b2879a1a4c13bf97467b8e5ad296e57a2 (diff) | |
QBitArray: avoid overflow in storage-to-size calculations
Unlike other containers, a QBitArray's size() is not limited by
storage, but, esp. on 32-bit platforms, its size_type: A INT_MAX
size() QBitArray only requires 256MiB of storage.
So we can't rely on "won't happen in practice" here and need to avoid
the potential UB (signed overflow) in the (d.size() * 8 - *d.data())
storage-to-logical-size calculation by using unsigned arithmetic.
Use the opportunity to Extract Method adjust_head_and_tail(),
centralizing the bit fiddling.
Pick-to: 6.7 6.6 6.5 6.2 5.15
Change-Id: I485eafdf3ce2087a81c683672ff98a43f97c9968
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib/tools/qbitarray.cpp')
| -rw-r--r-- | src/corelib/tools/qbitarray.cpp | 41 |
1 files changed, 20 insertions, 21 deletions
diff --git a/src/corelib/tools/qbitarray.cpp b/src/corelib/tools/qbitarray.cpp index 12794bd70a7..0ea012a94b1 100644 --- a/src/corelib/tools/qbitarray.cpp +++ b/src/corelib/tools/qbitarray.cpp @@ -120,6 +120,16 @@ static constexpr qsizetype allocation_size(qsizetype size) return size <= 0 ? 0 : storage_size(size) + 1; } +static void adjust_head_and_tail(char *data, qsizetype storageSize, qsizetype logicalSize) +{ + quint8 *c = reinterpret_cast<quint8 *>(data); + // store the difference between storage and logical size in d[0]: + *c = quint8(size_t(storageSize) * 8 - logicalSize); + // reset unallocated bits to 0: + if (logicalSize & 7) + *(c + 1 + logicalSize / 8) &= (1 << (logicalSize & 7)) - 1; +} + /*! Constructs a bit array containing \a size bits. The bits are initialized with \a value, which defaults to false (0). @@ -133,9 +143,7 @@ QBitArray::QBitArray(qsizetype size, bool value) uchar *c = reinterpret_cast<uchar *>(d.data()); memset(c + 1, value ? 0xff : 0, d.size() - 1); - *c = d.size() * 8 - size; - if (value && size && size & 7) - *(c + 1 + size / 8) &= (1 << (size & 7)) - 1; + adjust_head_and_tail(d.data(), d.size(), size); } /*! \fn qsizetype QBitArray::size() const @@ -205,11 +213,9 @@ void QBitArray::resize(qsizetype size) qsizetype s = d.size(); d.resize(allocation_size(size)); uchar *c = reinterpret_cast<uchar *>(d.data()); - if (size > (s << 3)) + if (d.size() > s) memset(c + s, 0, d.size() - s); - else if (size & 7) - *(c + 1 + size / 8) &= (1 << (size & 7)) - 1; - *c = d.size() * 8 - size; + adjust_head_and_tail(d.data(), d.size(), size); } } @@ -308,17 +314,11 @@ QBitArray QBitArray::fromBits(const char *data, qsizetype size) QBitArray result; if (size == 0) return result; - qsizetype nbytes = storage_size(size); - result.d = QByteArray(nbytes + 1, Qt::Uninitialized); - char *bits = result.d.data(); - memcpy(bits + 1, data, nbytes); - - // clear any unused bits from the last byte - if (size & 7) - bits[nbytes] &= 0xffU >> (8 - (size & 7)); - - *bits = result.d.size() * 8 - size; + auto &d = result.d; + d.resize(allocation_size(size)); + memcpy(d.data() + 1, data, d.size() - 1); + adjust_head_and_tail(d.data(), d.size(), size); return result; } @@ -962,14 +962,13 @@ QDataStream &operator>>(QDataStream &in, QBitArray &ba) allocated += blockSize; } - qsizetype paddingMask = ~((0x1 << (len & 0x7)) - 1); - if (paddingMask != ~0x0 && (ba.d.constData()[ba.d.size() - 1] & paddingMask)) { + const auto fromStream = ba.d.back(); + adjust_head_and_tail(ba.d.data(), ba.d.size(), len); + if (ba.d.back() != fromStream) { ba.clear(); in.setStatus(QDataStream::ReadCorruptData); return in; } - - *ba.d.data() = ba.d.size() * 8 - len; return in; } #endif // QT_NO_DATASTREAM |
