arm64: reject invalid asm and clean run argv

This commit is contained in:
Benjamin Oldenburg 2026-04-04 21:29:28 +07:00
parent 42d85ec622
commit 8b5ab1bb01
8 changed files with 269 additions and 34 deletions

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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)
{

View File

@ -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'

View File

@ -5,6 +5,7 @@
#include <tchar.h>
#include <windows.h>
#include <excpt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -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;
}
// =============================================

View File

@ -4,6 +4,7 @@
#include <tchar.h>
#include <windows.h>
#include <excpt.h>
#include <stdlib.h>
#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;
}

View File

@ -0,0 +1,131 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#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 <string.h>\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;
}