avast / avast/retdec

Incorrect inference of stack variable size

Open
#452 2 comments 0 reactions 1 assignee Claimed by @xkubov View on GitHub
bug C-bin2llvmir P-output
Dominant language
C++
Stars
8.6k
Forks
1k
PR merge metrics
No merged PRs in 30d

Description

There are already some open issues related to decompilation quality, but I haven't seen any that targets this problem specifically.

Please consider the function below (I will try to produce a more minimal example if needed, but this has the advantage of being real-world code)

```
// Address range: 0x61a2b4 - 0x61a3b1
int32_t entry_point(int32_t a1) {
int32_t v1 = __readfsdword(0); // bp-20
__writefsdword(0, (int32_t)&v1);
uint32_t v2 = GetVersion(); // 0x61a2da
int32_t v3 = v2 / 256 & 255; // 0x61a2e2
g7 = v3;
int32_t v4 = v2 & 255; // 0x61a2ec
g6 = v4;
g5 = v3 | 256 * v4;
g4 = v2 / 0x10000;
if (function_620f5a(1) == 0) {
// 0x61a317
function_61a3e1(28);
// UNREACHABLE
}
// 0x61a31f
if (function_61d29c() == 0) {
// 0x61a328
function_61a3e1(16);
// UNREACHABLE
}
int32_t lpModuleName = 0; // esi
function_620856(0);
g9 = GetCommandLineA();
g8 = (char *)function_6225b0();
function_622363();
function_6222aa();
function_618ded();
int32_t lpStartupInfo = 0; // bp-96
GetStartupInfoA((struct _STARTUPINFOA *)&lpStartupInfo);
function_622252();
if ((lpModuleName & 1) == 0) {
// 0x61a37f
// branch -> 0x61a382
}
int32_t * moduleHandle = GetModuleHandleA((char *)lpModuleName); // 0x61a388
function_618e1a(function_4f7a30((int32_t)moduleHandle));
int32_t v5;
return function_622031(*(int32_t *)*(int32_t *)v5, (struct _EXCEPTION_POINTERS *)v5);
}
```

The point of pain is `lpStartupInfo` being declared as `int32_t`, while it is clearly meant to be a `struct _STARTUPINFOA`. It would seem that bin2llvmir wasn't able to correctly infer the size of this stack variable.
Now, I don't want to just talk, I would like to actually contribute work towards fixing this issue. But I am not sure where to start.
- Should RetDec be aware of GetStartupInfoA's semantics? In other words, should RetDec contain a database that will say *function X writes N bytes starting at the address pointed by argument M*? That would be helpful for public APIs, but doesn't do anything for functions whose semantics are not known a priori (which are basically all non-imported functions).
- If we did have this kind of information, at which point would it need to be utilized? The information about size of `lpStartupInfo` needs to be encoded in the IR, (in the case above, we incorrectly get `%stack_var_-96 = alloca i32, align 4`)
Does bin2llvmir currently do enough high-level analysis to even "recognize" that lpStartupInfo is being passed to GetStartupInfoA?
- A completely different way would instead be to make more effort at preserving the original stack layout. Wherever there seems to be a gap in the stack frame, extend the previous variable or make it an array/whatever. In a way, this is what Hex-Rays does. But again, I am unsure to what precision does LLVM IR allow one to express the stack layout, or whether it would be sufficient.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.