Detect more invalid initializers

in presence of invalid source code we can't rely on the
next token to determine if we have or haven't already parsed
an initializer element, we really have to track it in some separate
state; it's a flag, so merge it with the other two we have (size_only
and first).  Also add some syntax checks for situations which
formerly lead to vstack leaks, see the added testcases.
This commit is contained in:
Michael Matz 2019-03-18 03:26:19 +01:00
parent d72b877e45
commit e6980f6cc7
3 changed files with 57 additions and 35 deletions

View file

@ -93,7 +93,7 @@ static int parse_btype(CType *type, AttributeDef *ad);
static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td); static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td);
static void parse_expr_type(CType *type); static void parse_expr_type(CType *type);
static void init_putv(CType *type, Section *sec, unsigned long c); static void init_putv(CType *type, Section *sec, unsigned long c);
static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only); static void decl_initializer(CType *type, Section *sec, unsigned long c, int flags);
static void block(int *bsym, int *csym, int is_expr); static void block(int *bsym, int *csym, int is_expr);
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope); static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
static void decl(int l); static void decl(int l);
@ -6596,14 +6596,18 @@ static void init_putz(Section *sec, unsigned long c, int size)
} }
} }
#define DIF_FIRST 1
#define DIF_SIZE_ONLY 2
#define DIF_HAVE_ELEM 4
/* t is the array or struct type. c is the array or struct /* t is the array or struct type. c is the array or struct
address. cur_field is the pointer to the current address. cur_field is the pointer to the current
field, for arrays the 'c' member contains the current start field, for arrays the 'c' member contains the current start
index. 'size_only' is true if only size info is needed (only used index. 'flags' is as in decl_initializer.
in arrays). al contains the already initialized length of the 'al' contains the already initialized length of the
current container (starting at c). This returns the new length of that. */ current container (starting at c). This returns the new length of that. */
static int decl_designator(CType *type, Section *sec, unsigned long c, static int decl_designator(CType *type, Section *sec, unsigned long c,
Sym **cur_field, int size_only, int al) Sym **cur_field, int flags, int al)
{ {
Sym *s, *f; Sym *s, *f;
int index, index_last, align, l, nb_elems, elem_size; int index, index_last, align, l, nb_elems, elem_size;
@ -6611,6 +6615,8 @@ static int decl_designator(CType *type, Section *sec, unsigned long c,
elem_size = 0; elem_size = 0;
nb_elems = 1; nb_elems = 1;
if (flags & DIF_HAVE_ELEM)
goto no_designator;
if (gnu_ext && (l = is_label()) != 0) if (gnu_ext && (l = is_label()) != 0)
goto struct_field; goto struct_field;
/* NOTE: we only support ranges for last designator */ /* NOTE: we only support ranges for last designator */
@ -6659,6 +6665,7 @@ static int decl_designator(CType *type, Section *sec, unsigned long c,
expect("="); expect("=");
} }
} else { } else {
no_designator:
if (type->t & VT_ARRAY) { if (type->t & VT_ARRAY) {
index = (*cur_field)->c; index = (*cur_field)->c;
if (type->ref->c >= 0 && index >= type->ref->c) if (type->ref->c >= 0 && index >= type->ref->c)
@ -6677,12 +6684,12 @@ static int decl_designator(CType *type, Section *sec, unsigned long c,
} }
/* must put zero in holes (note that doing it that way /* must put zero in holes (note that doing it that way
ensures that it even works with designators) */ ensures that it even works with designators) */
if (!size_only && c - corig > al) if (!(flags & DIF_SIZE_ONLY) && c - corig > al)
init_putz(sec, corig + al, c - corig - al); init_putz(sec, corig + al, c - corig - al);
decl_initializer(type, sec, c, 0, size_only); decl_initializer(type, sec, c, flags & ~DIF_FIRST);
/* XXX: make it more general */ /* XXX: make it more general */
if (!size_only && nb_elems > 1) { if (!(flags & DIF_SIZE_ONLY) && nb_elems > 1) {
unsigned long c_end; unsigned long c_end;
uint8_t *src, *dst; uint8_t *src, *dst;
int i; int i;
@ -6905,33 +6912,29 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
/* 't' contains the type and storage info. 'c' is the offset of the /* 't' contains the type and storage info. 'c' is the offset of the
object in section 'sec'. If 'sec' is NULL, it means stack based object in section 'sec'. If 'sec' is NULL, it means stack based
allocation. 'first' is true if array '{' must be read (multi allocation. 'flags & DIF_FIRST' is true if array '{' must be read (multi
dimension implicit array init handling). 'size_only' is true if dimension implicit array init handling). 'flags & DIF_SIZE_ONLY' is true if
size only evaluation is wanted (only for arrays). */ size only evaluation is wanted (only for arrays). */
static void decl_initializer(CType *type, Section *sec, unsigned long c, static void decl_initializer(CType *type, Section *sec, unsigned long c,
int first, int size_only) int flags)
{ {
int len, n, no_oblock, nb, i; int len, n, no_oblock, nb, i;
int size1, align1; int size1, align1;
int have_elem;
Sym *s, *f; Sym *s, *f;
Sym indexsym; Sym indexsym;
CType *t1; CType *t1;
/* If we currently are at an '}' or ',' we have read an initializer if (!(flags & DIF_HAVE_ELEM) && tok != '{' &&
element in one of our callers, and not yet consumed it. */
have_elem = tok == '}' || tok == ',';
if (!have_elem && tok != '{' &&
/* In case of strings we have special handling for arrays, so /* In case of strings we have special handling for arrays, so
don't consume them as initializer value (which would commit them don't consume them as initializer value (which would commit them
to some anonymous symbol). */ to some anonymous symbol). */
tok != TOK_LSTR && tok != TOK_STR && tok != TOK_LSTR && tok != TOK_STR &&
!size_only) { !(flags & DIF_SIZE_ONLY)) {
parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST); parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
have_elem = 1; flags |= DIF_HAVE_ELEM;
} }
if (have_elem && if ((flags & DIF_HAVE_ELEM) &&
!(type->t & VT_ARRAY) && !(type->t & VT_ARRAY) &&
/* Use i_c_parameter_t, to strip toplevel qualifiers. /* Use i_c_parameter_t, to strip toplevel qualifiers.
The source type might have VT_CONSTANT set, which is The source type might have VT_CONSTANT set, which is
@ -6945,15 +6948,13 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
size1 = type_size(t1, &align1); size1 = type_size(t1, &align1);
no_oblock = 1; no_oblock = 1;
if ((first && tok != TOK_LSTR && tok != TOK_STR) || if (((flags & DIF_FIRST) && tok != TOK_LSTR && tok != TOK_STR) ||
tok == '{') { tok == '{') {
if (tok != '{') if (tok != '{')
tcc_error("character array initializer must be a literal," tcc_error("character array initializer must be a literal,"
" optionally enclosed in braces"); " optionally enclosed in braces");
skip('{'); skip('{');
no_oblock = 0; no_oblock = 0;
if (tok == ',')
tcc_error("unexpected ','");
} }
/* only parse strings here if correct type (otherwise: handle /* only parse strings here if correct type (otherwise: handle
@ -6978,7 +6979,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
nb = cstr_len; nb = cstr_len;
if (n >= 0 && nb > (n - len)) if (n >= 0 && nb > (n - len))
nb = n - len; nb = n - len;
if (!size_only) { if (!(flags & DIF_SIZE_ONLY)) {
if (cstr_len > nb) if (cstr_len > nb)
tcc_warning("initializer-string for array is too long"); tcc_warning("initializer-string for array is too long");
/* in order to go faster for common case (char /* in order to go faster for common case (char
@ -7004,7 +7005,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
/* only add trailing zero if enough storage (no /* only add trailing zero if enough storage (no
warning in this case since it is standard) */ warning in this case since it is standard) */
if (n < 0 || len < n) { if (n < 0 || len < n) {
if (!size_only) { if (!(flags & DIF_SIZE_ONLY)) {
vpushi(0); vpushi(0);
init_putv(t1, sec, c + (len * size1)); init_putv(t1, sec, c + (len * size1));
} }
@ -7017,9 +7018,9 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
do_init_list: do_init_list:
len = 0; len = 0;
while (tok != '}' || have_elem) { while (tok != '}' || (flags & DIF_HAVE_ELEM)) {
len = decl_designator(type, sec, c, &f, size_only, len); len = decl_designator(type, sec, c, &f, flags, len);
have_elem = 0; flags &= ~DIF_HAVE_ELEM;
if (type->t & VT_ARRAY) { if (type->t & VT_ARRAY) {
++indexsym.c; ++indexsym.c;
/* special test for multi dimensional arrays (may not /* special test for multi dimensional arrays (may not
@ -7042,7 +7043,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
} }
} }
/* put zeros at the end */ /* put zeros at the end */
if (!size_only && len < n*size1) if (!(flags & DIF_SIZE_ONLY) && len < n*size1)
init_putz(sec, c + len, n*size1 - len); init_putz(sec, c + len, n*size1 - len);
if (!no_oblock) if (!no_oblock)
skip('}'); skip('}');
@ -7052,7 +7053,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
} else if ((type->t & VT_BTYPE) == VT_STRUCT) { } else if ((type->t & VT_BTYPE) == VT_STRUCT) {
size1 = 1; size1 = 1;
no_oblock = 1; no_oblock = 1;
if (first || tok == '{') { if ((flags & DIF_FIRST) || tok == '{') {
skip('{'); skip('{');
no_oblock = 0; no_oblock = 0;
} }
@ -7061,20 +7062,22 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
n = s->c; n = s->c;
goto do_init_list; goto do_init_list;
} else if (tok == '{') { } else if (tok == '{') {
if (flags & DIF_HAVE_ELEM)
skip(';');
next(); next();
decl_initializer(type, sec, c, first, size_only); decl_initializer(type, sec, c, flags & ~DIF_HAVE_ELEM);
skip('}'); skip('}');
} else if (size_only) { } else if ((flags & DIF_SIZE_ONLY)) {
/* If we supported only ISO C we wouldn't have to accept calling /* If we supported only ISO C we wouldn't have to accept calling
this on anything than an array size_only==1 (and even then this on anything than an array if DIF_SIZE_ONLY (and even then
only on the outermost level, so no recursion would be needed), only on the outermost level, so no recursion would be needed),
because initializing a flex array member isn't supported. because initializing a flex array member isn't supported.
But GNU C supports it, so we need to recurse even into But GNU C supports it, so we need to recurse even into
subfields of structs and arrays when size_only is set. */ subfields of structs and arrays when DIF_SIZE_ONLY is set. */
/* just skip expression */ /* just skip expression */
skip_or_save_block(NULL); skip_or_save_block(NULL);
} else { } else {
if (!have_elem) { if (!(flags & DIF_HAVE_ELEM)) {
/* This should happen only when we haven't parsed /* This should happen only when we haven't parsed
the init element above for fear of committing a the init element above for fear of committing a
string constant to memory too early. */ string constant to memory too early. */
@ -7154,7 +7157,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
/* compute size */ /* compute size */
begin_macro(init_str, 1); begin_macro(init_str, 1);
next(); next();
decl_initializer(type, NULL, 0, 1, 1); decl_initializer(type, NULL, 0, DIF_FIRST | DIF_SIZE_ONLY);
/* prepare second initializer parsing */ /* prepare second initializer parsing */
macro_ptr = init_str->str; macro_ptr = init_str->str;
next(); next();
@ -7321,7 +7324,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
size_t oldreloc_offset = 0; size_t oldreloc_offset = 0;
if (sec && sec->reloc) if (sec && sec->reloc)
oldreloc_offset = sec->reloc->data_offset; oldreloc_offset = sec->reloc->data_offset;
decl_initializer(type, sec, addr, 1, 0); decl_initializer(type, sec, addr, DIF_FIRST);
if (sec && sec->reloc) if (sec && sec->reloc)
squeeze_multi_relocs(sec, oldreloc_offset); squeeze_multi_relocs(sec, oldreloc_offset);
/* patch flexible array member size back to -1, */ /* patch flexible array member size back to -1, */

View file

@ -140,5 +140,15 @@ int bar (const char *(*g)()) // should match this 'g' argument
int foo(int ()) // abstract decl is wrong in definitions int foo(int ()) // abstract decl is wrong in definitions
{ {
return 0; return 0;
#elif defined test_invalid_1
void f(char*);
void g(void) {
f((char[]){1, ,});
} }
#elif defined test_invalid_2
int ga = 0.42 { 2 };
#elif defined test_invalid_3
struct S { int a, b; };
struct T { struct S x; };
struct T gt = { 42 a: 1, 43 };
#endif #endif

View file

@ -62,3 +62,12 @@
[test_abstract_decls] [test_abstract_decls]
60_errors_and_warnings.c:141: error: identifier expected 60_errors_and_warnings.c:141: error: identifier expected
[test_invalid_1]
60_errors_and_warnings.c:146: error: identifier expected
[test_invalid_2]
60_errors_and_warnings.c:149: error: ';' expected (got "{")
[test_invalid_3]
60_errors_and_warnings.c:153: error: ',' expected (got "a")