diff options
| author | Marc Mutz <marc.mutz@qt.io> | 2025-06-06 16:40:48 +0200 |
|---|---|---|
| committer | Marc Mutz <marc.mutz@qt.io> | 2025-07-04 01:10:09 +0200 |
| commit | 3f61f736266ece40d627dcf6214618a22a009fd1 (patch) | |
| tree | b48fa2420b0acd9966249cc28890e84dffdfc0a4 /src/corelib/tools/qarraydata.cpp | |
| parent | 22effeae2ca4565ad537ac95f638754a92afcf72 (diff) | |
QArrayData: fix potential UB in QBasicAtomic initialization
Until C++17 (inclusive), a default-constructed std::atomic object can,
officially, only be initialized with a call to std::atomic_init, for
which QBasicAtomic doesn't have API. It is even unclear whether
zero-initialization of static and thread-local objects will cause the
object to be initialized.
Seeing as we can't rely on malloc's zero-initialization to properly
initialize std::atomic objects (and therefore QBasicAtomic ones), and
because it's the right thing to do, from a [basic.life] POV, anyway,
port to placement new instead.
The realloc() feels fishy, seeing as it reallocs a struct that
contains an atomic variable (which we don't mark as Q_RELOCATABLE_TYPE
even for our own types), but assume that part it ok, and in-place
construct only if realloc() was passed nullptr (so is equivalen to
malloc()). A final solution for this can probably only be implemented
when we can depend on C++20's atomic_ref, which decouples the
underlying object from the atomic operations performed on it.
Rename the ref_ member to m_ref to catch any uses of the variable,
incl. since removed ones in older branches.
Amends 812a611dc05e5facd036856625ccb9274fdcb117 (which ported from
QtPrivate::RefCount to QBasicAtomicInt; I didn't check whether
QtPrivate::RefCount had a similar problem, because that commit is
already much older than what I can pick back to at this point).
Task-number: QTBUG-137465
Pick-to: 6.10 6.9 6.8 6.5
Change-Id: I188e05d09e20e0820af7cf1cbb651afa860bb9d6
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib/tools/qarraydata.cpp')
| -rw-r--r-- | src/corelib/tools/qarraydata.cpp | 17 |
1 files changed, 10 insertions, 7 deletions
diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 37d6dea35f9..54bd971cb36 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -181,13 +181,12 @@ allocateHelper(qsizetype objectSize, qsizetype alignment, qsizetype capacity, return {}; void *data = nullptr; - QArrayData *header = static_cast<QArrayData *>(::malloc(size_t(allocSize))); - if (Q_LIKELY(header)) { - header->ref_.storeRelaxed(1); - header->flags = {}; + void *mem = ::malloc(size_t(allocSize)); + QArrayData *header = nullptr; + if (Q_LIKELY(mem)) { + header = new (mem) QArrayData{1, {}, capacity}; // find where offset should point to so that data() is aligned to alignment bytes data = QTypedArrayData<void>::dataStart(header, alignment); - header->alloc = capacity; } return { data, header }; @@ -245,8 +244,12 @@ QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer, Q_ASSERT(offset > 0); Q_ASSERT(offset <= allocSize); // equals when all free space is at the beginning - QArrayData *header = static_cast<QArrayData *>(::realloc(data, size_t(allocSize))); - if (header) { + const bool hadData = data; + void *mem = ::realloc(data, size_t(allocSize)); + QArrayData *header = static_cast<QArrayData *>(mem); + if (mem) { + if (!hadData) + header = new (mem) QArrayData{0, {}, {}}; header->alloc = capacity; dataPointer = reinterpret_cast<char *>(header) + offset; } else { |
