fkie-cad / dewolf

A research decompiler implemented as a Binary Ninja plugin.
GNU Lesser General Public License v2.1
172 stars 9 forks source link

RuntimeError: did not expect to reach canary check this way in remove_stack_canary #130

Open jnhols opened 1 year ago

jnhols commented 1 year ago

What happened?

The decompiler crashes with a RuntimeError in remove_stack_canary during preprocessing.

Traceback (most recent call last):
    File ""/home/ubuntu/.binaryninja/plugins/dewolf/decompile.py"", line 80, in <module>
main(Decompiler)
    File ""/home/ubuntu/.binaryninja/plugins/dewolf/decompiler/util/commandline.py"", line 65, in main
task = decompiler.decompile(function_name, options)
    File ""/home/ubuntu/.binaryninja/plugins/dewolf/decompile.py"", line 55, in decompile
pipeline.run(task)
    File ""/home/ubuntu/.binaryninja/plugins/dewolf/decompiler/pipeline/pipeline.py"", line 97, in run
instance.run(task)
    File ""/home/ubuntu/.binaryninja/plugins/dewolf/decompiler/pipeline/preprocessing/remove_stack_canary.py"", line 24, in run
self._patch_canary(fail_node)
    File ""/home/ubuntu/.binaryninja/plugins/dewolf/decompiler/pipeline/preprocessing/remove_stack_canary.py"", line 49, in _patch_canary
self._patch_branch_condition(pred)
    File ""/home/ubuntu/.binaryninja/plugins/dewolf/decompiler/pipeline/preprocessing/remove_stack_canary.py"", line 62, in _patch_branch_condition
raise RuntimeError(""did not expect to reach canary check this way"")
RuntimeError: did not expect to reach canary check this way

How to reproduce?

Decompile size_opt in uniq or main in one of the other samples given below.

remove_stack_canary_runtime_error.zip

Affected Binary Ninja Version(s)

3.2.3814

NeoQuix commented 1 year ago

All failed methods have a control flow where __stack_chk_fail will be called after error(1,..) in the cfg.

This control flow is not correct, because with specific optimization levels (min binary; -O1, -Os), the compiler knows/accounts for that error(1,...) will not return. Therefore __stack_chk_fail will not be called even if there is no assembly instruction between them.

Simple code + binary to reproduce:

#include <stdio.h>
#include <error.h>

int main(int argc){
    if(argc < 100){
        argc = 252;
    }else{
        error(1, 0, "Error %d", 1);
    }
    return 0;
}

Compiled with gcc -O1 -fstack-protector-all to force a stack canary. a.out.zip

NeoQuix commented 1 year ago

Still a bninja upstream issue. The outgoing_edges for the basic block with error are still wrong.