High incident rate of "wrong function frame" errors

I really don’t know if this is the result of previous ignorance or that my RE focus has shifted, but I feel like the rate at which hexrays errors out with ‘incorrect function frame’ seems to be rather high with IDA 9.2.

Supporting most of my cases, I wrote this script to auto-fix function frames:

import idc
import idautils
import ida_frame
import ida_funcs

def analyze_frame(ea):
    func = ida_funcs.get_func(ea)
    if not func:
        return None
    
    locals_size = 0
    gpr_count = 0
    xmm_count = 0
    
    gprs = {'rbp','rbx','rsi','rdi','r12','r13','r14','r15'}
    
    for head in idautils.Heads(func.start_ea, min(func.start_ea + 0x100, func.end_ea)):
        mnem = idc.print_insn_mnem(head)
        op0 = idc.print_operand(head, 0)
        op1 = idc.print_operand(head, 1)
        
        # Count GPR pushes
        if mnem == 'push' and op0.lower() in gprs:
            gpr_count += 1
        
        # Count XMM saves (vmovaps [...], xmmN)
        if mnem == 'vmovaps' and 'xmm' in op1.lower() and '[' in op0:
            xmm_count += 1
        
        # Pattern 1: lea rbp, [rax - LOCALS] or lea rbp, [rsp + X]
        if mnem == 'lea' and 'rbp' in op0.lower():
            # Extract the constant
            if '-' in op1:
                # lea rbp, [rax-0D8h] → locals = 0xD8
                val = op1.split('-')[-1].rstrip('h]').strip()
                locals_size = int(val, 16)
                break
            elif '+' in op1 and 'rsp' in op1.lower():
                # lea rbp, [rsp+80h] → need post-AND [rbp+NNNN+var] pattern
                # Check for AND alignment next
                continue
        
        # Pattern 2: Check for aligned frame, extract from var access
        if mnem == 'and' and 'rbp' in op0.lower() and 'FFFFFFE0' in op1.upper():
            # AVX-aligned frame - scan forward for [rbp+NNNN+var_XXX]
            for h2 in idautils.Heads(head, min(head + 0x50, func.end_ea)):
                disasm = idc.generate_disasm_line(h2, 0)
                if 'rbp+' in disasm and 'var_' in disasm:
                    # Extract NNNN from [rbp+NNNNh+var_XXX]
                    import re
                    match = re.search(r'\[rbp\+([0-9A-Fa-f]+)h\+var_', disasm)
                    if match:
                        locals_size = int(match.group(1), 16)
                        break
            break
    
    saved_regs = (gpr_count * 8) + (xmm_count * 16)
    
    return {
        'locals': locals_size,
        'saved_regs': saved_regs,
        'gpr_count': gpr_count,
        'xmm_count': xmm_count
    }

def fix_frame(ea):
    result = analyze_frame(ea)
    if not result:
        print("Failed to analyze")
        return
    
    print(f"Locals: 0x{result['locals']:X}")
    print(f"Saved: 0x{result['saved_regs']:X} ({result['gpr_count']} GPRs, {result['xmm_count']} XMMs)")
    
    func = ida_funcs.get_func(ea)
    ida_frame.del_frame(func)
    ida_frame.add_frame(func, result['locals'], result['saved_regs'], 0)
    print("Frame fixed")

# Usage:
# i.e. fix_frame(0x146CD3840)

What breaks IDA’s ability to do this properly?

It is hard to say without seeing sample files, could you send some?
Our logic to create frames is much more complex because it handles many other cases. If your script works for your case, great!

Here’s the i64 file with one function (the __Step function) that’s triggering a incorrect frame error extracted via “extract function”.

That was a bit annoying … I can’t upload files larger than 10MB and I couldn’t use the proper part file names (if you try to decompress, rename the files from xxx.00y.7z to xxx.7z.00y and 7z unpack the 001 file.

game-dump-for-hexrays.002.7z (5.4 MB)
game-dump-for-hexrays.001.7z (9 MB)

I also uploaded the minimal, extracted i64, and the original game binary to my server:

Note that the game.exe will require a rather long analysis, which is why I also provided the i64 file.

there is a known issue in 9.2 where sometimes the special member __return_addressis missing from the frame, which causes the decompiler error. It will be fixed in 9.3.

1 Like

For now you can workaround this issue by doing MakeUnknown then MakeFunction (bound to U and P by default respectively) on the offending function’s beginning address. This seems to work fine but today I had a weird function analysis issue on a rather simple DLL, so this may still be a bit broken in some cases.

Is this fixed in the IDA 9.3 Beta?

yes, normally the fix should be present in b1 (please report if not).