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 caller. 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 22. 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. Regards, James