On Fri, Dec 08, 2017 at 09:49:05AM +0100, Andreas Tille wrote:
> Hi Flavien,
> I have put the porter lists of the affected architectures in CC whether
> there is somebody who has a hint for a better solution than removing
> these architectures from the supported architectures.  This kind of
> "random failure"[1] is quite hard to debug for somebody who is not
> familiar for the said architectures.

f4 is (long, long, long) -> long, and so the generated Qt metacall magic
wrapper around f4 treats its arguments as an array of long*,
dereferences them, passes them to f4 and stores the return value for the

However, camp's own Value class only has camp::intType; it has no type
for long or long long. This means that valueToVariant always gives a
QVariant storing an int, so QtFunction::execute invokes the meta method
with QGenericArgument's pointing to ints. Therefore, when the metacall
wrapper reads them, it reads too much, and gets 32 bits of garbage after
them in memory. Normally in C arguments are promoted automatically, but
because of all these levels of indirection it has to be done manually
(as you can see for example with the double tests, which must use the
double literal 1. rather than the int literal 1).

Now, as to why it only affects 64-bit big-endian. Obviously, 32-bit is
unaffected, as sizeof(int) == sizeof(long) there. On 64-bit
little-endian, reading too much data puts the garbage in the *higher*
bits in the registers; if you then add values with garbage in the higher
half, the lower half will remain correct, and it gets stored as a 64-bit
value. Then eventually it gets read as an int (variantType sees that the
function returns a QMetaType::Long, which is mapped to a return
QVariant::Int), so the higher 32 bits get dropped, and all appears fine
(despite the horrendous out-of-bounds memory accesses).

On 64-bit big-endian systems, though, it's not quite so forgiving. When
it reads the 32-bit value as a 64-bit value, the endianness means that
the 32 bits of garbage are the *lower* 32 bits in the registers, and so
when adding three numbers together, the sum of these garbage halves
could overflow (up to twice) into the higher 32 bits, which store the
desired values, causing the upper half to non-deterministically be 20,
21 or 22. This gets stored as a 64-bit quantity again, and then later
re-read as a 32-bit quantity, and again due to the endianness it only
reads what was the higher half in the register, i.e. either 20, 21 or

I don't have a patch, because fixing this requires a fairly involved
trawl through the source. I haven't tried using it, but valgrind might
catch these out-of-bounds reads regardless of the system's endianness.

TL;DR camp needs to stop treating longs like ints.


Reply via email to