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
This commit is contained in:
grischka 2023-04-24 21:58:50 +02:00
parent 40131b7e0f
commit a045400501
5 changed files with 118 additions and 123 deletions

View File

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

17
tcc.h
View File

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

View File

@ -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;i<nb_operands;i++) {
ASMOperand *op;
op = &operands[i];
tcc_free(op->constraint);
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;
}

View File

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

82
tccpp.c
View File

@ -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(&macro_equal_buf);
cstr_reset(&tokcstr);
TOK_GET(&t, &a, &cv);
cstr_cat(&macro_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 <FILENAME>");
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(&macro_equal_buf);
tok_str_free_str(tokstr_buf.str);
/* free allocators */