QBinaryJson: reject a misaligned Base::tableOffset


The binary JSON format is 4 byte aligned throughout: alignedSize()
rounds every string and entry up to a multiple of 4, Header is 8
bytes, Base is 12, Entry, Value and offset are 4 each, and
fromRawData() refuses data that is not itself 4 byte
aligned. Base::table() is therefore dereferenced as an array of
properly aligned offsets.

The tableOffset comes straight from the data, though, and isValid()
only bounds-checks it, so a tableOffset that is not a multiple of 4
makes table() return a misaligned pointer. Unaligned loads are usually
not a problem at the assembler level: either the hardware supports
them directly, or the operation is trapped and emulated by the kernel
(which is slow, though). The problem is that the loads are UB at the
C++ abstract machine level, so the optimizer can break the code in
very sophisticated ways; in code whose job it is to decide whether the
data can be trusted.

28 of the 32 files in invalidBinaryData/ have tableOffset == 395; only
17, 27 and 38-41 are rejected before it is used. Ubsan complained
about misaligned loads:

   qbinaryjson.cpp:442:22: runtime error: member call on misaligned address 0x7c80945e0e23 for type 'const struct offset', which requires 4 byte alignment
   0x7c80945e0e23: note: pointer points here
    00  00 00 04 22 00 00 00 00  00 00 00 24 00 00 00 1c  00 6a 73 01 00 00 00 04  02 00 00 23 00 00 00
                 ^
       #0 0x7f20a2885c27 in QBinaryJsonPrivate::Object::isValidHelper(unsigned int, QVarLengthArray<QBinaryJsonPrivate::IsValidFrame, 16ll>&) const qbinaryjson.cpp:442
       #1 0x7f20a28888cb in QBinaryJsonPrivate::ConstData::isValid() const qbinaryjson.cpp:320
       #2 0x7f20a288d5b3 in QBinaryJson::fromRawData(char const*, int, QBinaryJson::DataValidation) qbinaryjson.cpp:82
       #3 0x55e38e739bfb in tst_QtJson::invalidBinaryData() tst_qtjson.cpp:129

Rejecting an unaligned tableOffset cannot reject anything the writer
produces - Base::reserveSpace() only ever advances tableOffset by
multiples of 4 - so no document that round-trips today starts failing.
Those 28 files were already rejected and still are, just earlier.

[ChangeLog][QBinaryJson] Fixed a bug that would cause undefined
behavior on certain invalid inputs to occur, making rejection of such
inputs unreliable (we did not observe them in our CI, but UB can
manifest in surprising ways, so absence of proof isn't proof of
absence).

Amends qtbase/37a7b035f8feb248d25741b22e22ffb0ec7463d6 (Qt 5.0), which
added both isValid() overloads; the code moved here unchanged in
12423d302a9716a3848f4a7ca542392060376431. Qt 5.15 has it in qtbase's
src/corelib/serialization/qbinaryjson.cpp.

Pick-to: 6.12 6.11 6.8 6.5 5.15
Task-number: QTBUG-149219
Change-Id: I54a80669eeb3e3a52fa900a8e891d467710f35e5
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
commita2a158316afbbaa46f45174bcfd4c5cbbf1ed954
parent378b7ab9fb0d527637b2c2f816458061a2ddd2c9
authorMarc Mutz <marc.mutz@qt.io>
committerMarc Mutz <marc.mutz@qt.io>
commit timeWed, 2 Sep 2026 06:34:56 +0000