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:
parent
e69a151573
commit
e576d50d7c
@ -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
|
||||||
|
@ -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(); }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user