Skip to content

Fix decompiler crash on stack-returned values in c_return_as_atoms#27

Open
vietd0x wants to merge 1 commit into
sefcom:masterfrom
vietd0x:fix/c_return_as_atoms-stack-return
Open

Fix decompiler crash on stack-returned values in c_return_as_atoms#27
vietd0x wants to merge 1 commit into
sefcom:masterfrom
vietd0x:fix/c_return_as_atoms-stack-return

Conversation

@vietd0x
Copy link
Copy Markdown

@vietd0x vietd0x commented May 29, 2026

Summary

FunctionHandler.c_return_as_atoms calls Atom.from_argument(...) without a stack pointer. When a callee's recovered return value maps to a SimStackArg (e.g. a value returned on the stack), Atom.from_argument raises:

ValueError: You must provide a stack pointer to translate a SimStackArg

This propagates out of the ReachingDefinitionsAnalysis (invoked via CompleteCallingConventions / the decompiler). Under the decompiler's resilience mode the exception is swallowed, decompilation aborts, and Decompiler.codegen ends up None — so the function silently fails to decompile.

The sibling helper c_args_as_atoms already handles this correctly: it resolves sp from the state and wraps the call in try/except ValueError: continue. This PR makes c_return_as_atoms consistent with it.

Root cause

c_return_as_atoms (in angr/analyses/reaching_definitions/function_handler.py):

return {
    Atom.from_argument(footprint_arg, state.arch, full_reg=True)  # no sp, no try/except
    for footprint_arg in retval.get_footprint()
}

Fix

Resolve sp from the state and skip footprint args that can't be translated, mirroring c_args_as_atoms:

sp_value = state.get_one_value(Register(state.arch.sp_offset, state.arch.bytes), strip_annotations=True)
sp = state.get_stack_offset(sp_value) if sp_value is not None else None
atoms = set()
for footprint_arg in retval.get_footprint():
    try:
        atoms.add(Atom.from_argument(footprint_arg, state.arch, full_reg=True, sp=sp))
    except ValueError:
        continue
return atoms

Behavior impact

  • Register-returned values (the common case): unchanged — SimRegArg ignores sp, and no ValueError is raised, so output is identical.
  • Stack-returned values: previously crashed (and killed decompilation); now produce the correct MemoryLocation atom when sp is resolvable, or are skipped gracefully when it isn't — exactly the strategy c_args_as_atoms already uses for arguments.

Test plan

  • Reproduced the crash on a real (stripped) Rust binary: decompilation produced codegen=None with two swallowed SimStackArg ValueErrors; fail_fast=True pinpointed the call site at c_return_as_atoms.
  • After the fix, the same function decompiles successfully with no errors.

🤖 Generated with Claude Code

c_return_as_atoms called Atom.from_argument without a stack pointer, so
any callee whose return value mapped to a SimStackArg raised
"You must provide a stack pointer to translate a SimStackArg". Under the
decompiler's resilience mode this silently aborted decompilation,
leaving codegen=None.

Mirror the existing c_args_as_atoms handling: resolve sp from the state
and skip stack args that can't be translated. Register-returned values
(the common case) are unaffected since SimRegArg ignores sp.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@bluesadi
Copy link
Copy Markdown
Collaborator

bluesadi commented May 29, 2026

Hi,

Thanks for summitting this PR! Could you please move it to angr/angr instead? Oxidizer has already been merged into angr and this repo is supposed to be archieved.

Best

@ltfish
Copy link
Copy Markdown
Collaborator

ltfish commented May 30, 2026

@bluesadi You probably want to make it clear in README.md :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants