summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qbitarray.cpp
diff options
context:
space:
mode:
authorMarc Mutz <marc.mutz@qt.io>2024-01-30 11:15:13 +0100
committerMarc Mutz <marc.mutz@qt.io>2024-02-01 18:42:13 +0100
commit78f8dfc5427457783ceef7d85885cddbec035ebe (patch)
tree7694617760a483108bb954e390ba306994d6b085 /src/corelib/tools/qbitarray.cpp
parent5ba0982b2879a1a4c13bf97467b8e5ad296e57a2 (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.cpp41
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