From 8b5ab1bb0141e66887bfbc4051688a3e478701a2 Mon Sep 17 00:00:00 2001 From: Benjamin Oldenburg Date: Sat, 4 Apr 2026 21:29:28 +0700 Subject: [PATCH] arm64: reject invalid asm and clean run argv --- arm64-asm.c | 51 ++++++++--- arm64-tok.h | 2 - tccrun.c | 15 +++ tests/tests2/139_arm64_errors.c | 28 ++++++ tests/tests2/139_arm64_errors.expect | 16 +++- win32/lib/crt1.c | 24 ++--- win32/lib/wincrt1.c | 36 +++++++- win32/test_run_arg_cleanup.c | 131 +++++++++++++++++++++++++++ 8 files changed, 269 insertions(+), 34 deletions(-) create mode 100644 win32/test_run_arg_cleanup.c diff --git a/arm64-asm.c b/arm64-asm.c index 2f04c8e2..2e02909b 100644 --- a/arm64-asm.c +++ b/arm64-asm.c @@ -825,6 +825,15 @@ static int is_valid_movw_imm(int64_t val) return 0; } +static int is_valid_movw_shift(int shift, int is_64bit) +{ + if (shift < 0 || (shift & 15)) + return 0; + if (shift > (is_64bit ? 48 : 16)) + return 0; + return 1; +} + static int arm64_memory_is_base_only(const SValue *sv) { int r; @@ -1278,8 +1287,7 @@ static void asm_data_proc(TCCState *s1, int token) opcode = ARM64_EOR_REG; break; case TOK_ASM_mul: - case TOK_ASM_muls: - opcode = token == TOK_ASM_mul ? ARM64_MUL_REG : ARM64_MULS_REG; + opcode = ARM64_MUL_REG; break; default: tcc_error("unsupported data processing instruction"); @@ -1333,15 +1341,16 @@ static void asm_data_proc(TCCState *s1, int token) return; } rm = op3.reg; - if (is_64bit != !!(op2.reg_type & REG_X) || is_64bit != !!(op3.reg_type & REG_X)) + if (is_64bit != !!(op2.reg_type & REG_X) || is_64bit != !!(op3.reg_type & REG_X)) { tcc_error("mismatched register widths"); + return; + } gen_dp_reg(opcode, rd, rn, rm, is_64bit); } } else if (op2.type & OP_IM) { tcc_error("missing source register for immediate form"); } else { - is_64bit = (op1.reg_type & REG_X); - gen_mov_reg(rd, rn, is_64bit); + tcc_error("missing third operand"); } } @@ -1664,18 +1673,37 @@ static void asm_move_wide(TCCState *s1, int token) if (tok == ',') next(); parse_operand(s1, &op2); + if (!(op1.type & OP_REG)) { + tcc_error("expected register in first operand"); + return; + } + if (!(op2.type & OP_IM) || op2.e.sym) { + tcc_error("expected immediate in second operand"); + return; + } + rd = op1.reg; is_64bit = (op1.reg_type & REG_X); - imm = op2.e.v & 0xFFFF; + if ((uint64_t)op2.e.v > 0xFFFF) { + tcc_error("move wide immediate out of range"); + return; + } + imm = op2.e.v; if (tok == ',') { next(); - if (tok == TOK_ASM_lsl) { - next(); - if (tok == '#') next(); - asm_expr(s1, &op2.e); - shift = (int)op2.e.v / 16; + if (tok != TOK_ASM_lsl) { + tcc_error("move wide shift must use lsl"); + return; } + next(); + if (tok == '#') next(); + asm_expr(s1, &op2.e); + if (op2.e.sym || !is_valid_movw_shift((int)op2.e.v, is_64bit)) { + tcc_error("move wide shift out of range"); + return; + } + shift = (int)op2.e.v / 16; } switch (token) { @@ -1706,7 +1734,6 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) case TOK_ASM_orr: case TOK_ASM_eor: case TOK_ASM_mul: - case TOK_ASM_muls: asm_data_proc(s1, opcode); break; diff --git a/arm64-tok.h b/arm64-tok.h index 8d1b5cd4..9270e0fb 100644 --- a/arm64-tok.h +++ b/arm64-tok.h @@ -297,7 +297,6 @@ /* Multiply/divide */ DEF_ASM(mul) - DEF_ASM(muls) DEF_ASM(madd) DEF_ASM(msub) DEF_ASM(smaddl) @@ -577,7 +576,6 @@ #define ARM64_ORR_REG 0x2A000000U #define ARM64_EOR_REG 0x4A000000U #define ARM64_MUL_REG 0x1B000000U /* Base opcode, Rm/Rn/Rd must be filled in */ -#define ARM64_MULS_REG 0x3B000000U /* Base opcode, Rm/Rn/Rd must be filled in */ /* Move wide immediate */ #define ARM64_MOVZ 0x52800000U diff --git a/tccrun.c b/tccrun.c index ccca4596..36b82e04 100644 --- a/tccrun.c +++ b/tccrun.c @@ -266,6 +266,17 @@ static void rt_flush_target_io(void) if (fn) fn(NULL); } + +static void rt_cleanup_run_targv(TCCState *s) +{ + void (*cleanup)(void); + + if (!s) + return; + cleanup = (void (*)(void))tcc_get_symbol(s, "__tcc_cleanup_run_targv"); + if (cleanup) + cleanup(); +} #endif /* launch the compiled program with the given arguments */ @@ -699,6 +710,9 @@ static void rt_exit(rt_frame *f, int code) ((void (*)(void))p)(); } #endif +#ifdef _WIN32 + rt_cleanup_run_targv(s); +#endif #if defined(_WIN64) && defined(__aarch64__) && !defined(CONFIG_TCC_BACKTRACE_ONLY) rt_restore_context_from_jmpbuf(s->run_jb, code); return; @@ -1577,6 +1591,7 @@ static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info) ((void (*)(void))p)(); } #endif + rt_cleanup_run_targv(s); rt_restore_context_from_jmpbuf(s->run_jb, 255); } #endif diff --git a/tests/tests2/139_arm64_errors.c b/tests/tests2/139_arm64_errors.c index e064a4c5..00deace5 100644 --- a/tests/tests2/139_arm64_errors.c +++ b/tests/tests2/139_arm64_errors.c @@ -43,6 +43,34 @@ int main(void) return 0; } +#elif defined test_missing_third_operand +int main(void) +{ + __asm__("add x0, x1"); + return 0; +} + +#elif defined test_movz_imm_range +int main(void) +{ + __asm__("movz x0, #0x10000"); + return 0; +} + +#elif defined test_movz_shift_range +int main(void) +{ + __asm__("movz x0, #1, lsl #8"); + return 0; +} + +#elif defined test_invalid_muls +int main(void) +{ + __asm__("muls x0, x1, x2"); + return 0; +} + #elif defined test_extended_inline_asm int main(void) { diff --git a/tests/tests2/139_arm64_errors.expect b/tests/tests2/139_arm64_errors.expect index 9bef6640..53cb39b7 100644 --- a/tests/tests2/139_arm64_errors.expect +++ b/tests/tests2/139_arm64_errors.expect @@ -13,8 +13,20 @@ [test_invalid_barrier_option] 139_arm64_errors.c:42: error: invalid operand 'xyz' +[test_missing_third_operand] +139_arm64_errors.c:48: error: missing third operand + +[test_movz_imm_range] +139_arm64_errors.c:55: error: move wide immediate out of range + +[test_movz_shift_range] +139_arm64_errors.c:62: error: move wide shift out of range + +[test_invalid_muls] +139_arm64_errors.c:70: error: ARM64 instruction 'muls' not implemented + [test_extended_inline_asm] -139_arm64_errors.c:51: error: invalid reference in constraint 1 ('2') +139_arm64_errors.c:79: error: invalid reference in constraint 1 ('2') [test_extended_inline_clobber] -139_arm64_errors.c:59: error: invalid clobber register 'bogus' +139_arm64_errors.c:87: error: invalid clobber register 'bogus' diff --git a/win32/lib/crt1.c b/win32/lib/crt1.c index 43de92f2..70db7291 100644 --- a/win32/lib/crt1.c +++ b/win32/lib/crt1.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -38,6 +39,7 @@ int __cdecl __tgetmainargs(int *pargc, _TCHAR ***pargv, _TCHAR ***penv, int glob int __cdecl __getmainargs(int *pargc, char ***pargv, char ***penv, int globb, _startupinfo*); int __cdecl __wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penv, int globb, _startupinfo*); int __cdecl get_tenviron(_TCHAR ***penv); +int __cdecl _XcptFilter(unsigned long, struct _EXCEPTION_POINTERS *); void __cdecl __set_app_type(int apptype); unsigned int __cdecl _controlfp(unsigned int new_value, unsigned int mask); extern int _tmain(int argc, _TCHAR * argv[], _TCHAR * env[]); @@ -120,6 +122,7 @@ typedef struct run_targv_state { _TCHAR **saved_argv; _TCHAR **run_argv; int active; + struct run_targv_state *prev; } run_targv_state; static __declspec(thread) run_targv_state *run_targv_tls; @@ -457,11 +460,8 @@ fail: } #endif -static void restore_run_targv(int ret, void *opaque) +static void restore_run_targv(run_targv_state *state) { - run_targv_state *state = (run_targv_state *)opaque; - - (void)ret; if (!state || !state->active) return; __argc = state->saved_argc; @@ -470,12 +470,12 @@ static void restore_run_targv(int ret, void *opaque) state->run_argv = NULL; state->active = 0; if (run_targv_tls == state) - run_targv_tls = NULL; + run_targv_tls = state->prev; } -static void restore_run_targv_atexit(void) +void __tcc_cleanup_run_targv(void) { - restore_run_targv(0, run_targv_tls); + restore_run_targv(run_targv_tls); } int _runtmain(int argc, /* as tcc passed in */ char **argv) @@ -483,7 +483,7 @@ int _runtmain(int argc, /* as tcc passed in */ char **argv) int ret; int run_argc = argc; _TCHAR **env = run_get_tenviron(); - run_targv_state argv_state = { __argc, __targv, NULL, 0 }; + run_targv_state argv_state = { __argc, __targv, NULL, 0, NULL }; wchar_t **run_wargv = NULL; run_wargv = build_run_wargv(&run_argc); @@ -501,9 +501,8 @@ int _runtmain(int argc, /* as tcc passed in */ char **argv) } if (!argv_state.run_argv) return 1; + argv_state.prev = run_targv_tls; run_targv_tls = &argv_state; - if (atexit(restore_run_targv_atexit)) - goto fail; __argc = run_argc; __targv = argv_state.run_argv; argv_state.active = 1; @@ -516,11 +515,8 @@ int _runtmain(int argc, /* as tcc passed in */ char **argv) run_dtors(); if (__run_on_exit) __run_on_exit(ret); + __tcc_cleanup_run_targv(); return ret; -fail: - run_targv_tls = NULL; - free_run_targv(argv_state.run_argv); - return 1; } // ============================================= diff --git a/win32/lib/wincrt1.c b/win32/lib/wincrt1.c index c8ed64d1..1ba4ae21 100644 --- a/win32/lib/wincrt1.c +++ b/win32/lib/wincrt1.c @@ -4,6 +4,7 @@ #include #include +#include #include #define __UNKNOWN_APP 0 #define __CONSOLE_APP 1 @@ -29,10 +30,36 @@ int __cdecl __tgetmainargs(int *pargc, _TCHAR ***pargv, _TCHAR ***penv, int glob int __cdecl __getmainargs(int *pargc, char ***pargv, char ***penv, int globb, _startupinfo*); int __cdecl __wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penv, int globb, _startupinfo*); int __cdecl get_tenviron(_TCHAR ***penv); +int __cdecl _XcptFilter(unsigned long, struct _EXCEPTION_POINTERS *); __attribute__((weak)) int __cdecl __rt_get_run_argstart(void); #include "crtinit.c" +typedef struct run_targv_state { + int saved_argc; + _TCHAR **saved_argv; + int active; + struct run_targv_state *prev; +} run_targv_state; + +static __declspec(thread) run_targv_state *run_targv_tls; + +static void restore_run_targv(run_targv_state *state) +{ + if (!state || !state->active) + return; + __argc = state->saved_argc; + __targv = state->saved_argv; + state->active = 0; + if (run_targv_tls == state) + run_targv_tls = state->prev; +} + +void __tcc_cleanup_run_targv(void) +{ + restore_run_targv(run_targv_tls); +} + static int select_run_arg_start_t(int base, const _TCHAR *arg0, int full_argc, _TCHAR **full_argv) { int i; @@ -121,8 +148,7 @@ int _twinstart(void) int _runtwinmain(int argc, /* as tcc passed in */ char **argv) { - int saved_argc = __argc; - _TCHAR **saved_argv = __targv; + run_targv_state argv_state = { __argc, __targv, 0, NULL }; int ret; int run_arg_start = -1; #ifdef UNICODE @@ -155,8 +181,10 @@ int _runtwinmain(int argc, /* as tcc passed in */ char **argv) __argc = argc, __targv = argv; } #endif + argv_state.prev = run_targv_tls; + run_targv_tls = &argv_state; + argv_state.active = 1; ret = go_winmain(__argc > 1 ? __targv[1] : NULL); - __argc = saved_argc; - __targv = saved_argv; + __tcc_cleanup_run_targv(); return ret; } diff --git a/win32/test_run_arg_cleanup.c b/win32/test_run_arg_cleanup.c new file mode 100644 index 00000000..60c08717 --- /dev/null +++ b/win32/test_run_arg_cleanup.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include + +#include "..\tcc.h" + +typedef struct libtcc_api { + TCCState *(*tcc_new)(void); + void (*tcc_delete)(TCCState *s); + int (*tcc_set_options)(TCCState *s, const char *str); + void (*tcc_set_lib_path)(TCCState *s, const char *path); + void (*tcc_set_error_func)(TCCState *s, void *opaque, TCCErrorFunc *error_func); + int (*tcc_set_output_type)(TCCState *s, int output_type); + int (*tcc_add_include_path)(TCCState *s, const char *pathname); + int (*tcc_add_library_path)(TCCState *s, const char *pathname); + int (*tcc_compile_string)(TCCState *s, const char *buf); + int (*tcc_run)(TCCState *s, int argc, char **argv); +} libtcc_api; + +static const char crash_program[] = + "int main(void)\n" + "{\n" + " *(volatile int *)0 = 1;\n" + " return 0;\n" + "}\n"; + +static const char argv_program[] = + "#include \n" + "int main(int argc, char **argv)\n" + "{\n" + " if (argc != 2)\n" + " return 10 + argc;\n" + " if (strcmp(argv[0], \"beta\") || strcmp(argv[1], \"gamma\"))\n" + " return 20;\n" + " return 0;\n" + "}\n"; + +static void handle_error(void *opaque, const char *msg) +{ + fprintf((FILE *)opaque, "%s\n", msg); +} + +static FARPROC load_symbol(HMODULE dll, const char *name) +{ + FARPROC proc = GetProcAddress(dll, name); + + if (!proc) { + fprintf(stderr, "missing libtcc symbol: %s\n", name); + exit(1); + } + return proc; +} + +static int run_program(const libtcc_api *api, const char *source, int run_arg_start) +{ + TCCState *s; + char *argv[] = { "unused0", "unused1", "unused2", NULL }; + int ret; + + s = api->tcc_new(); + if (!s) { + fprintf(stderr, "tcc_new failed\n"); + return 100; + } + + api->tcc_set_error_func(s, stderr, handle_error); + api->tcc_set_options(s, "-bt"); + api->tcc_set_lib_path(s, "."); + api->tcc_add_include_path(s, ".\\include"); + api->tcc_add_include_path(s, ".\\win32\\include"); + api->tcc_add_library_path(s, ".\\win32\\lib"); + s->run_arg_start = run_arg_start; + + ret = api->tcc_set_output_type(s, TCC_OUTPUT_MEMORY); + if (!ret) + ret = api->tcc_compile_string(s, source); + if (!ret) + ret = api->tcc_run(s, 3, argv); + + api->tcc_delete(s); + return ret; +} + +int main(int argc, char **argv) +{ + HMODULE dll; + libtcc_api api; + int ret; + + if (argc < 4) { + fprintf(stderr, "usage: %s alpha beta gamma\n", argv[0]); + return 1; + } + + dll = LoadLibraryA("libtcc.dll"); + if (!dll) { + fprintf(stderr, "failed to load libtcc.dll\n"); + return 1; + } + + memset(&api, 0, sizeof(api)); + api.tcc_new = (void *)load_symbol(dll, "tcc_new"); + api.tcc_delete = (void *)load_symbol(dll, "tcc_delete"); + api.tcc_set_options = (void *)load_symbol(dll, "tcc_set_options"); + api.tcc_set_lib_path = (void *)load_symbol(dll, "tcc_set_lib_path"); + api.tcc_set_error_func = (void *)load_symbol(dll, "tcc_set_error_func"); + api.tcc_set_output_type = (void *)load_symbol(dll, "tcc_set_output_type"); + api.tcc_add_include_path = (void *)load_symbol(dll, "tcc_add_include_path"); + api.tcc_add_library_path = (void *)load_symbol(dll, "tcc_add_library_path"); + api.tcc_compile_string = (void *)load_symbol(dll, "tcc_compile_string"); + api.tcc_run = (void *)load_symbol(dll, "tcc_run"); + + ret = run_program(&api, crash_program, 1); + if (ret != 255) { + fprintf(stderr, "crash program returned %d instead of 255\n", ret); + FreeLibrary(dll); + return 2; + } + + ret = run_program(&api, argv_program, 2); + if (ret != 0) { + fprintf(stderr, "argv cleanup test returned %d\n", ret); + FreeLibrary(dll); + return 3; + } + + puts("run argv cleanup ok"); + FreeLibrary(dll); + return 0; +}