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.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>
(cherry picked from commit 78f8dfc5427457783ceef7d85885cddbec035ebe)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Marc Mutz 2024-01-30 11:15:13 +01:00 committed by Qt Cherry-pick Bot
parent e69a151573
commit e576d50d7c
2 changed files with 22 additions and 23 deletions

View File

@ -118,6 +118,16 @@ static constexpr qsizetype allocation_size(qsizetype size)
return size <= 0 ? 0 : storage_size(size) + 1; 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 Constructs a bit array containing \a size bits. The bits are
initialized with \a value, which defaults to false (0). initialized with \a value, which defaults to false (0).
@ -131,9 +141,7 @@ QBitArray::QBitArray(qsizetype size, bool value)
uchar *c = reinterpret_cast<uchar *>(d.data()); uchar *c = reinterpret_cast<uchar *>(d.data());
memset(c + 1, value ? 0xff : 0, d.size() - 1); memset(c + 1, value ? 0xff : 0, d.size() - 1);
*c = d.size() * 8 - size; adjust_head_and_tail(d.data(), d.size(), size);
if (value && size && size & 7)
*(c + 1 + size / 8) &= (1 << (size & 7)) - 1;
} }
/*! \fn qsizetype QBitArray::size() const /*! \fn qsizetype QBitArray::size() const
@ -203,11 +211,9 @@ void QBitArray::resize(qsizetype size)
qsizetype s = d.size(); qsizetype s = d.size();
d.resize(allocation_size(size)); d.resize(allocation_size(size));
uchar *c = reinterpret_cast<uchar *>(d.data()); uchar *c = reinterpret_cast<uchar *>(d.data());
if (size > (s << 3)) if (d.size() > s)
memset(c + s, 0, d.size() - s); memset(c + s, 0, d.size() - s);
else if (size & 7) adjust_head_and_tail(d.data(), d.size(), size);
*(c + 1 + size / 8) &= (1 << (size & 7)) - 1;
*c = d.size() * 8 - size;
} }
} }
@ -306,17 +312,11 @@ QBitArray QBitArray::fromBits(const char *data, qsizetype size)
QBitArray result; QBitArray result;
if (size == 0) if (size == 0)
return result; return result;
qsizetype nbytes = storage_size(size);
result.d = QByteArray(nbytes + 1, Qt::Uninitialized); auto &d = result.d;
char *bits = result.d.data(); d.resize(allocation_size(size));
memcpy(bits + 1, data, nbytes); memcpy(d.data() + 1, data, d.size() - 1);
adjust_head_and_tail(d.data(), d.size(), size);
// clear any unused bits from the last byte
if (size & 7)
bits[nbytes] &= 0xffU >> (8 - (size & 7));
*bits = result.d.size() * 8 - size;
return result; return result;
} }
@ -958,14 +958,13 @@ QDataStream &operator>>(QDataStream &in, QBitArray &ba)
allocated += blockSize; allocated += blockSize;
} }
qsizetype paddingMask = ~((0x1 << (len & 0x7)) - 1); const auto fromStream = ba.d.back();
if (paddingMask != ~0x0 && (ba.d.constData()[ba.d.size() - 1] & paddingMask)) { adjust_head_and_tail(ba.d.data(), ba.d.size(), len);
if (ba.d.back() != fromStream) {
ba.clear(); ba.clear();
in.setStatus(QDataStream::ReadCorruptData); in.setStatus(QDataStream::ReadCorruptData);
return in; return in;
} }
*ba.d.data() = ba.d.size() * 8 - len;
return in; return in;
} }
#endif // QT_NO_DATASTREAM #endif // QT_NO_DATASTREAM

View File

@ -70,8 +70,8 @@ public:
void swap(QBitArray &other) noexcept { d.swap(other.d); } void swap(QBitArray &other) noexcept { d.swap(other.d); }
inline qsizetype size() const { return (d.size() << 3) - *d.constData(); } qsizetype size() const { return qsizetype((size_t(d.size()) << 3) - *d.constData()); }
inline qsizetype count() const { return (d.size() << 3) - *d.constData(); } qsizetype count() const { return size(); }
qsizetype count(bool on) const; qsizetype count(bool on) const;
inline bool isEmpty() const { return d.isEmpty(); } inline bool isEmpty() const { return d.isEmpty(); }