diff options
author | Marc Mutz <marc.mutz@qt.io> | 2024-01-29 10:27:15 +0100 |
---|---|---|
committer | Marc Mutz <marc.mutz@qt.io> | 2024-01-30 22:28:19 +0100 |
commit | c6aa399d062c8b31c2ab88acf564a24cdff7b3c8 (patch) | |
tree | 563237ba974fed2f7606a57f9b2010cec96230b9 /src/corelib/tools | |
parent | 4d7b64cd2f927ac2413ed4d8fcb8be042fa083c5 (diff) |
QBitArray: avoid overflow in size-to-storage 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 (size + 7) / 8
logical-to-storage-size calculation by using unsigned arithmetic.
Use the opportunity to Extract Methods storage_size() and
allocation_size(), which differ by one (d[[0] contains the size() mod
8), making it clear what's what.
[ChangeLog][QtCore][QBitArray] Fixed a bug with QBitArrays whose
size() came within 7 of the size_type's maximum.
Pick-to: 6.7 6.6 6.5 6.2 5.15
Change-Id: I5d94bae9c9c210ba1e36f8cf03609125c81bd15d
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'src/corelib/tools')
-rw-r--r-- | src/corelib/tools/qbitarray.cpp | 19 |
1 files changed, 15 insertions, 4 deletions
diff --git a/src/corelib/tools/qbitarray.cpp b/src/corelib/tools/qbitarray.cpp index fed2bb6bd2..cc3b8a15cf 100644 --- a/src/corelib/tools/qbitarray.cpp +++ b/src/corelib/tools/qbitarray.cpp @@ -104,12 +104,23 @@ QT_BEGIN_NAMESPACE * inline qsizetype size() const { return (d.size() << 3) - *d.constData(); } */ +static constexpr qsizetype storage_size(qsizetype size) +{ + // avoid overflow when adding 7, by doing the arithmetic in unsigned space: + return qsizetype((size_t(size) + 7) / 8); +} + +static constexpr qsizetype allocation_size(qsizetype size) +{ + return size <= 0 ? 0 : storage_size(size) + 1; +} + /*! Constructs a bit array containing \a size bits. The bits are initialized with \a value, which defaults to false (0). */ QBitArray::QBitArray(qsizetype size, bool value) - : d(size <= 0 ? 0 : 1 + (size + 7) / 8, Qt::Uninitialized) + : d(allocation_size(size), Qt::Uninitialized) { Q_ASSERT_X(size >= 0, "QBitArray::QBitArray", "Size must be greater than or equal to 0."); if (size <= 0) @@ -187,7 +198,7 @@ void QBitArray::resize(qsizetype size) d.resize(0); } else { qsizetype s = d.size(); - d.resize(1 + (size + 7) / 8); + d.resize(allocation_size(size)); uchar *c = reinterpret_cast<uchar *>(d.data()); if (size > (s << 3)) memset(c + s, 0, d.size() - s); @@ -292,7 +303,7 @@ QBitArray QBitArray::fromBits(const char *data, qsizetype size) QBitArray result; if (size == 0) return result; - qsizetype nbytes = (size + 7) / 8; + qsizetype nbytes = storage_size(size); result.d = QByteArray(nbytes + 1, Qt::Uninitialized); char *bits = result.d.data(); @@ -922,7 +933,7 @@ QDataStream &operator>>(QDataStream &in, QBitArray &ba) } const qsizetype Step = 8 * 1024 * 1024; - qsizetype totalBytes = (len + 7) / 8; + const qsizetype totalBytes = storage_size(len); qsizetype allocated = 0; while (allocated < totalBytes) { |