History

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.

Michael Matz committed 7 years ago

Commit e6980f61 parent d72b8773 files · +57 −35Browse files

Unified · Split

3 files
  1. tccgen.c +38 −35
  2. tests/tests2/60_errors_and_warnings.c +10 −0
  3. tests/tests2/60_errors_and_warnings.expect +9 −0
tccgen.c+38−35
@@ -93,7 +93,7 @@ static int parse_btype(CType *type, AttributeDef *ad); · 92 unmodified lines
93static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td);93static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td);
94static void parse_expr_type(CType *type);94static void parse_expr_type(CType *type);
95static void init_putv(CType *type, Section *sec, unsigned long c);95static void init_putv(CType *type, Section *sec, unsigned long c);
96static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only);96static void decl_initializer(CType *type, Section *sec, unsigned long c, int flags);
97static void block(int *bsym, int *csym, int is_expr);97static void block(int *bsym, int *csym, int is_expr);
98static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);98static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
99static void decl(int l);99static void decl(int l);
@@ -6596,14 +6596,18 @@ static void init_putz(Section *sec, unsigned long c, int size) · 6496 unmodified lines
6596 }6596 }
6597}6597}
6598 6598
6599#define DIF_FIRST 1
6600#define DIF_SIZE_ONLY 2
6601#define DIF_HAVE_ELEM 4
6602
6599/* t is the array or struct type. c is the array or struct6603/* t is the array or struct type. c is the array or struct
6600 address. cur_field is the pointer to the current6604 address. cur_field is the pointer to the current
6601 field, for arrays the 'c' member contains the current start6605 field, for arrays the 'c' member contains the current start
6602 index. 'size_only' is true if only size info is needed (only used6606 index. 'flags' is as in decl_initializer.
6603 in arrays). al contains the already initialized length of the6607 'al' contains the already initialized length of the
6604 current container (starting at c). This returns the new length of that. */6608 current container (starting at c). This returns the new length of that. */
6605static int decl_designator(CType *type, Section *sec, unsigned long c,6609static int decl_designator(CType *type, Section *sec, unsigned long c,
6606 Sym **cur_field, int size_only, int al)6610 Sym **cur_field, int flags, int al)
6607{6611{
6608 Sym *s, *f;6612 Sym *s, *f;
6609 int index, index_last, align, l, nb_elems, elem_size;6613 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, · 1 unmodified line
6611 6615
6612 elem_size = 0;6616 elem_size = 0;
6613 nb_elems = 1;6617 nb_elems = 1;
6618 if (flags & DIF_HAVE_ELEM)
6619 goto no_designator;
6614 if (gnu_ext && (l = is_label()) != 0)6620 if (gnu_ext && (l = is_label()) != 0)
6615 goto struct_field;6621 goto struct_field;
6616 /* NOTE: we only support ranges for last designator */6622 /* NOTE: we only support ranges for last designator */
@@ -6659,6 +6665,7 @@ static int decl_designator(CType *type, Section *sec, unsigned long c, · 42 unmodified lines
6659 expect("=");6665 expect("=");
6660 }6666 }
6661 } else {6667 } else {
6668 no_designator:
6662 if (type->t & VT_ARRAY) {6669 if (type->t & VT_ARRAY) {
6663 index = (*cur_field)->c;6670 index = (*cur_field)->c;
6664 if (type->ref->c >= 0 && index >= type->ref->c)6671 if (type->ref->c >= 0 && index >= type->ref->c)
@@ -6677,12 +6684,12 @@ static int decl_designator(CType *type, Section *sec, unsigned long c, · 12 unmodified lines
6677 }6684 }
6678 /* must put zero in holes (note that doing it that way6685 /* must put zero in holes (note that doing it that way
6679 ensures that it even works with designators) */6686 ensures that it even works with designators) */
6680 if (!size_only && c - corig > al)6687 if (!(flags & DIF_SIZE_ONLY) && c - corig > al)
6681 init_putz(sec, corig + al, c - corig - al);6688 init_putz(sec, corig + al, c - corig - al);
6682 decl_initializer(type, sec, c, 0, size_only);6689 decl_initializer(type, sec, c, flags & ~DIF_FIRST);
6683 6690
6684 /* XXX: make it more general */6691 /* XXX: make it more general */
6685 if (!size_only && nb_elems > 1) {6692 if (!(flags & DIF_SIZE_ONLY) && nb_elems > 1) {
6686 unsigned long c_end;6693 unsigned long c_end;
6687 uint8_t *src, *dst;6694 uint8_t *src, *dst;
6688 int i;6695 int i;
@@ -6905,33 +6912,29 @@ static void init_putv(CType *type, Section *sec, unsigned long c) · 216 unmodified lines
6905 6912
6906/* 't' contains the type and storage info. 'c' is the offset of the6913/* 't' contains the type and storage info. 'c' is the offset of the
6907 object in section 'sec'. If 'sec' is NULL, it means stack based6914 object in section 'sec'. If 'sec' is NULL, it means stack based
6908 allocation. 'first' is true if array '{' must be read (multi6915 allocation. 'flags & DIF_FIRST' is true if array '{' must be read (multi
6909 dimension implicit array init handling). 'size_only' is true if6916 dimension implicit array init handling). 'flags & DIF_SIZE_ONLY' is true if
6910 size only evaluation is wanted (only for arrays). */6917 size only evaluation is wanted (only for arrays). */
6911static void decl_initializer(CType *type, Section *sec, unsigned long c, 6918static void decl_initializer(CType *type, Section *sec, unsigned long c,
6912 int first, int size_only)6919 int flags)
6913{6920{
6914 int len, n, no_oblock, nb, i;6921 int len, n, no_oblock, nb, i;
6915 int size1, align1;6922 int size1, align1;
6916 int have_elem;
6917 Sym *s, *f;6923 Sym *s, *f;
6918 Sym indexsym;6924 Sym indexsym;
6919 CType *t1;6925 CType *t1;
6920 6926
6921 /* If we currently are at an '}' or ',' we have read an initializer6927 if (!(flags & DIF_HAVE_ELEM) && tok != '{' &&
6922 element in one of our callers, and not yet consumed it. */
6923 have_elem = tok == '}' || tok == ',';
6924 if (!have_elem && tok != '{' &&
6925 /* In case of strings we have special handling for arrays, so6928 /* In case of strings we have special handling for arrays, so
6926 don't consume them as initializer value (which would commit them6929 don't consume them as initializer value (which would commit them
6927 to some anonymous symbol). */6930 to some anonymous symbol). */
6928 tok != TOK_LSTR && tok != TOK_STR &&6931 tok != TOK_LSTR && tok != TOK_STR &&
6929 !size_only) {6932 !(flags & DIF_SIZE_ONLY)) {
6930 parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);6933 parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
6931 have_elem = 1;6934 flags |= DIF_HAVE_ELEM;
6932 }6935 }
6933 6936
6934 if (have_elem &&6937 if ((flags & DIF_HAVE_ELEM) &&
6935 !(type->t & VT_ARRAY) &&6938 !(type->t & VT_ARRAY) &&
6936 /* Use i_c_parameter_t, to strip toplevel qualifiers.6939 /* Use i_c_parameter_t, to strip toplevel qualifiers.
6937 The source type might have VT_CONSTANT set, which is6940 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, · 7 unmodified lines
6945 size1 = type_size(t1, &align1);6948 size1 = type_size(t1, &align1);
6946 6949
6947 no_oblock = 1;6950 no_oblock = 1;
6948 if ((first && tok != TOK_LSTR && tok != TOK_STR) || 6951 if (((flags & DIF_FIRST) && tok != TOK_LSTR && tok != TOK_STR) ||
6949 tok == '{') {6952 tok == '{') {
6950 if (tok != '{')6953 if (tok != '{')
6951 tcc_error("character array initializer must be a literal,"6954 tcc_error("character array initializer must be a literal,"
6952 " optionally enclosed in braces");6955 " optionally enclosed in braces");
6953 skip('{');6956 skip('{');
6954 no_oblock = 0;6957 no_oblock = 0;
6955 if (tok == ',')
6956 tcc_error("unexpected ','");
6957 }6958 }
6958 6959
6959 /* only parse strings here if correct type (otherwise: handle6960 /* only parse strings here if correct type (otherwise: handle
@@ -6978,7 +6979,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c, · 18 unmodified lines
6978 nb = cstr_len;6979 nb = cstr_len;
6979 if (n >= 0 && nb > (n - len))6980 if (n >= 0 && nb > (n - len))
6980 nb = n - len;6981 nb = n - len;
6981 if (!size_only) {6982 if (!(flags & DIF_SIZE_ONLY)) {
6982 if (cstr_len > nb)6983 if (cstr_len > nb)
6983 tcc_warning("initializer-string for array is too long");6984 tcc_warning("initializer-string for array is too long");
6984 /* in order to go faster for common case (char6985 /* in order to go faster for common case (char
@@ -7004,7 +7005,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c, · 19 unmodified lines
7004 /* only add trailing zero if enough storage (no7005 /* only add trailing zero if enough storage (no
7005 warning in this case since it is standard) */7006 warning in this case since it is standard) */
7006 if (n < 0 || len < n) {7007 if (n < 0 || len < n) {
7007 if (!size_only) {7008 if (!(flags & DIF_SIZE_ONLY)) {
7008 vpushi(0);7009 vpushi(0);
7009 init_putv(t1, sec, c + (len * size1));7010 init_putv(t1, sec, c + (len * size1));
7010 }7011 }
@@ -7017,9 +7018,9 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c, · 6 unmodified lines
7017 7018
7018 do_init_list:7019 do_init_list:
7019 len = 0;7020 len = 0;
7020 while (tok != '}' || have_elem) {7021 while (tok != '}' || (flags & DIF_HAVE_ELEM)) {
7021 len = decl_designator(type, sec, c, &f, size_only, len);7022 len = decl_designator(type, sec, c, &f, flags, len);
7022 have_elem = 0;7023 flags &= ~DIF_HAVE_ELEM;
7023 if (type->t & VT_ARRAY) {7024 if (type->t & VT_ARRAY) {
7024 ++indexsym.c;7025 ++indexsym.c;
7025 /* special test for multi dimensional arrays (may not7026 /* special test for multi dimensional arrays (may not
@@ -7042,7 +7043,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c, · 16 unmodified lines
7042 }7043 }
7043 }7044 }
7044 /* put zeros at the end */7045 /* put zeros at the end */
7045 if (!size_only && len < n*size1)7046 if (!(flags & DIF_SIZE_ONLY) && len < n*size1)
7046 init_putz(sec, c + len, n*size1 - len);7047 init_putz(sec, c + len, n*size1 - len);
7047 if (!no_oblock)7048 if (!no_oblock)
7048 skip('}');7049 skip('}');
@@ -7052,7 +7053,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c, · 3 unmodified lines
7052 } else if ((type->t & VT_BTYPE) == VT_STRUCT) {7053 } else if ((type->t & VT_BTYPE) == VT_STRUCT) {
7053 size1 = 1;7054 size1 = 1;
7054 no_oblock = 1;7055 no_oblock = 1;
7055 if (first || tok == '{') {7056 if ((flags & DIF_FIRST) || tok == '{') {
7056 skip('{');7057 skip('{');
7057 no_oblock = 0;7058 no_oblock = 0;
7058 }7059 }
@@ -7061,20 +7062,22 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c, · 2 unmodified lines
7061 n = s->c;7062 n = s->c;
7062 goto do_init_list;7063 goto do_init_list;
7063 } else if (tok == '{') {7064 } else if (tok == '{') {
7065 if (flags & DIF_HAVE_ELEM)
7066 skip(';');
7064 next();7067 next();
7065 decl_initializer(type, sec, c, first, size_only);7068 decl_initializer(type, sec, c, flags & ~DIF_HAVE_ELEM);
7066 skip('}');7069 skip('}');
7067 } else if (size_only) {7070 } else if ((flags & DIF_SIZE_ONLY)) {
7068 /* If we supported only ISO C we wouldn't have to accept calling7071 /* If we supported only ISO C we wouldn't have to accept calling
7069 this on anything than an array size_only==1 (and even then7072 this on anything than an array if DIF_SIZE_ONLY (and even then
7070 only on the outermost level, so no recursion would be needed),7073 only on the outermost level, so no recursion would be needed),
7071 because initializing a flex array member isn't supported.7074 because initializing a flex array member isn't supported.
7072 But GNU C supports it, so we need to recurse even into7075 But GNU C supports it, so we need to recurse even into
7073 subfields of structs and arrays when size_only is set. */7076 subfields of structs and arrays when DIF_SIZE_ONLY is set. */
7074 /* just skip expression */7077 /* just skip expression */
7075 skip_or_save_block(NULL);7078 skip_or_save_block(NULL);
7076 } else {7079 } else {
7077 if (!have_elem) {7080 if (!(flags & DIF_HAVE_ELEM)) {
7078 /* This should happen only when we haven't parsed7081 /* This should happen only when we haven't parsed
7079 the init element above for fear of committing a7082 the init element above for fear of committing a
7080 string constant to memory too early. */7083 string constant to memory too early. */
@@ -7154,7 +7157,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 73 unmodified lines
7154 /* compute size */7157 /* compute size */
7155 begin_macro(init_str, 1);7158 begin_macro(init_str, 1);
7156 next();7159 next();
7157 decl_initializer(type, NULL, 0, 1, 1);7160 decl_initializer(type, NULL, 0, DIF_FIRST | DIF_SIZE_ONLY);
7158 /* prepare second initializer parsing */7161 /* prepare second initializer parsing */
7159 macro_ptr = init_str->str;7162 macro_ptr = init_str->str;
7160 next();7163 next();
@@ -7321,7 +7324,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 160 unmodified lines
7321 size_t oldreloc_offset = 0;7324 size_t oldreloc_offset = 0;
7322 if (sec && sec->reloc)7325 if (sec && sec->reloc)
7323 oldreloc_offset = sec->reloc->data_offset;7326 oldreloc_offset = sec->reloc->data_offset;
7324 decl_initializer(type, sec, addr, 1, 0);7327 decl_initializer(type, sec, addr, DIF_FIRST);
7325 if (sec && sec->reloc)7328 if (sec && sec->reloc)
7326 squeeze_multi_relocs(sec, oldreloc_offset);7329 squeeze_multi_relocs(sec, oldreloc_offset);
7327 /* patch flexible array member size back to -1, */7330 /* patch flexible array member size back to -1, */
tests/tests2/60_errors_and_warnings.c+10−0
@@ -140,5 +140,15 @@ int bar (const char *(*g)()) // should match this 'g' argument · 139 unmodified lines
140int foo(int ()) // abstract decl is wrong in definitions140int foo(int ()) // abstract decl is wrong in definitions
141{141{
142 return 0;142 return 0;
143#elif defined test_invalid_1
144void f(char*);
145void g(void) {
146 f((char[]){1, ,});
143}147}
148#elif defined test_invalid_2
149int ga = 0.42 { 2 };
150#elif defined test_invalid_3
151struct S { int a, b; };
152struct T { struct S x; };
153struct T gt = { 42 a: 1, 43 };
144#endif154#endif
tests/tests2/60_errors_and_warnings.expect+9−0
@@ -62,3 +62,12 @@ · 61 unmodified lines
62 62
63[test_abstract_decls]63[test_abstract_decls]
6460_errors_and_warnings.c:141: error: identifier expected6460_errors_and_warnings.c:141: error: identifier expected
65
66[test_invalid_1]
6760_errors_and_warnings.c:146: error: identifier expected
68
69[test_invalid_2]
7060_errors_and_warnings.c:149: error: ';' expected (got "{")
71
72[test_invalid_3]
7360_errors_and_warnings.c:153: error: ',' expected (got "a")

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t