Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xabort instruction terminates the control flow in certain conditions #6222

Open
xusheng6 opened this issue Dec 3, 2024 · 5 comments
Open
Assignees
Labels
Component: Core Issue needs changes to the core Effort: Medium Issue should take < 1 month Impact: Low Issue is a papercut or has a good, supported workaround Type: Bug Issue is a non-crashing bug with repro steps
Milestone

Comments

@xusheng6
Copy link
Member

xusheng6 commented Dec 3, 2024

While looking at mandiant/capa#2406, I noticed a very strange issue that the number of basic blocks of a function can be reported different in certain conditions (yet unknown).

To start with, due to mandiant/capa#2516, the capa is getting the list of basic blocks of each function twice instead of once. And for function 0x5494e0, when it pulls the list of basic blocks, binja reports 13 basic blocks for the first time, but only one basic block in the second time. If I open the binary in binja GUI, i also see 13 functions. It is yet unclear why we would report only one functions in the second time.

But the basic block is indeed unusual, it contains an xabort instruction. And as can be seen in the below screenshot, for some reason we split the code at 0x5494e7 (which is immediately after the xabort instruciotn) into a new basic block. So maybe xabort somehow terminates the basic block? And in the second time we query the list of basic blocks, maybe certain analysis info is not yet ready, so the function does not contain other basic blocks.

Screenshot 2024-12-03 at 4 30 48 PM

2f7f5fb5de175e770d7eae87666f9831.elf_.zip

@xusheng6
Copy link
Member Author

xusheng6 commented Dec 4, 2024

get_instruction_info returns an UnresolvedBranch for xabort instruction, which might be relevant to the issue

@xusheng6 xusheng6 self-assigned this Dec 5, 2024
@xusheng6 xusheng6 added this to the Gallifrey milestone Dec 5, 2024
@xusheng6
Copy link
Member Author

xusheng6 commented Dec 5, 2024

I somehow remember this also affects the debugger -- I see that there is only one basic block in the graph view, but re-analyzing the function gives me back all of the expected blocks.

I am suspecting that in certain conditions, the analysis update removes the CFG info but does not recalculate it, leading to an incomplete CFG

@xusheng6
Copy link
Member Author

xusheng6 commented Dec 6, 2024

This can be reproduced by the following script:

from binaryninja import *

bv = load('/Users/xusheng/Downloads/2f7f5fb5de175e770d7eae87666f9831.elf_.bndb')
func = bv.get_function_at(0x5494e0)
print(func.mlil)
print(len(func.basic_blocks))

for func in bv.functions:
    if func.start == 0x5494e0:
        continue
    try:
        print(func.mlil)
    except:
        pass

func = bv.get_function_at(0x5494e0)
print(func.mlil)
print(len(func.basic_blocks))

The last line print(len(func.basic_blocks)) will print 1 instead of 13.

I have to say this is the most mysterious bug that I have seen. The second print(func.mlil) at line 17 is important -- if we remove it, it will print 13 (correct) instead of just 1.

This bug is probably a combination of cache eviction and analysis update, which get the function into a weird state, and the indirect branch info needed by the basic block analysis is missing. The indirect branch info is used by the analysis to continue the control flow at 0x5494e7

@xusheng6
Copy link
Member Author

xusheng6 commented Dec 6, 2024

Well, it seems the operation of requesting the MLIL causes the indirect_branches to be cleared and not populated after that:

from binaryninja import *

bv = load('/Users/xusheng/Downloads/2f7f5fb5de175e770d7eae87666f9831.elf_.bndb')
func = bv.get_function_at(0x5494e0)
print(func.indirect_branches)
print(func.mlil)
print(func.indirect_branches)

This prints

[<branch x86_64:0x5494e4 -> x86_64:0x5494e7>, <branch x86_64:0x54952e -> x86_64:0x549531>]
<MediumLevelILFunction: x86_64@0x5494e0>
[]

@xusheng6
Copy link
Member Author

xusheng6 commented Dec 6, 2024

@xusheng6 xusheng6 added Type: Bug Issue is a non-crashing bug with repro steps Component: Core Issue needs changes to the core Impact: Low Issue is a papercut or has a good, supported workaround Effort: Medium Issue should take < 1 month labels Dec 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: Core Issue needs changes to the core Effort: Medium Issue should take < 1 month Impact: Low Issue is a papercut or has a good, supported workaround Type: Bug Issue is a non-crashing bug with repro steps
Projects
None yet
Development

No branches or pull requests

1 participant