Skip to content

x86-16 afva does not reconcile typed BP arguments after changing the calling convention #26760

Description

@andreibelov

Environment

$ date
r2 -v
uname -ms
Fri 18 Sep 01:14:14 BST 2026
radare2 6.2.2 +37545 abi:142 @ linux-x86_64
birth: git.6.2.2 2026-09-17__17:41:32
commit: ad27058877024389292fddf12e1db6e13824ba34
options: gpl -O? cs:5 cl:2 make
Linux x86_64

Description

On x86-16, the default calling convention is fastcall.
If a function is first analysed with af, radare2 recovers a generic BP-relative argument under that convention.
Changing the function to cdecl afterwards and running afva does not reconcile the existing autogenerated variable with the known typed prototype.

The function signature is correct: int typed16 (short value);
but the recovered variable remains generic: arg int16_t arg_4h @ bp+0x4

The expected variable is:

arg short value @ bp+0x4

Repeated afva calls leave the stale name and type unchanged.

This was found while testing #26747 after the typed-function recovery change.

Further debugging shows that the 2-byte slot calculation is correct: during cdecl recovery, ptr=4, bp_off=2, and frame_off=2. The problem is that the existing generic variable causes recovery to return before the prototype argument can be applied.

This appears related to, but distinct from, #19595. #19595 reports a false-positive ax register argument in x86-16 analysis; this report is about stale BP-variable metadata after changing the function calling convention.

Potential area of change

When a function's calling convention changes, invalidate or reconcile autogenerated argument variables.
For typed functions, afva could replace a default arg_* name/type when the variable's recovered storage slot matches a
declared prototype argument, while continuing to preserve explicitly renamed or retyped variables.

Test

The test uses a conventional 16-bit frame:

push bp
mov bp, sp
sub sp, 2
mov ax, word [bp+4]
mov word [bp-2], ax
leave
ret

Minimal reproduction:

r2 -NN -q -a x86 -b 16 malloc://32
wx 5589e583ec028b46048946fec9c3
f typed16
td int typed16(short value);
af
afc cdecl
afva
afcf
afvb

Actual output:

int typed16 (short value);
arg int16_t arg_4h @ bp+0x4
var int16_t var_2h @ bp-0x2

Running afva again produces the same result.

Control

If variable recovery is disabled until after the correct calling convention is
selected, the prototype is mapped correctly:

wx 5589e583ec028b46048946fec9c3
f typed16
td int typed16(short value);
e anal.vars=false
af
afc cdecl
e anal.vars=true
afva
afcf
afvb

Output:

int typed16 (short value);
arg short value @ bp+0x4
var int16_t var_2h @ bp-0x2

Deleting the recovered variables before retrying also works:

afc cdecl
afv-*
afva

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions