From a045400501147ecef5d51539a7d57e2cb0c39c1e Mon Sep 17 00:00:00 2001 From: grischka Date: Mon, 24 Apr 2023 21:58:50 +0200 Subject: [PATCH] CStrings leakless avoid memory leaks with lost CStrings on stack after errors. tccpp.c: - use/abuse static Cstring tokcstr where possible tccgen.c: - use/abuse static Cstring initstr where possible tcc.h/libtcc.a: - add 'stk_data' array to track memory pointer on stack - add macros stk_push/pop() and cstr_new/free_s() tccasm.c: - use that - use char[16] instead of char* for op.constraint --- libtcc.c | 8 +++++- tcc.h | 17 ++++++++--- tccasm.c | 48 +++++++++++++++---------------- tccgen.c | 86 +++++++++++++++++++++++++------------------------------- tccpp.c | 82 ++++++++++++++++++++++++----------------------------- 5 files changed, 118 insertions(+), 123 deletions(-) diff --git a/libtcc.c b/libtcc.c index 1f896f26..d891f87d 100644 --- a/libtcc.c +++ b/libtcc.c @@ -68,6 +68,9 @@ /* XXX: get rid of this ASAP (or maybe not) */ ST_DATA struct TCCState *tcc_state; TCC_SEM(static tcc_compile_sem); +/* an array of pointers to memory to be free'd after errors */ +ST_DATA void** stk_data; +ST_DATA int nb_stk_data; /********************************************************/ #ifdef _WIN32 @@ -609,8 +612,11 @@ static void error1(int mode, const char *fmt, va_list ap) cstr_free(&cs); if (mode != ERROR_WARN) s1->nb_errors++; - if (mode == ERROR_ERROR && s1->error_set_jmp_enabled) + if (mode == ERROR_ERROR && s1->error_set_jmp_enabled) { + while (nb_stk_data) + tcc_free(*(void**)stk_data[--nb_stk_data]); longjmp(s1->error_jmp_buf, 1); + } } LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, TCCErrorFunc error_func) diff --git a/tcc.h b/tcc.h index 75ebfbca..4bc5f508 100644 --- a/tcc.h +++ b/tcc.h @@ -495,6 +495,7 @@ typedef struct CString { int size; /* size in bytes */ int size_allocated; void *data; /* either 'char *' or 'nwchar_t *' */ + struct CString *prev; } CString; /* type definition */ @@ -733,7 +734,7 @@ typedef struct ExprValue { #define MAX_ASM_OPERANDS 30 typedef struct ASMOperand { int id; /* GCC 3 optional identifier (0 if number only supported) */ - char *constraint; + char constraint[16]; char asm_str[16]; /* computed asm string for operand */ SValue *vt; /* C value of the expression */ int ref_index; /* if >= 0, gives reference to a output constraint */ @@ -1208,6 +1209,8 @@ enum tcc_token { /* ------------ libtcc.c ------------ */ ST_DATA struct TCCState *tcc_state; +ST_DATA void** stk_data; +ST_DATA int nb_stk_data; /* public functions currently used by the tcc main function */ ST_FUNC char *pstrcpy(char *buf, size_t buf_size, const char *s); @@ -1257,11 +1260,17 @@ ST_FUNC void cstr_free(CString *cstr); ST_FUNC int cstr_printf(CString *cs, const char *fmt, ...) PRINTF_LIKE(2,3); ST_FUNC int cstr_vprintf(CString *cstr, const char *fmt, va_list ap); ST_FUNC void cstr_reset(CString *cstr); - ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen); ST_FUNC int tcc_open(TCCState *s1, const char *filename); ST_FUNC void tcc_close(void); +/* mark a memory pointer on stack for cleanup after errors */ +#define stk_push(p) dynarray_add(&stk_data, &nb_stk_data, p) +#define stk_pop() (--nb_stk_data) +/* mark CString on stack for cleanup errors */ +#define cstr_new_s(cstr) (cstr_new(cstr), stk_push(&(cstr)->data)) +#define cstr_free_s(cstr) (cstr_free(cstr), stk_pop()) + ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags); /* flags: */ #define AFF_PRINT_ERROR 0x10 /* print error if file not found */ @@ -1483,8 +1492,8 @@ ST_FUNC int type_size(CType *type, int *a); ST_FUNC void mk_pointer(CType *type); ST_FUNC void vstore(void); ST_FUNC void inc(int post, int c); -ST_FUNC void parse_mult_str (CString *astr, const char *msg); -ST_FUNC void parse_asm_str(CString *astr); +ST_FUNC CString* parse_mult_str(const char *msg); +ST_FUNC CString* parse_asm_str(void); ST_FUNC void indir(void); ST_FUNC void unary(void); ST_FUNC void gexpr(void); diff --git a/tccasm.c b/tccasm.c index 65aae62f..fcae05eb 100644 --- a/tccasm.c +++ b/tccasm.c @@ -1064,15 +1064,12 @@ ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, } static void subst_asm_operands(ASMOperand *operands, int nb_operands, - CString *out_str, CString *in_str) + CString *out_str, const char *str) { int c, index, modifier; - const char *str; ASMOperand *op; SValue sv; - cstr_new(out_str); - str = in_str->data; for(;;) { c = *str++; if (c == '%') { @@ -1118,11 +1115,11 @@ static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr, { ASMOperand *op; int nb_operands; + char* astr; if (tok != ':') { nb_operands = *nb_operands_ptr; for(;;) { - CString astr; if (nb_operands >= MAX_ASM_OPERANDS) tcc_error("too many asm operands"); op = &operands[nb_operands++]; @@ -1135,10 +1132,8 @@ static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr, next(); skip(']'); } - parse_mult_str(&astr, "string constant"); - op->constraint = tcc_malloc(astr.size); - strcpy(op->constraint, astr.data); - cstr_free(&astr); + astr = parse_mult_str("string constant")->data; + pstrcpy(op->constraint, sizeof op->constraint, astr); skip('('); gexpr(); if (is_output) { @@ -1171,7 +1166,8 @@ static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr, /* parse the GCC asm() instruction */ ST_FUNC void asm_instr(void) { - CString astr, astr1; + CString astr, *astr1; + ASMOperand operands[MAX_ASM_OPERANDS]; int nb_outputs, nb_operands, i, must_subst, out_reg, nb_labels; uint8_t clobber_regs[NB_ASM_REGS]; @@ -1183,7 +1179,11 @@ ST_FUNC void asm_instr(void) || tok == TOK_GOTO) { next(); } - parse_asm_str(&astr); + + astr1 = parse_asm_str(); + cstr_new_s(&astr); + cstr_cat(&astr, astr1->data, astr1->size); + nb_operands = 0; nb_outputs = 0; nb_labels = 0; @@ -1273,13 +1273,14 @@ ST_FUNC void asm_instr(void) printf("asm: \"%s\"\n", (char *)astr.data); #endif if (must_subst) { - subst_asm_operands(operands, nb_operands + nb_labels, &astr1, &astr); - cstr_free(&astr); - } else { - astr1 = astr; + cstr_reset(astr1); + cstr_cat(astr1, astr.data, astr.size); + cstr_reset(&astr); + subst_asm_operands(operands, nb_operands + nb_labels, &astr, astr1->data); } + #ifdef ASM_DEBUG - printf("subst_asm: \"%s\"\n", (char *)astr1.data); + printf("subst_asm: \"%s\"\n", (char *)astr.data); #endif /* generate loads */ @@ -1290,7 +1291,8 @@ ST_FUNC void asm_instr(void) bleed out to surrounding code. */ sec = cur_text_section; /* assemble the string with tcc internal assembler */ - tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0); + tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 0); + cstr_free_s(&astr); if (sec != cur_text_section) { tcc_warning("inline asm tries to change current section"); use_section1(tcc_state, sec); @@ -1305,23 +1307,20 @@ ST_FUNC void asm_instr(void) /* free everything */ for(i=0;iconstraint); vpop(); } - cstr_free(&astr1); + } ST_FUNC void asm_global_instr(void) { - CString astr; + CString *astr; int saved_nocode_wanted = nocode_wanted; /* Global asm blocks are always emitted. */ nocode_wanted = 0; next(); - parse_asm_str(&astr); + astr = parse_asm_str(); skip(')'); /* NOTE: we do not eat the ';' so that we can restore the current token after the assembler parsing */ @@ -1335,14 +1334,13 @@ ST_FUNC void asm_global_instr(void) ind = cur_text_section->data_offset; /* assemble the string with tcc internal assembler */ - tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1); + tcc_assemble_inline(tcc_state, astr->data, astr->size - 1, 1); cur_text_section->data_offset = ind; /* restore the current C token */ next(); - cstr_free(&astr); nocode_wanted = saved_nocode_wanted; } diff --git a/tccgen.c b/tccgen.c index c7e8d136..d5c01670 100644 --- a/tccgen.c +++ b/tccgen.c @@ -402,7 +402,6 @@ ST_FUNC int tccgen_compile(TCCState *s1) ST_FUNC void tccgen_finish(TCCState *s1) { tcc_debug_end(s1); /* just in case of errors: free memory */ - cstr_free(&initstr); free_inline_functions(s1); sym_pop(&global_stack, NULL, 0); sym_pop(&local_stack, NULL, 0); @@ -412,6 +411,8 @@ ST_FUNC void tccgen_finish(TCCState *s1) dynarray_reset(&sym_pools, &nb_sym_pools); sym_free_first = NULL; global_label_stack = local_label_stack = NULL; + cstr_free(&initstr); + dynarray_reset(&stk_data, &nb_stk_data); } /* ------------------------------------------------------------------------- */ @@ -3756,18 +3757,19 @@ ST_FUNC void inc(int post, int c) vpop(); /* if post op, return saved value */ } -ST_FUNC void parse_mult_str (CString *astr, const char *msg) +ST_FUNC CString* parse_mult_str (const char *msg) { /* read the string */ if (tok != TOK_STR) expect(msg); - cstr_new(astr); + cstr_reset(&initstr); while (tok == TOK_STR) { /* XXX: add \0 handling too ? */ - cstr_cat(astr, tokc.str.data, -1); + cstr_cat(&initstr, tokc.str.data, -1); next(); } - cstr_ccat(astr, '\0'); + cstr_ccat(&initstr, '\0'); + return &initstr; } /* If I is >= 1 and a power of two, returns log2(i)+1. @@ -3792,7 +3794,7 @@ ST_FUNC int exact_log2p1(int i) static void parse_attribute(AttributeDef *ad) { int t, n; - CString astr; + char *astr; redo: if (tok != TOK_ATTRIBUTE1 && tok != TOK_ATTRIBUTE2) @@ -3839,37 +3841,33 @@ redo: case TOK_SECTION1: case TOK_SECTION2: skip('('); - parse_mult_str(&astr, "section name"); - ad->section = find_section(tcc_state, (char *)astr.data); + astr = parse_mult_str("section name")->data; + ad->section = find_section(tcc_state, astr); skip(')'); - cstr_free(&astr); break; case TOK_ALIAS1: case TOK_ALIAS2: skip('('); - parse_mult_str(&astr, "alias(\"target\")"); - ad->alias_target = /* save string as token, for later */ - tok_alloc((char*)astr.data, astr.size-1)->tok; + astr = parse_mult_str("alias(\"target\")")->data; + /* save string as token, for later */ + ad->alias_target = tok_alloc_const(astr); skip(')'); - cstr_free(&astr); break; case TOK_VISIBILITY1: case TOK_VISIBILITY2: skip('('); - parse_mult_str(&astr, - "visibility(\"default|hidden|internal|protected\")"); - if (!strcmp (astr.data, "default")) + astr = parse_mult_str("visibility(\"default|hidden|internal|protected\")")->data; + if (!strcmp (astr, "default")) ad->a.visibility = STV_DEFAULT; - else if (!strcmp (astr.data, "hidden")) + else if (!strcmp (astr, "hidden")) ad->a.visibility = STV_HIDDEN; - else if (!strcmp (astr.data, "internal")) + else if (!strcmp (astr, "internal")) ad->a.visibility = STV_INTERNAL; - else if (!strcmp (astr.data, "protected")) + else if (!strcmp (astr, "protected")) ad->a.visibility = STV_PROTECTED; else expect("visibility(\"default|hidden|internal|protected\")"); skip(')'); - cstr_free(&astr); break; case TOK_ALIGNED1: case TOK_ALIGNED2: @@ -4818,26 +4816,25 @@ static inline void convert_parameter_type(CType *pt) } } -ST_FUNC void parse_asm_str(CString *astr) +ST_FUNC CString* parse_asm_str(void) { skip('('); - parse_mult_str(astr, "string constant"); + return parse_mult_str("string constant"); } /* Parse an asm label and return the token */ static int asm_label_instr(void) { int v; - CString astr; + char *astr; next(); - parse_asm_str(&astr); + astr = parse_asm_str()->data; skip(')'); #ifdef ASM_DEBUG - printf("asm_alias: \"%s\"\n", (char *)astr.data); + printf("asm_alias: \"%s\"\n", astr); #endif - v = tok_alloc(astr.data, astr.size - 1)->tok; - cstr_free(&astr); + v = tok_alloc_const(astr); return v; } @@ -8357,29 +8354,23 @@ static void free_inline_functions(TCCState *s) dynarray_reset(&s->inline_fns, &s->nb_inline_fns); } -static void do_Static_assert(void){ - CString error_str; +static void do_Static_assert(void) +{ int c; + const char *msg; next(); skip('('); c = expr_const(); - - if (tok == ')') { - if (!c) - tcc_error("_Static_assert fail"); - next(); - goto static_assert_out; + msg = "_Static_assert fail"; + if (tok == ',') { + next(); + msg = parse_mult_str("string constant")->data; } - - skip(','); - parse_mult_str(&error_str, "string constant"); - if (c == 0) - tcc_error("%s", (char *)error_str.data); - cstr_free(&error_str); skip(')'); - static_assert_out: - skip(';'); + if (c == 0) + tcc_error("%s", msg); + skip(';'); } /* 'l' is VT_LOCAL or VT_CONST to define default storage type @@ -8393,10 +8384,11 @@ static int decl(int l) AttributeDef ad, adbase; while (1) { - if (tok == TOK_STATIC_ASSERT) { - do_Static_assert(); - continue; - } + + if (tok == TOK_STATIC_ASSERT) { + do_Static_assert(); + continue; + } oldint = 0; if (!parse_btype(&btype, &adbase, l == VT_LOCAL)) { diff --git a/tccpp.c b/tccpp.c index 5192e028..eb4e8982 100644 --- a/tccpp.c +++ b/tccpp.c @@ -45,7 +45,6 @@ ST_DATA TokenSym **table_ident; static TokenSym *hash_ident[TOK_HASH_SIZE]; static char token_buf[STRING_MAX_SIZE + 1]; static CString cstr_buf; -static CString macro_equal_buf; static TokenString tokstr_buf; static unsigned char isidnum_table[256 - CH_EOF]; static int pp_debug_tok, pp_debug_symv; @@ -399,7 +398,6 @@ ST_FUNC void cstr_new(CString *cstr) ST_FUNC void cstr_free(CString *cstr) { tcc_free(cstr->data); - cstr_new(cstr); } /* reset string to empty */ @@ -1273,12 +1271,11 @@ static int macro_is_equal(const int *a, const int *b) return 1; while (*a && *b) { - /* first time preallocate macro_equal_buf, next time only reset position to start */ - cstr_reset(¯o_equal_buf); + cstr_reset(&tokcstr); TOK_GET(&t, &a, &cv); - cstr_cat(¯o_equal_buf, get_tok_str(t, &cv), 0); + cstr_cat(&tokcstr, get_tok_str(t, &cv), 0); TOK_GET(&t, &b, &cv); - if (strcmp(macro_equal_buf.data, get_tok_str(t, &cv))) + if (strcmp(tokcstr.data, get_tok_str(t, &cv))) return 0; } return !(*a || *b); @@ -1348,14 +1345,15 @@ search_cached_include(TCCState *s1, const char *filename, int add); static int parse_include(TCCState *s1, int do_next, int test) { int c, i; - CString cs; char name[1024], buf[1024], *p; CachedInclude *e; - cstr_new(&cs); c = skip_spaces(); if (c == '<' || c == '\"') { - file->buf_ptr = parse_pp_string(file->buf_ptr, c == '<' ? '>' : c, &cs); + cstr_reset(&tokcstr); + file->buf_ptr = parse_pp_string(file->buf_ptr, c == '<' ? '>' : c, &tokcstr); + i = tokcstr.size; + pstrncpy(name, tokcstr.data, i >= sizeof name ? sizeof name - 1 : i); next_nomacro(); } else { /* computed #include : concatenate tokens until result is one of @@ -1363,24 +1361,22 @@ static int parse_include(TCCState *s1, int do_next, int test) parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_LINEFEED | (parse_flags & PARSE_FLAG_ASM_FILE); + name[0] = 0; for (;;) { next(); - p = cs.data, i = cs.size - 1; + p = name, i = strlen(p) - 1; if (i > 0 && ((p[0] == '"' && p[i] == '"') || (p[0] == '<' && p[i] == '>'))) break; if (tok == TOK_LINEFEED) tcc_error("'#include' expects \"FILENAME\" or "); - cstr_cat(&cs, get_tok_str(tok, &tokc), -1); + pstrcat(name, sizeof name, get_tok_str(tok, &tokc)); } c = p[0]; /* remove '<>|""' */ - memmove(p, p + 1, cs.size -= 2); + memmove(p, p + 1, i - 1), p[i - 1] = 0; } - cstr_ccat(&cs, '\0'); - pstrcpy(name, sizeof name, cs.data); - cstr_free(&cs); i = do_next ? file->include_next_index : -1; for (;;) { @@ -2934,7 +2930,6 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) Sym *s; CValue cval; TokenString str; - CString cstr; tok_str_new(&str); t0 = t1 = 0; @@ -2949,8 +2944,8 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) goto bad_stringy; s = sym_find2(args, t); if (s) { - cstr_new(&cstr); - cstr_ccat(&cstr, '\"'); + cstr_reset(&tokcstr); + cstr_ccat(&tokcstr, '\"'); st = s->d; spc = 0; while (*st >= 0) { @@ -2961,24 +2956,23 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) const char *s = get_tok_str(t, &cval); while (*s) { if (t == TOK_PPSTR && *s != '\'') - add_char(&cstr, *s); + add_char(&tokcstr, *s); else - cstr_ccat(&cstr, *s); + cstr_ccat(&tokcstr, *s); ++s; } } } - cstr.size -= spc; - cstr_ccat(&cstr, '\"'); - cstr_ccat(&cstr, '\0'); + tokcstr.size -= spc; + cstr_ccat(&tokcstr, '\"'); + cstr_ccat(&tokcstr, '\0'); #ifdef PP_DEBUG - printf("\nstringize: <%s>\n", (char *)cstr.data); + printf("\nstringize: <%s>\n", (char *)tokcstr.data); #endif /* add string */ - cval.str.size = cstr.size; - cval.str.data = cstr.data; + cval.str.size = tokcstr.size; + cval.str.data = tokcstr.data; tok_str_add2(&str, TOK_PPSTR, &cval); - cstr_free(&cstr); } else { bad_stringy: expect("macro parameter after '#'"); @@ -3047,19 +3041,19 @@ static char const ab_month_name[12][4] = static int paste_tokens(int t1, CValue *v1, int t2, CValue *v2) { - CString cstr; int n, ret = 1; - cstr_new(&cstr); + cstr_reset(&tokcstr); if (t1 != TOK_PLCHLDR) - cstr_cat(&cstr, get_tok_str(t1, v1), -1); - n = cstr.size; + cstr_cat(&tokcstr, get_tok_str(t1, v1), -1); + n = tokcstr.size; if (t2 != TOK_PLCHLDR) - cstr_cat(&cstr, get_tok_str(t2, v2), -1); - cstr_ccat(&cstr, '\0'); + cstr_cat(&tokcstr, get_tok_str(t2, v2), -1); + cstr_ccat(&tokcstr, '\0'); + //printf("paste <%s>\n", (char*)tokcstr.data); - tcc_open_bf(tcc_state, ":paste:", cstr.size); - memcpy(file->buffer, cstr.data, cstr.size); + tcc_open_bf(tcc_state, ":paste:", tokcstr.size); + memcpy(file->buffer, tokcstr.data, tokcstr.size); tok_flags = 0; for (;;) { next_nomacro1(); @@ -3068,13 +3062,11 @@ static int paste_tokens(int t1, CValue *v1, int t2, CValue *v2) if (is_space(tok)) continue; tcc_warning("pasting \"%.*s\" and \"%s\" does not give a valid" - " preprocessing token", n, (char *)cstr.data, (char*)cstr.data + n); + " preprocessing token", n, file->buffer, file->buffer + n); ret = 0; break; } tcc_close(); - //printf("paste <%s>\n", (char*)cstr.data); - cstr_free(&cstr); return ret; } @@ -3214,7 +3206,6 @@ static int macro_subst_tok( TokenString str; char *cstrval; CValue cval; - CString cstr; char buf[32]; /* if symbol is a macro, prepare substitution */ @@ -3245,12 +3236,11 @@ static int macro_subst_tok( add_cstr: t1 = TOK_STR; add_cstr1: - cstr_new(&cstr); - cstr_cat(&cstr, cstrval, 0); - cval.str.size = cstr.size; - cval.str.data = cstr.data; + cstr_reset(&tokcstr); + cstr_cat(&tokcstr, cstrval, 0); + cval.str.size = tokcstr.size; + cval.str.data = tokcstr.data; tok_str_add2(tok_str, t1, &cval); - cstr_free(&cstr); } else if (s->d) { int saved_parse_flags = parse_flags; int *joined_str = NULL; @@ -3634,7 +3624,6 @@ static void tcc_predefs(TCCState *s1, CString *cs, int is_asm) ST_FUNC void preprocess_start(TCCState *s1, int filetype) { int is_asm = !!(filetype & (AFF_TYPE_ASM|AFF_TYPE_ASMPP)); - CString cstr; tccpp_new(s1); @@ -3652,6 +3641,7 @@ ST_FUNC void preprocess_start(TCCState *s1, int filetype) set_idnum('.', is_asm ? IS_ID : 0); if (!(filetype & AFF_TYPE_ASM)) { + CString cstr; cstr_new(&cstr); tcc_predefs(s1, &cstr, is_asm); if (s1->cmdline_defs.size) @@ -3710,6 +3700,7 @@ ST_FUNC void tccpp_new(TCCState *s) memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *)); memset(s->cached_includes_hash, 0, sizeof s->cached_includes_hash); + cstr_new(&tokcstr); cstr_new(&cstr_buf); cstr_realloc(&cstr_buf, STRING_MAX_SIZE); tok_str_new(&tokstr_buf); @@ -3755,7 +3746,6 @@ ST_FUNC void tccpp_delete(TCCState *s) /* free static buffers */ cstr_free(&tokcstr); cstr_free(&cstr_buf); - cstr_free(¯o_equal_buf); tok_str_free_str(tokstr_buf.str); /* free allocators */