-
Notifications
You must be signed in to change notification settings - Fork 214
Description
Hi everyone! This is my first bug report contribution into the FlatCC project. Thanks for making such an awesome project! Here are the details:
Files attached
- build.sh has the build command to repro (define $GCC_PATH)
- version.txt has GCC version (latest none-eabi) -- I was very close to filing a GCC bug until I turned on -fno-strict-aliasing and realized the issue is with the aliasing in FlatCC, not with the compiler
- tempfiles.c The source code, after the preprocessor pasted the generated FlatCC code. Note the suffix "v2" is added by me by hand before posting here, not generated
- out.lst The dissasembly
Explanation of the source code provided in tempfiles.c
The code in question is in the function interface_encoding_populate_general_rsp(). writes a message determined at compile-time into an array in the stack, and then finishes the message and copies into a buffer given by the caller.
If one steps through the code, essentially the following happens:
- Response_as_root_v2() reads the word at msg_array[0] which should be equal to 12 and returns &msg_array[12]
- Response_data_v2() reads the word at msg_array[12] which should be equal to 8 and then finds &msg_array[12-8]=&msg_array[4]. Then reads half-word at msg_array[4] and checks whether it is >=8. If less, it asserts
From the initialization of msg_array we can see everything should be initialized correctly, then Response_as_root_v2() and Reponse_data_v2() will work exactly as described above.
Actual Issue
The compiler smartly determines that it needs to look at msg_array[4] and check if it is less than 8. However, it reads msg_array[4] into R5 which has garbage in the stack. Then it initializes msg_array in the stack, and then compares R5 with 7 (branches to assert if less-or-equal). The problem is that by the time the comparison is done, the array is partly initialized and R5 has garbage from the stack before the initialization of the array
a: f8bd 5004 ldrh.w r5, [sp, #4]
e: cc0f ldmia r4!, {r0, r1, r2, r3}
10: e8ac 000f stmia.w ip!, {r0, r1, r2, r3}
14: cc0f ldmia r4!, {r0, r1, r2, r3}
16: 2d07 cmp r5, #7
Schema
The relevant portion looks like this: A table that requires an union inside, and the union has one of many tables
table MyTableResponse{
// Some integers
}
union ResponseData {
// A bunch of tables
/// Generic response (success w/ no payload or error).
MyTable: MyTableResponse,
}
table Response {
data: ResponseData (required);
}
Background
I found this by Aardappel which seems to be an user on stackoverflow who is highly involved with the project:
https://stackoverflow.com/questions/24330925/does-flatbuffers-avoid-strict-aliasing-somehow
Then in theory, if you first construct a FlatBuffer and then immediately read it, it could optimize across the writing and reading code, and do "evil" optimisations of the kind Linus was referring to in your link above (pretend the writing never happened).
Workarounds
- Add a compiler barrier
- Make things volatile and cast them before passing them into flatcc API's
- Use lower optimization levels
- Use -fno-strict-aliasing
- A bunch of other potential compiler tricks