Skip to content

Jump optimization can cause use-after-free when using label references #424

@asumagic

Description

@asumagic

Cleared up trace from clang's address sanitizer:

=================================================================
==250880==ERROR: AddressSanitizer: heap-use-after-free on address 0x7b92d87e8848 at pc 0x55c882678ee3 bp 0x7fffdf2611d0 sp 0x7fffdf2611c8
READ of size 8 at 0x7b92d87e8848 thread T0
    #0 0x55c882678ee2 in get_label_disp mir/mir-gen.c:768:3
    #1 0x55c882678ee2 in gen_setup_lrefs mir/mir-gen.c:784:30
    #2 0x55c882678ee2 in target_rebase mir/mir-gen-x86_64.c:3010:3
    #3 0x55c882678ee2 in generate_func_code mir/mir-gen.c:9504:5

0x7b92d87e8848 is located 24 bytes inside of 80-byte region [0x7b92d87e8830,0x7b92d87e8880)
freed by thread T0 here:
    #0 0x55c882467e3d in free.part.0 (/home/sdelang/projects/src/cpp/angelsea/build/tests/angelsea-tests+0x3d2e3d) (BuildId: d085633f284604194c7ba20e446c77c67605fa02)
    #1 0x55c8826bd038 in gen_delete_insn mir-gen.c:696:3
    #2 0x55c8826bd038 in remove_bb mir/mir-gen.c:4212:5

Disabling jump optimization from the pipeline works around the issue.

This C source reproduces the issue:

typedef __INT8_TYPE__    asINT8;
typedef __INT16_TYPE__   asINT16;
typedef __INT32_TYPE__   asINT32;
typedef __INT64_TYPE__   asINT64;
typedef __UINT8_TYPE__   asBYTE;
typedef __UINT16_TYPE__  asWORD;
typedef __UINT32_TYPE__  asUINT;
typedef __UINT32_TYPE__  asDWORD;
typedef __UINT64_TYPE__  asQWORD;
typedef __UINTPTR_TYPE__ asPWORD;

#define asASSERT

#if __SIZEOF_POINTER__ == 4
        #define AS_PTR_SIZE 1
#else
        #define AS_PTR_SIZE 2
#endif

#define VALUE_OF_BOOLEAN_TRUE 1

typedef enum
{
        asEXECUTION_FINISHED        = 0,
        asEXECUTION_SUSPENDED       = 1,
        asEXECUTION_ABORTED         = 2,
        asEXECUTION_EXCEPTION       = 3,
        asEXECUTION_PREPARED        = 4,
        asEXECUTION_UNINITIALIZED   = 5,
        asEXECUTION_ACTIVE          = 6,
        asEXECUTION_ERROR           = 7,
        asEXECUTION_DESERIALIZATION = 8
} asEContextState;

typedef union {
        asINT8 as_asINT8;
        asINT16 as_asINT16;
        asINT32 as_asINT32;
        asINT64 as_asINT64;
        asBYTE as_asBYTE;
        asWORD as_asWORD;
        asDWORD as_asDWORD;
        asQWORD as_asQWORD;
        asPWORD as_asPWORD;
        float as_float;
        double as_double;
        void* as_ptr;
} asea_var;

typedef struct asSVMRegisters asSVMRegisters;
typedef struct asIScriptContext asIScriptContext;
typedef struct asITypeInfo asITypeInfo;
typedef struct asCScriptFunction asCScriptFunction;
typedef struct asCObjectType asCObjectType;
typedef struct asSTypeBehaviour asSTypeBehaviour;

typedef struct {        asDWORD *programPointer;
        void *stackFramePointer;
        void *stackPointer;
        asea_var valueRegister;
        void *objectRegister;
        asITypeInfo *objectType;
        char doProcessSuspend;
        asIScriptContext *ctx;
} asea_vm_registers;

typedef union { float f; asDWORD i; } asea_i2f;
void asea_call_script_function(asSVMRegisters* vm_registers, void* function);
void asea_debug_message(asSVMRegisters* vm_registers, const char* text);
void asea_set_internal_exception(asSVMRegisters* vm_registers, const char* text);
float asea_fmodf(float a, float b);
float asea_fmod(float a, float b);

#define ASEA_STACK_DWORD_OFFSET(base, dword_offset) (void*)((char*)(base) + ((dword_offset) * 4))
#define ASEA_FRAME_VAR(dword_offset) (*(asea_var*)(ASEA_STACK_DWORD_OFFSET(l_fp, -(dword_offset))))
#define ASEA_STACK_VAR(dword_offset) (*(asea_var*)(ASEA_STACK_DWORD_OFFSET(l_sp, (dword_offset))))
#define ASEA_STACK_TOP (*(asea_var*)(l_sp))
#define ASEA_VALUEREG_DEREF() (*(asea_var*)(regs->valueRegister.as_ptr))

/* start of code generated by angelsea bytecode2c */
/* /home/sdelang/projects/src/cpp/angelsea/tests/scripts/switch.as:3:1: void switch_test(int i) */
void asea_jit_mod0_fn1(asSVMRegisters *_regs, asPWORD entryLabel) {
        asea_vm_registers *regs = (asea_vm_registers *)_regs;
        asDWORD *l_bc;
        void *l_sp;
        void *l_fp;
                l_bc = regs->programPointer;
                l_sp = regs->stackPointer;
                l_fp = regs->stackFramePointer;
        static const void *const entry[] = { &&bc0,
                &&bc0,
                &&bc30,
                &&bc41,
                &&bc49,
                &&bc60,
                &&bc68,
                &&bc79,
                &&bc93,
                &&bc101,
                &&bc112,
                &&bc120,
                &&bc131,
                &&bc139,
                &&bc150,
        };
        goto *entry[entryLabel];

        /* bytecode: JitEntry 1 */
        bc0: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc3: {
                l_bc += 3;
        }
        /* bytecode: CMPIi 0 5 */
        bc6: {
                asINT32 lhs = ASEA_FRAME_VAR(0).as_asINT32;
                asINT32 rhs = 5;
                if      (lhs == rhs) regs->valueRegister.as_asINT32 = 0;
                else if (lhs < rhs)  regs->valueRegister.as_asINT32 = -1;
                else                 regs->valueRegister.as_asINT32 = 1;
                l_bc += 2;
        }
        /* bytecode: JP 129 */
        bc8: {
                if( regs->valueRegister.as_asINT32 > 0 ) {
                        l_bc += 131;
                        goto bc139;
                } else {
                        l_bc += 2;
                }
        }
        /* bytecode: CMPIi 0 0 */
        bc10: {
                asINT32 lhs = ASEA_FRAME_VAR(0).as_asINT32;
                asINT32 rhs = 0;
                if      (lhs == rhs) regs->valueRegister.as_asINT32 = 0;
                else if (lhs < rhs)  regs->valueRegister.as_asINT32 = -1;
                else                 regs->valueRegister.as_asINT32 = 1;
                l_bc += 2;
        }
        /* bytecode: JS 125 */
        bc12: {
                if( regs->valueRegister.as_asINT32 < 0 ) {
                        l_bc += 127;
                        goto bc139;
                } else {
                        l_bc += 2;
                }
        }
        /* bytecode: SUBIi 1 0 0 */
        bc14: {
                asINT32 lhs = ASEA_FRAME_VAR(0).as_asINT32;
                ASEA_FRAME_VAR(1).as_asINT32 = lhs - (0);
                l_bc += 3;
        }
        /* bytecode: JMPP 1 */
        bc17: {
                regs->programPointer = l_bc;
                regs->stackPointer = l_sp;
                regs->stackFramePointer = l_fp;
                return; /* unsupported instruction */
        }
        /* bytecode: JMP 10 */
        bc18: {
                l_bc += 12;
                goto bc30;
        }
        /* bytecode: JMP 27 */
        bc20: {
                l_bc += 29;
                goto bc49;
        }
        /* bytecode: JMP 44 */
        bc22: {
                l_bc += 46;
                goto bc68;
        }
        /* bytecode: JMP 56 */
        bc24: {
                l_bc += 58;
                goto bc82;
        }
        /* bytecode: JMP 73 */
        bc26: {
                l_bc += 75;
                goto bc101;
        }
        /* bytecode: JMP 90 */
        bc28: {
                l_bc += 92;
                goto bc120;
        }
        /* bytecode: JitEntry 2 */
        bc30: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc33: {
                l_bc += 3;
        }
        /* bytecode: PGA 136673144758984 */
        bc36: {
                extern void* asea_jit_mod1_str0;
                l_sp = ASEA_STACK_DWORD_OFFSET(l_sp, -AS_PTR_SIZE);
                ASEA_STACK_TOP.as_asPWORD = (asPWORD)&asea_jit_mod1_str0;
                l_bc += 3;
        }
        /* bytecode: CALLSYS print # void print(const string&in) */
        bc39: {
                regs->programPointer = l_bc;
                regs->stackPointer = l_sp;
                regs->stackFramePointer = l_fp;
                return; /* unsupported instruction */
        }
        /* bytecode: JitEntry 3 */
        bc41: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc44: {
                l_bc += 3;
        }
        /* bytecode: JMP 104 */
        bc47: {
                l_bc += 106;
                goto bc153;
        }
        /* bytecode: JitEntry 4 */
        bc49: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc52: {
                l_bc += 3;
        }
        /* bytecode: PGA 136673144760424 */
        bc55: {
                extern void* asea_jit_mod1_str1;
                l_sp = ASEA_STACK_DWORD_OFFSET(l_sp, -AS_PTR_SIZE);
                ASEA_STACK_TOP.as_asPWORD = (asPWORD)&asea_jit_mod1_str1;
                l_bc += 3;
        }
        /* bytecode: CALLSYS print # void print(const string&in) */
        bc58: {
                regs->programPointer = l_bc;
                regs->stackPointer = l_sp;
                regs->stackFramePointer = l_fp;
                return; /* unsupported instruction */
        }
        /* bytecode: JitEntry 5 */
        bc60: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc63: {
                l_bc += 3;
        }
        /* bytecode: JMP 85 */
        bc66: {
                l_bc += 87;
                goto bc153;
        }
        /* bytecode: JitEntry 6 */
        bc68: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc71: {
                l_bc += 3;
        }
        /* bytecode: PGA 136673144761960 */
        bc74: {
                extern void* asea_jit_mod1_str2;
                l_sp = ASEA_STACK_DWORD_OFFSET(l_sp, -AS_PTR_SIZE);
                ASEA_STACK_TOP.as_asPWORD = (asPWORD)&asea_jit_mod1_str2;
                l_bc += 3;
        }
        /* bytecode: CALLSYS print # void print(const string&in) */
        bc77: {
                regs->programPointer = l_bc;
                regs->stackPointer = l_sp;
                regs->stackFramePointer = l_fp;
                return; /* unsupported instruction */
        }
        /* bytecode: JitEntry 7 */
        bc79: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc82: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc85: {
                l_bc += 3;
        }
        /* bytecode: PGA 136673144763016 */
        bc88: {
                extern void* asea_jit_mod1_str3;
                l_sp = ASEA_STACK_DWORD_OFFSET(l_sp, -AS_PTR_SIZE);
                ASEA_STACK_TOP.as_asPWORD = (asPWORD)&asea_jit_mod1_str3;
                l_bc += 3;
        }
        /* bytecode: CALLSYS print # void print(const string&in) */
        bc91: {
                regs->programPointer = l_bc;
                regs->stackPointer = l_sp;
                regs->stackFramePointer = l_fp;
                return; /* unsupported instruction */
        }
        /* bytecode: JitEntry 8 */
        bc93: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc96: {
                l_bc += 3;
        }
        /* bytecode: JMP 52 */
        bc99: {
                l_bc += 54;
                goto bc153;
        }
        /* bytecode: JitEntry 9 */
        bc101: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc104: {
                l_bc += 3;
        }
        /* bytecode: PGA 136673144764552 */
        bc107: {
                extern void* asea_jit_mod1_str4;
                l_sp = ASEA_STACK_DWORD_OFFSET(l_sp, -AS_PTR_SIZE);
                ASEA_STACK_TOP.as_asPWORD = (asPWORD)&asea_jit_mod1_str4;
                l_bc += 3;
        }
        /* bytecode: CALLSYS print # void print(const string&in) */
        bc110: {
                regs->programPointer = l_bc;
                regs->stackPointer = l_sp;
                regs->stackFramePointer = l_fp;
                return; /* unsupported instruction */
        }
        /* bytecode: JitEntry 10 */
        bc112: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc115: {
                l_bc += 3;
        }
        /* bytecode: JMP 33 */
        bc118: {
                l_bc += 35;
                goto bc153;
        }
        /* bytecode: JitEntry 11 */
        bc120: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc123: {
                l_bc += 3;
        }
        /* bytecode: PGA 136673144766664 */
        bc126: {
                extern void* asea_jit_mod1_str5;
                l_sp = ASEA_STACK_DWORD_OFFSET(l_sp, -AS_PTR_SIZE);
                ASEA_STACK_TOP.as_asPWORD = (asPWORD)&asea_jit_mod1_str5;
                l_bc += 3;
        }
        /* bytecode: CALLSYS print # void print(const string&in) */
        bc129: {
                regs->programPointer = l_bc;
                regs->stackPointer = l_sp;
                regs->stackFramePointer = l_fp;
                return; /* unsupported instruction */
        }
        /* bytecode: JitEntry 12 */
        bc131: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc134: {
                l_bc += 3;
        }
        /* bytecode: JMP 14 */
        bc137: {
                l_bc += 16;
                goto bc153;
        }
        /* bytecode: JitEntry 13 */
        bc139: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc142: {
                l_bc += 3;
        }
        /* bytecode: PGA 136673144768776 */
        bc145: {
                extern void* asea_jit_mod1_str6;
                l_sp = ASEA_STACK_DWORD_OFFSET(l_sp, -AS_PTR_SIZE);
                ASEA_STACK_TOP.as_asPWORD = (asPWORD)&asea_jit_mod1_str6;
                l_bc += 3;
        }
        /* bytecode: CALLSYS print # void print(const string&in) */
        bc148: {
                regs->programPointer = l_bc;
                regs->stackPointer = l_sp;
                regs->stackFramePointer = l_fp;
                return; /* unsupported instruction */
        }
        /* bytecode: JitEntry 14 */
        bc150: {
                l_bc += 3;
        }
        /* bytecode: JitEntry 0 */
        bc153: {
                l_bc += 3;
        }
        /* bytecode: RET 1 */
        bc156: {
                regs->programPointer = l_bc;
                regs->stackPointer = l_sp;
                regs->stackFramePointer = l_fp;
                return; /* unsupported instruction */
        }
}

void *asea_jit_mod1_str0 = 0;
void *asea_jit_mod1_str1 = 0;
void *asea_jit_mod1_str2 = 0;
void *asea_jit_mod1_str3 = 0;
void *asea_jit_mod1_str4 = 0;
void *asea_jit_mod1_str5 = 0;
void *asea_jit_mod1_str6 = 0;

int main() {
    asea_jit_mod0_fn1(0, 1);
}

This is a pretty bad repro that will actually just segfault on running either way, but when running with -O2, it will crash in gen_setup_lrefs. Can spend some time minifying a functional repro like with #423 if there is interest.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions