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
9393 static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td);
9494 static void parse_expr_type(CType *type);
9595 static void init_putv(CType *type, Section *sec, unsigned long c);
96−static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only);
96+static void decl_initializer(CType *type, Section *sec, unsigned long c, int flags);
9797 static void block(int *bsym, int *csym, int is_expr);
9898 static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
9999 static void decl(int l);
@@ -6596,14 +6596,18 @@ static void init_putz(Section *sec, unsigned long c, int size) · 6496 unmodified lines
65966596 }
65976597 }
65986598
6599+#define DIF_FIRST 1
6600+#define DIF_SIZE_ONLY 2
6601+#define DIF_HAVE_ELEM 4
6602+
65996603 /* t is the array or struct type. c is the array or struct
66006604 address. cur_field is the pointer to the current
66016605 field, for arrays the 'c' member contains the current start
6602− index. 'size_only' is true if only size info is needed (only used
6603− in arrays). al contains the already initialized length of the
6606+ index. 'flags' is as in decl_initializer.
6607+ 'al' contains the already initialized length of the
66046608 current container (starting at c). This returns the new length of that. */
66056609 static 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)
66076611 {
66086612 Sym *s, *f;
66096613 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
66116615
66126616 elem_size = 0;
66136617 nb_elems = 1;
6618+ if (flags & DIF_HAVE_ELEM)
6619+ goto no_designator;
66146620 if (gnu_ext && (l = is_label()) != 0)
66156621 goto struct_field;
66166622 /* 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
66596665 expect("=");
66606666 }
66616667 } else {
6668+ no_designator:
66626669 if (type->t & VT_ARRAY) {
66636670 index = (*cur_field)->c;
66646671 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
66776684 }
66786685 /* must put zero in holes (note that doing it that way
66796686 ensures that it even works with designators) */
6680− if (!size_only && c - corig > al)
6687+ if (!(flags & DIF_SIZE_ONLY) && c - corig > al)
66816688 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);
66836690
66846691 /* XXX: make it more general */
6685− if (!size_only && nb_elems > 1) {
6692+ if (!(flags & DIF_SIZE_ONLY) && nb_elems > 1) {
66866693 unsigned long c_end;
66876694 uint8_t *src, *dst;
66886695 int i;
@@ -6905,33 +6912,29 @@ static void init_putv(CType *type, Section *sec, unsigned long c) · 216 unmodified lines
69056912
69066913 /* 't' contains the type and storage info. 'c' is the offset of the
69076914 object in section 'sec'. If 'sec' is NULL, it means stack based
6908− allocation. 'first' is true if array '{' must be read (multi
6909− dimension implicit array init handling). 'size_only' is true if
6915+ allocation. 'flags & DIF_FIRST' is true if array '{' must be read (multi
6916+ dimension implicit array init handling). 'flags & DIF_SIZE_ONLY' is true if
69106917 size only evaluation is wanted (only for arrays). */
69116918 static void decl_initializer(CType *type, Section *sec, unsigned long c,
6912− int first, int size_only)
6919+ int flags)
69136920 {
69146921 int len, n, no_oblock, nb, i;
69156922 int size1, align1;
6916− int have_elem;
69176923 Sym *s, *f;
69186924 Sym indexsym;
69196925 CType *t1;
69206926
6921− /* If we currently are at an '}' or ',' we have read an initializer
6922− element in one of our callers, and not yet consumed it. */
6923− have_elem = tok == '}' || tok == ',';
6924− if (!have_elem && tok != '{' &&
6927+ if (!(flags & DIF_HAVE_ELEM) && tok != '{' &&
69256928 /* In case of strings we have special handling for arrays, so
69266929 don't consume them as initializer value (which would commit them
69276930 to some anonymous symbol). */
69286931 tok != TOK_LSTR && tok != TOK_STR &&
6929− !size_only) {
6932+ !(flags & DIF_SIZE_ONLY)) {
69306933 parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
6931− have_elem = 1;
6934+ flags |= DIF_HAVE_ELEM;
69326935 }
69336936
6934− if (have_elem &&
6937+ if ((flags & DIF_HAVE_ELEM) &&
69356938 !(type->t & VT_ARRAY) &&
69366939 /* Use i_c_parameter_t, to strip toplevel qualifiers.
69376940 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
69456948 size1 = type_size(t1, &align1);
69466949
69476950 no_oblock = 1;
6948− if ((first && tok != TOK_LSTR && tok != TOK_STR) ||
6951+ if (((flags & DIF_FIRST) && tok != TOK_LSTR && tok != TOK_STR) ||
69496952 tok == '{') {
69506953 if (tok != '{')
69516954 tcc_error("character array initializer must be a literal,"
69526955 " optionally enclosed in braces");
69536956 skip('{');
69546957 no_oblock = 0;
6955− if (tok == ',')
6956− tcc_error("unexpected ','");
69576958 }
69586959
69596960 /* 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
69786979 nb = cstr_len;
69796980 if (n >= 0 && nb > (n - len))
69806981 nb = n - len;
6981− if (!size_only) {
6982+ if (!(flags & DIF_SIZE_ONLY)) {
69826983 if (cstr_len > nb)
69836984 tcc_warning("initializer-string for array is too long");
69846985 /* 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
70047005 /* only add trailing zero if enough storage (no
70057006 warning in this case since it is standard) */
70067007 if (n < 0 || len < n) {
7007− if (!size_only) {
7008+ if (!(flags & DIF_SIZE_ONLY)) {
70087009 vpushi(0);
70097010 init_putv(t1, sec, c + (len * size1));
70107011 }
@@ -7017,9 +7018,9 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c, · 6 unmodified lines
70177018
70187019 do_init_list:
70197020 len = 0;
7020− while (tok != '}' || have_elem) {
7021− len = decl_designator(type, sec, c, &f, size_only, len);
7022− have_elem = 0;
7021+ while (tok != '}' || (flags & DIF_HAVE_ELEM)) {
7022+ len = decl_designator(type, sec, c, &f, flags, len);
7023+ flags &= ~DIF_HAVE_ELEM;
70237024 if (type->t & VT_ARRAY) {
70247025 ++indexsym.c;
70257026 /* 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
70427043 }
70437044 }
70447045 /* put zeros at the end */
7045− if (!size_only && len < n*size1)
7046+ if (!(flags & DIF_SIZE_ONLY) && len < n*size1)
70467047 init_putz(sec, c + len, n*size1 - len);
70477048 if (!no_oblock)
70487049 skip('}');
@@ -7052,7 +7053,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c, · 3 unmodified lines
70527053 } else if ((type->t & VT_BTYPE) == VT_STRUCT) {
70537054 size1 = 1;
70547055 no_oblock = 1;
7055− if (first || tok == '{') {
7056+ if ((flags & DIF_FIRST) || tok == '{') {
70567057 skip('{');
70577058 no_oblock = 0;
70587059 }
@@ -7061,20 +7062,22 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c, · 2 unmodified lines
70617062 n = s->c;
70627063 goto do_init_list;
70637064 } else if (tok == '{') {
7065+ if (flags & DIF_HAVE_ELEM)
7066+ skip(';');
70647067 next();
7065− decl_initializer(type, sec, c, first, size_only);
7068+ decl_initializer(type, sec, c, flags & ~DIF_HAVE_ELEM);
70667069 skip('}');
7067− } else if (size_only) {
7070+ } else if ((flags & DIF_SIZE_ONLY)) {
70687071 /* 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 then
7072+ this on anything than an array if DIF_SIZE_ONLY (and even then
70707073 only on the outermost level, so no recursion would be needed),
70717074 because initializing a flex array member isn't supported.
70727075 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. */
70747077 /* just skip expression */
70757078 skip_or_save_block(NULL);
70767079 } else {
7077− if (!have_elem) {
7080+ if (!(flags & DIF_HAVE_ELEM)) {
70787081 /* This should happen only when we haven't parsed
70797082 the init element above for fear of committing a
70807083 string constant to memory too early. */
@@ -7154,7 +7157,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 73 unmodified lines
71547157 /* compute size */
71557158 begin_macro(init_str, 1);
71567159 next();
7157− decl_initializer(type, NULL, 0, 1, 1);
7160+ decl_initializer(type, NULL, 0, DIF_FIRST | DIF_SIZE_ONLY);
71587161 /* prepare second initializer parsing */
71597162 macro_ptr = init_str->str;
71607163 next();
@@ -7321,7 +7324,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 160 unmodified lines
73217324 size_t oldreloc_offset = 0;
73227325 if (sec && sec->reloc)
73237326 oldreloc_offset = sec->reloc->data_offset;
7324− decl_initializer(type, sec, addr, 1, 0);
7327+ decl_initializer(type, sec, addr, DIF_FIRST);
73257328 if (sec && sec->reloc)
73267329 squeeze_multi_relocs(sec, oldreloc_offset);
73277330 /* 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
140140 int foo(int ()) // abstract decl is wrong in definitions
141141 {
142142 return 0;
143+#elif defined test_invalid_1
144+void f(char*);
145+void g(void) {
146+ f((char[]){1, ,});
143147 }
148+#elif defined test_invalid_2
149+int ga = 0.42 { 2 };
150+#elif defined test_invalid_3
151+struct S { int a, b; };
152+struct T { struct S x; };
153+struct T gt = { 42 a: 1, 43 };
144154 #endif
tests/tests2/60_errors_and_warnings.expect+9−0
@@ -62,3 +62,12 @@ · 61 unmodified lines
6262
6363 [test_abstract_decls]
6464 60_errors_and_warnings.c:141: error: identifier expected
65+
66+[test_invalid_1]
67+60_errors_and_warnings.c:146: error: identifier expected
68+
69+[test_invalid_2]
70+60_errors_and_warnings.c:149: error: ';' expected (got "{")
71+
72+[test_invalid_3]
73+60_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