Vector35 / Vector35/binaryninja-api
BN sometimes mishandles two 32-bit arguments packed into 64-bit stack slot
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 1.3k
- Forks
- 298
- Avg merge
- 5d 5h
- Merged PRs (30d)
- 19
Description
Version and Platform (required):
- Binary Ninja Version: 5.4.9875-dev (86d1e1ad)
- Edition: Commercial
- OS: Debian Linux
- OS Version: unstable
- CPU Architecture: x86_64
Bug Description:
In the Apple arm64 ABI, "Function arguments may consume slots on the stack that are not multiples of 8 bytes." Thus, for example, a series of 32-bit arguments on the stack is placed with no padding between arguments. Binary Ninja mishandles this… sometimes.
Steps To Reproduce:
Test case: test.zip
Both files (optimized.o and unoptimized.o) are built from the same source code:
extern void test(int x0, int x1, int x2, int x3, int x4, int x5, int x6, int x7, int sp0, int sp4, int sp8, int spC);
void go() {
test(0, 1, 2, 3, 4, 5, 6, 7, 0x100, 0x104, 0x108, 0x10c);
}
To reproduce, load one of the objects, create a function for go (for some reason it is not created automatically; another bug?), and set the type of test to match the source code.
Expected behavior: Both files should show test being called with the correct arguments.
Actual behavior: unoptimized.o is correct, but optimized.o shows:
0040004c return _test(x0: 0, x1: 1, x2: 2, x3: 3, x4: 4, x5: 5, x6: 6, x7: 7,
0040004c sp0: 0x10400000100, sp4: 0x10c00000108)
The difference between the binaries is that optimized.o was built with -O3 while unoptimized.o was built with -O0. As a result, optimized.o packs the arguments into 64-bit registers before storing them to the stack:
0040000c 082180d2 mov x8, #0x108
00400010 8821c0f2 movk x8, #0x10c, lsl #0x20 {0x10c00000108}
00400014 092080d2 mov x9, #0x100
00400018 8920c0f2 movk x9, #0x104, lsl #0x20 {0x10400000100}
0040001c e92300a9 stp x9, x8, [sp] {var_20} {var_18} {0x10400000100} {0x10c00000108}
Whereas optimized.o just stores the arguments one at a time:
00400010 08208052 mov w8, #0x100
00400014 280100b9 str w8, [x9 {var_20}] {0x100}
00400018 88208052 mov w8, #0x104
0040001c 280500b9 str w8, [x9, #0x4 {var_1c}] {0x104}
00400020 08218052 mov w8, #0x108
00400024 280900b9 str w8, [x9, #0x8 {var_18}] {0x108}
00400028 88218052 mov w8, #0x10c
0040002c 280d00b9 str w8, [x9, #0xc {var_14}] {0x10c}
The actual stack layout is the same. But for some reason BN treats these differently.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by loading test.zip's optimized.o and unoptimized.o in Binary Ninja, creating go, and applying the stated type to test. Compare the generated call arguments and trace how the Apple arm64 ABI handles packed 32-bit stack arguments; done means both binaries display the twelve expected arguments, including sp0 through spC.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- reverse-engineering
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100