aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
-rw-r--r--evaluate.c3
-rw-r--r--options.c8
-rw-r--r--options.h4
-rw-r--r--sparse.130
-rw-r--r--symbol.c67
-rw-r--r--symbol.h25
-rw-r--r--validation/flex-array-align.c18
-rw-r--r--validation/flex-array-array.c15
-rw-r--r--validation/flex-array-error.c26
-rw-r--r--validation/flex-array-nested.c29
-rw-r--r--validation/flex-array-sizeof.c18
-rw-r--r--validation/flex-array-union-array-no.c9
-rw-r--r--validation/flex-array-union-array-yes.c11
-rw-r--r--validation/flex-array-union-array.h11
14 files changed, 256 insertions, 18 deletions
diff --git a/evaluate.c b/evaluate.c
index 9106aa34..3ff76fa8 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -2256,6 +2256,9 @@ static struct symbol *evaluate_sizeof(struct expression *expr)
size = bits_in_char;
}
+ if (has_flexible_array(type) && Wflexible_array_sizeof)
+ warning(expr->pos, "using sizeof on a flexible structure");
+
if (is_array_type(type) && size < 0) { // VLA, 1-dimension only
struct expression *base, *size;
struct symbol *base_type;
diff --git a/options.c b/options.c
index 294dfd3b..e2a0717a 100644
--- a/options.c
+++ b/options.c
@@ -100,6 +100,10 @@ int Wdesignated_init = 1;
int Wdo_while = 0;
int Wenum_mismatch = 1;
int Wexternal_function_has_definition = 1;
+int Wflexible_array_array = 1;
+int Wflexible_array_nested = 0;
+int Wflexible_array_sizeof = 0;
+int Wflexible_array_union = 0;
int Wimplicit_int = 1;
int Winit_cstring = 0;
int Wint_to_pointer_cast = 1;
@@ -840,6 +844,10 @@ static const struct flag warnings[] = {
{ "do-while", &Wdo_while },
{ "enum-mismatch", &Wenum_mismatch },
{ "external-function-has-definition", &Wexternal_function_has_definition },
+ { "flexible-array-array", &Wflexible_array_array },
+ { "flexible-array-nested", &Wflexible_array_nested },
+ { "flexible-array-sizeof", &Wflexible_array_sizeof },
+ { "flexible-array-union", &Wflexible_array_union },
{ "implicit-int", &Wimplicit_int },
{ "init-cstring", &Winit_cstring },
{ "int-to-pointer-cast", &Wint_to_pointer_cast },
diff --git a/options.h b/options.h
index abdf0864..70c6ce9b 100644
--- a/options.h
+++ b/options.h
@@ -99,6 +99,10 @@ extern int Wdesignated_init;
extern int Wdo_while;
extern int Wenum_mismatch;
extern int Wexternal_function_has_definition;
+extern int Wflexible_array_array;
+extern int Wflexible_array_nested;
+extern int Wflexible_array_sizeof;
+extern int Wflexible_array_union;
extern int Wimplicit_int;
extern int Winit_cstring;
extern int Wint_to_pointer_cast;
diff --git a/sparse.1 b/sparse.1
index 48dab7a9..ed528fd1 100644
--- a/sparse.1
+++ b/sparse.1
@@ -257,6 +257,36 @@ Sparse issues these warnings by default. To turn them off, use
\fB\-Wno\-external\-function\-has\-definition\fR.
.
.TP
+.B -Wflexible-array-array
+Warn about arrays of structures containing a flexible array.
+
+Sparse issues these warnings by default. To turn them off, use
+\fB-Wno-flexible-array-array\fR.
+.
+.TP
+.B -Wflexible-array-nested
+Warn about structures containing a flexible array being contained into
+another structure, union or array.
+
+Sparse does not issue these warnings by default.
+.
+.TP
+.B -Wflexible-array-sizeof
+Warn about using the sizeof operator on a structure containing a flexible array,
+possibly recursively.
+
+Sparse does not issue these warnings by default.
+.
+.TP
+.B -Wflexible-array-union
+Enable the warnings regarding flexible arrays and unions.
+To have any effect, at least one of \fB-Wflexible-array-array\fR,
+\fB-Wflexible-array-nested\fR or \fB-Wflexible-array-sizeof\fR must also
+be enabled.
+
+Sparse does issue these warnings by default.
+.
+.TP
.B \-Winit\-cstring
Warn about initialization of a char array with a too long constant C string.
diff --git a/symbol.c b/symbol.c
index 7f0c8558..9ae827c1 100644
--- a/symbol.c
+++ b/symbol.c
@@ -87,6 +87,8 @@ struct struct_union_info {
unsigned long max_align;
unsigned long bit_size;
int align_size;
+ char has_flex_array;
+ struct symbol *flex_array;
};
/*
@@ -94,13 +96,8 @@ struct struct_union_info {
*/
static void lay_out_union(struct symbol *sym, struct struct_union_info *info)
{
- examine_symbol_type(sym);
-
- // Unnamed bitfields do not affect alignment.
- if (sym->ident || !is_bitfield_type(sym)) {
- if (sym->ctype.alignment > info->max_align)
- info->max_align = sym->ctype.alignment;
- }
+ if (sym->bit_size < 0 && is_array_type(sym))
+ sparse_error(sym->pos, "flexible array member '%s' in a union", show_ident(sym->ident));
if (sym->bit_size > info->bit_size)
info->bit_size = sym->bit_size;
@@ -125,24 +122,18 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info)
unsigned long bit_size, align_bit_mask;
int base_size;
- examine_symbol_type(sym);
-
- // Unnamed bitfields do not affect alignment.
- if (sym->ident || !is_bitfield_type(sym)) {
- if (sym->ctype.alignment > info->max_align)
- info->max_align = sym->ctype.alignment;
- }
-
bit_size = info->bit_size;
base_size = sym->bit_size;
/*
- * Unsized arrays cause us to not align the resulting
- * structure size
+ * If the member is unsized, either it's a flexible array or
+ * it's invalid and a warning has already been issued.
*/
if (base_size < 0) {
- info->align_size = 0;
+ if (!is_array_type(sym))
+ return;
base_size = 0;
+ info->flex_array = sym;
}
align_bit_mask = bytes_to_bits(sym->ctype.alignment) - 1;
@@ -196,6 +187,20 @@ static struct symbol * examine_struct_union_type(struct symbol *sym, int advance
sparse_error(member->pos, "member '%s' has __auto_type", show_ident(member->ident));
member->ctype.base_type = &incomplete_ctype;
}
+ if (info.flex_array)
+ sparse_error(info.flex_array->pos, "flexible array member '%s' is not last", show_ident(info.flex_array->ident));
+ examine_symbol_type(member);
+
+ if (member->ctype.alignment > info.max_align) {
+ // Unnamed bitfields do not affect alignment.
+ if (member->ident || !is_bitfield_type(member))
+ info.max_align = member->ctype.alignment;
+ }
+
+ if (has_flexible_array(member))
+ info.has_flex_array = 1;
+ if (has_flexible_array(member) && Wflexible_array_nested)
+ warning(sym->pos, "nested flexible arrays");
fn(member, &info);
} END_FOR_EACH_PTR(member);
@@ -206,6 +211,11 @@ static struct symbol * examine_struct_union_type(struct symbol *sym, int advance
bit_align = bytes_to_bits(sym->ctype.alignment)-1;
bit_size = (bit_size + bit_align) & ~bit_align;
}
+ if (info.flex_array) {
+ info.has_flex_array = 1;
+ }
+ if (info.has_flex_array && (!is_union_type(sym) || Wflexible_array_union))
+ sym->has_flex_array = 1;
sym->bit_size = bit_size;
return sym;
}
@@ -261,6 +271,8 @@ static struct symbol * examine_array_type(struct symbol *sym)
bit_size = -1;
}
}
+ if (has_flexible_array(base_type) && Wflexible_array_array)
+ warning(sym->pos, "array of flexible structures");
alignment = base_type->ctype.alignment;
if (!sym->ctype.alignment)
sym->ctype.alignment = alignment;
@@ -782,14 +794,17 @@ struct symbol bool_ctype, void_ctype, type_ctype,
incomplete_ctype, label_ctype, bad_ctype,
null_ctype;
struct symbol autotype_ctype;
+struct symbol schar_ptr_ctype, short_ptr_ctype;
struct symbol int_ptr_ctype, uint_ptr_ctype;
struct symbol long_ptr_ctype, ulong_ptr_ctype;
struct symbol llong_ptr_ctype, ullong_ptr_ctype;
+struct symbol size_t_ptr_ctype, intmax_ptr_ctype, ptrdiff_ptr_ctype;
struct symbol float32_ctype, float32x_ctype;
struct symbol float64_ctype, float64x_ctype;
struct symbol float128_ctype;
struct symbol const_void_ctype, const_char_ctype;
struct symbol const_ptr_ctype, const_string_ctype;
+struct symbol const_wchar_ctype, const_wstring_ctype;
struct symbol zero_int;
@@ -876,17 +891,24 @@ static const struct ctype_declare {
{ &null_ctype, T_PTR(&void_ctype) },
{ &label_ctype, T_PTR(&void_ctype) },
{ &lazy_ptr_ctype, T_PTR(&void_ctype) },
+ { &schar_ptr_ctype, T_PTR(&schar_ctype) },
+ { &short_ptr_ctype, T_PTR(&short_ctype) },
{ &int_ptr_ctype, T_PTR(&int_ctype) },
{ &uint_ptr_ctype, T_PTR(&uint_ctype) },
{ &long_ptr_ctype, T_PTR(&long_ctype) },
{ &ulong_ptr_ctype, T_PTR(&ulong_ctype) },
{ &llong_ptr_ctype, T_PTR(&llong_ctype) },
{ &ullong_ptr_ctype, T_PTR(&ullong_ctype) },
+ { &size_t_ptr_ctype, T_PTR(&void_ctype) }, // will be adjusted
+ { &intmax_ptr_ctype, T_PTR(&void_ctype) }, // will be adjusted
+ { &ptrdiff_ptr_ctype, T_PTR(&void_ctype) }, // will be adjusted
{ &const_ptr_ctype, T_PTR(&const_void_ctype) },
{ &const_string_ctype, T_PTR(&const_char_ctype) },
+ { &const_wstring_ctype,T_PTR(&const_wchar_ctype) },
{ &const_void_ctype, T_CONST(&void_ctype, NULL, NULL) },
{ &const_char_ctype, T_CONST(&char_ctype, &bits_in_char, &max_int_alignment)},
+ { &const_wchar_ctype, T_CONST(&int_ctype, NULL, NULL) },
{ NULL, }
};
@@ -931,4 +953,13 @@ void init_ctype(void)
intptr_ctype = ssize_t_ctype;
if (!uintptr_ctype)
uintptr_ctype = size_t_ctype;
+
+ size_t_ptr_ctype.ctype.base_type = size_t_ctype;
+ intmax_ptr_ctype.ctype.base_type = intmax_ctype;
+ ptrdiff_ptr_ctype.ctype.base_type = ptrdiff_ctype;
+
+ const_wchar_ctype.ctype.base_type = wchar_ctype;
+ const_wchar_ctype.rank = wchar_ctype->rank;
+ const_wchar_ctype.ctype.alignment = wchar_ctype->ctype.alignment;
+ const_wchar_ctype.bit_size = wchar_ctype->bit_size;
}
diff --git a/symbol.h b/symbol.h
index a3ed9567..e75ea3ab 100644
--- a/symbol.h
+++ b/symbol.h
@@ -185,6 +185,7 @@ struct symbol {
examined:1,
expanding:1,
evaluated:1,
+ has_flex_array:1,
string:1,
designated_init:1,
forced_arg:1,
@@ -298,14 +299,17 @@ extern struct symbol bool_ctype, void_ctype, type_ctype,
incomplete_ctype, label_ctype, bad_ctype,
null_ctype;
extern struct symbol autotype_ctype;
+extern struct symbol schar_ptr_ctype, short_ptr_ctype;
extern struct symbol int_ptr_ctype, uint_ptr_ctype;
extern struct symbol long_ptr_ctype, ulong_ptr_ctype;
extern struct symbol llong_ptr_ctype, ullong_ptr_ctype;
+extern struct symbol size_t_ptr_ctype, intmax_ptr_ctype, ptrdiff_ptr_ctype;
extern struct symbol float32_ctype, float32x_ctype;
extern struct symbol float64_ctype, float64x_ctype;
extern struct symbol float128_ctype;
extern struct symbol const_void_ctype, const_char_ctype;
extern struct symbol const_ptr_ctype, const_string_ctype;
+extern struct symbol const_wchar_ctype, const_wstring_ctype;
/* Special internal symbols */
extern struct symbol zero_int;
@@ -421,6 +425,20 @@ static inline int is_array_type(struct symbol *type)
return type->type == SYM_ARRAY;
}
+static inline int is_struct_type(struct symbol *type)
+{
+ if (type->type == SYM_NODE)
+ type = type->ctype.base_type;
+ return type->type == SYM_STRUCT;
+}
+
+static inline int is_union_type(struct symbol *type)
+{
+ if (type->type == SYM_NODE)
+ type = type->ctype.base_type;
+ return type->type == SYM_UNION;
+}
+
static inline int is_float_type(struct symbol *type)
{
if (type->type == SYM_NODE)
@@ -506,6 +524,13 @@ static inline int is_extern_inline(struct symbol *sym)
is_function(sym->ctype.base_type);
}
+static inline int has_flexible_array(struct symbol *type)
+{
+ if (type->type == SYM_NODE)
+ type = type->ctype.base_type;
+ return type->has_flex_array;
+}
+
static inline int get_sym_type(struct symbol *type)
{
if (type->type == SYM_NODE)
diff --git a/validation/flex-array-align.c b/validation/flex-array-align.c
new file mode 100644
index 00000000..9f28942a
--- /dev/null
+++ b/validation/flex-array-align.c
@@ -0,0 +1,18 @@
+struct s {
+ __INT32_TYPE__ x;
+ __INT16_TYPE__ y;
+ unsigned char f[];
+};
+
+static int foo(struct s *s)
+{
+ return (sizeof(*s) << 16) | __builtin_offsetof(typeof(*s), f);
+}
+
+/*
+ * check-name: flex-array-align
+ * check-command: test-linearize -Wno-flexible-array-sizeof $file
+ *
+ * check-output-ignore
+ * check-output-contains: ret\\..*\\$0x80006
+ */
diff --git a/validation/flex-array-array.c b/validation/flex-array-array.c
new file mode 100644
index 00000000..921a0698
--- /dev/null
+++ b/validation/flex-array-array.c
@@ -0,0 +1,15 @@
+struct s {
+ int i;
+ long f[];
+};
+
+static struct s a[2];
+
+/*
+ * check-name: flex-array-array
+ * check-command: sparse -Wflexible-array-array $file
+ *
+ * check-error-start
+flex-array-array.c:6:18: warning: array of flexible structures
+ * check-error-end
+ */
diff --git a/validation/flex-array-error.c b/validation/flex-array-error.c
new file mode 100644
index 00000000..2b7e6953
--- /dev/null
+++ b/validation/flex-array-error.c
@@ -0,0 +1,26 @@
+struct s {
+ int i;
+ long f[];
+ int j;
+};
+
+union u {
+ int i;
+ long f[];
+};
+
+// trigger the examination of the offending types
+static int foo(struct s *s, union u *u)
+{
+ return __builtin_offsetof(typeof(*s), i)
+ + __builtin_offsetof(typeof(*u), i);
+}
+
+/*
+ * check-name: flex-array-error
+ *
+ * check-error-start
+flex-array-error.c:3:14: error: flexible array member 'f' is not last
+flex-array-error.c:9:14: error: flexible array member 'f' in a union
+ * check-error-end
+ */
diff --git a/validation/flex-array-nested.c b/validation/flex-array-nested.c
new file mode 100644
index 00000000..63767683
--- /dev/null
+++ b/validation/flex-array-nested.c
@@ -0,0 +1,29 @@
+struct f {
+ int i;
+ long f[];
+};
+
+struct s {
+ struct f f;
+};
+
+union u {
+ struct f f;
+};
+
+// trigger the examination of the offending types
+static int foo(struct s *s, union u *u)
+{
+ return __builtin_offsetof(typeof(*s), f)
+ + __builtin_offsetof(typeof(*u), f);
+}
+
+/*
+ * check-name: flex-array-nested
+ * check-command: sparse -Wflexible-array-nested $file
+ *
+ * check-error-start
+flex-array-nested.c:6:8: warning: nested flexible arrays
+flex-array-nested.c:10:7: warning: nested flexible arrays
+ * check-error-end
+ */
diff --git a/validation/flex-array-sizeof.c b/validation/flex-array-sizeof.c
new file mode 100644
index 00000000..05394e19
--- /dev/null
+++ b/validation/flex-array-sizeof.c
@@ -0,0 +1,18 @@
+struct s {
+ int i;
+ long f[];
+};
+
+static int foo(struct s *s)
+{
+ return sizeof(*s);
+}
+
+/*
+ * check-name: flex-array-sizeof
+ * check-command: sparse -Wflexible-array-sizeof $file
+ *
+ * check-error-start
+flex-array-sizeof.c:8:16: warning: using sizeof on a flexible structure
+ * check-error-end
+ */
diff --git a/validation/flex-array-union-array-no.c b/validation/flex-array-union-array-no.c
new file mode 100644
index 00000000..5a1de787
--- /dev/null
+++ b/validation/flex-array-union-array-no.c
@@ -0,0 +1,9 @@
+#include "flex-array-union-array.h"
+
+/*
+ * check-name: flex-array-union-no
+ * check-command: sparse -Wflexible-array-array -Wno-flexible-array-union $file
+ *
+ * check-error-start
+ * check-error-end
+ */
diff --git a/validation/flex-array-union-array-yes.c b/validation/flex-array-union-array-yes.c
new file mode 100644
index 00000000..c2b71d65
--- /dev/null
+++ b/validation/flex-array-union-array-yes.c
@@ -0,0 +1,11 @@
+#include "flex-array-union-array.h"
+
+/*
+ * check-name: flex-array-union-yes
+ * check-command: sparse -Wflexible-array-array -Wflexible-array-union $file
+ *
+ * check-error-start
+flex-array-union-array-yes.c: note: in included file:
+flex-array-union-array.h:11:17: warning: array of flexible structures
+ * check-error-end
+ */
diff --git a/validation/flex-array-union-array.h b/validation/flex-array-union-array.h
new file mode 100644
index 00000000..b2a74d1a
--- /dev/null
+++ b/validation/flex-array-union-array.h
@@ -0,0 +1,11 @@
+struct s_flex {
+ int i;
+ long f[];
+};
+
+union s {
+ struct s_flex flex;
+ char buf[200];
+};
+
+static union s a[2];