summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Mutz <marc.mutz@qt.io>2023-02-14 09:26:04 +0100
committerMarc Mutz <marc.mutz@qt.io>2023-03-11 13:56:16 +0000
commit519d2d8f442409e86a0ee2fa16bd543342180861 (patch)
tree5b8d614e77170e804b5087af4d2f637dc0e183df
parent59412965123e64bd19c05635c5db7ac3c5445f22 (diff)
QVarLengthArray: fix memory leak in (qsizetype) ctorv6.4.36.4.3
Instead of duplicating the logic of resize() to avoid three stores to (ptr, a, s), and getting the memory management wrong (malloc()ed ptr leaked when a following default-construction failed), simply call the QVLA default ctor followed by std::uninitialized_default_construct_n(). After we called the QVLA default ctor, the lifetime of this object has begun, so if the following code throws, ~QVLA will be called to clean up. This was not the case when we didn't delegate the ctor: if the body of this ctor threw, ~QVLA would _not_ have been called, since the lifetime of the object had never started. Since the dtor may now run, we need to maintain the class invariants, so we can't just set the size() before we have constructed all elements. This is where std::uninitialized_default_construct_n() comes in: it's strongly exception-safe, so if a constructor throws, it will destroy all previously-constructed elements, so that either we fail and size() == 0 is true or it doesn't, then we can set size() to asize. Change-Id: I03408d8a473e8a31fa0086ef5ac551ce46a758f8 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io> (cherry picked from commit 217f2ab3eb04b7489959e8e1134184e8071dbaf0)
-rw-r--r--src/corelib/tools/qvarlengtharray.h29
1 files changed, 14 insertions, 15 deletions
diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h
index 2c88fbef2b..02015be904 100644
--- a/src/corelib/tools/qvarlengtharray.h
+++ b/src/corelib/tools/qvarlengtharray.h
@@ -653,23 +653,22 @@ QVarLengthArray(InputIterator, InputIterator) -> QVarLengthArray<ValueType>;
template <class T, qsizetype Prealloc>
Q_INLINE_TEMPLATE QVarLengthArray<T, Prealloc>::QVarLengthArray(qsizetype asize)
+ : QVarLengthArray()
{
- this->s = asize;
- static_assert(Prealloc > 0, "QVarLengthArray Prealloc must be greater than 0.");
- Q_ASSERT_X(size() >= 0, "QVarLengthArray::QVarLengthArray()", "Size must be greater than or equal to 0.");
- if (size() > Prealloc) {
- this->ptr = malloc(size() * sizeof(T));
- Q_CHECK_PTR(data());
- this->a = size();
- } else {
- this->ptr = this->array;
- this->a = Prealloc;
- }
- if constexpr (QTypeInfo<T>::isComplex) {
- T *i = end();
- while (i != begin())
- new (--i) T;
+ Q_ASSERT_X(asize >= 0, "QVarLengthArray::QVarLengthArray(qsizetype)",
+ "Size must be greater than or equal to 0.");
+
+ // historically, this ctor worked for non-copyable/non-movable T, so keep it working, why not?
+ // resize(asize) // this requires a movable or copyable T, can't use, need to do it by hand
+
+ if (asize > Prealloc) {
+ this->ptr = malloc(asize * sizeof(T));
+ Q_CHECK_PTR(this->ptr);
+ this->a = asize;
}
+ if constexpr (QTypeInfo<T>::isComplex)
+ std::uninitialized_default_construct_n(data(), asize);
+ this->s = asize;
}
template <class T>