diff options
-rw-r--r-- | evaluate.c | 26 | ||||
-rw-r--r-- | expand.c | 87 | ||||
-rw-r--r-- | memops.c | 11 | ||||
-rw-r--r-- | symbol.c | 41 | ||||
-rw-r--r-- | validation/eval/addressable-complex.c | 23 | ||||
-rw-r--r-- | validation/eval/addressable-degen.c | 17 | ||||
-rw-r--r-- | validation/expand/constant-init-array.c | 15 | ||||
-rw-r--r-- | validation/expand/constant-init-nested-array.c | 15 | ||||
-rw-r--r-- | validation/expand/constant-init-nested-struct.c | 23 | ||||
-rw-r--r-- | validation/expand/constant-init-string.c | 15 | ||||
-rw-r--r-- | validation/expand/constant-union-flt2int.c | 20 | ||||
-rw-r--r-- | validation/expand/constant-union-int2flt.c | 19 | ||||
-rw-r--r-- | validation/expand/constant-union-size.c (renamed from validation/bug-expand-union0.c) | 7 | ||||
-rw-r--r-- | validation/expand/cost-deref-nested.c | 20 | ||||
-rw-r--r-- | validation/expand/default-init-array.c | 16 | ||||
-rw-r--r-- | validation/expand/default-init-struct.c | 22 | ||||
-rw-r--r-- | validation/expand/union-cast.c | 27 | ||||
-rw-r--r-- | validation/memops/type-punning-flt2int.c (renamed from validation/bug-expand-union1.c) | 5 | ||||
-rw-r--r-- | validation/memops/type-punning-int2flt.c | 19 | ||||
-rw-r--r-- | validation/nocast.c | 10 |
20 files changed, 390 insertions, 48 deletions
@@ -110,6 +110,8 @@ static struct symbol *evaluate_string(struct expression *expr) sym->ctype.modifiers = MOD_STATIC; sym->ctype.base_type = array; sym->initializer = initstr; + sym->examined = 1; + sym->evaluated = 1; initstr->ctype = sym; initstr->string = expr->string; @@ -119,6 +121,8 @@ static struct symbol *evaluate_string(struct expression *expr) array->ctype.alignment = 1; array->ctype.modifiers = MOD_STATIC; array->ctype.base_type = &char_ctype; + array->examined = 1; + array->evaluated = 1; addr->symbol = sym; addr->ctype = &lazy_ptr_ctype; @@ -379,10 +383,8 @@ static inline int classify_type(struct symbol *type, struct symbol **base) if (type->type == SYM_NODE) type = type->ctype.base_type; if (type->type == SYM_TYPEOF) { - type = evaluate_expression(type->initializer); - if (!type) - type = &bad_ctype; - else if (type->type == SYM_NODE) + type = examine_symbol_type(type); + if (type->type == SYM_NODE) type = type->ctype.base_type; } if (type->type == SYM_ENUM) @@ -1540,6 +1542,16 @@ static int compatible_argument_type(struct expression *expr, struct symbol *targ return compatible_assignment_types(expr, target, rp, where); } +static void mark_addressable(struct expression *expr) +{ + while (expr->type == EXPR_BINOP && expr->op == '+') + expr = expr->left; + if (expr->type == EXPR_SYMBOL) { + struct symbol *sym = expr->symbol; + sym->ctype.modifiers |= MOD_ADDRESSABLE; + } +} + static void mark_assigned(struct expression *expr) { struct symbol *sym; @@ -1762,6 +1774,7 @@ static struct symbol *degenerate(struct expression *expr) *expr = *expr->unop; ctype = create_pointer(expr, ctype, 1); expr->ctype = ctype; + mark_addressable(expr); default: /* nothing */; } @@ -1780,10 +1793,7 @@ static struct symbol *evaluate_addressof(struct expression *expr) ctype = op->ctype; *expr = *op->unop; - if (expr->type == EXPR_SYMBOL) { - struct symbol *sym = expr->symbol; - sym->ctype.modifiers |= MOD_ADDRESSABLE; - } + mark_addressable(expr); /* * symbol expression evaluation is lazy about the type @@ -621,12 +621,70 @@ static int expand_addressof(struct expression *expr) return expand_expression(expr->unop); } +/// +// lookup the type of a struct's memeber at the requested offset +static struct symbol *find_member(struct symbol *sym, int offset) +{ + struct ptr_list *head, *list; + + head = (struct ptr_list *) sym->symbol_list; + list = head; + if (!head) + return NULL; + do { + int nr = list->nr; + int i; + for (i = 0; i < nr; i++) { + struct symbol *ent = (struct symbol *) list->list[i]; + int curr = ent->offset; + if (curr == offset) + return ent; + if (curr > offset) + return NULL; + } + } while ((list = list->next) != head); + return NULL; +} + +/// +// lookup a suitable default initializer value at the requested offset +static struct expression *default_initializer(struct symbol *sym, int offset) +{ + static struct expression value; + struct symbol *type; + +redo: + switch (sym->type) { + case SYM_NODE: + sym = sym->ctype.base_type; + goto redo; + case SYM_STRUCT: + type = find_member(sym, offset); + if (!type) + return NULL; + break; + case SYM_ARRAY: + type = sym->ctype.base_type; + break; + default: + return NULL; + } + + if (is_integral_type(type)) + value.type = EXPR_VALUE; + else if (is_float_type(type)) + value.type = EXPR_FVALUE; + else + return NULL; + + value.ctype = type; + return &value; +} + /* * Look up a trustable initializer value at the requested offset. * * Return NULL if no such value can be found or statically trusted. - * - * FIXME!! We should check that the size is right! */ static struct expression *constant_symbol_value(struct symbol *sym, int offset) { @@ -648,10 +706,11 @@ static struct expression *constant_symbol_value(struct symbol *sym, int offset) if (entry->init_offset < offset) continue; if (entry->init_offset > offset) - return NULL; + break; return entry->init_expr; } END_FOR_EACH_PTR(entry); - return NULL; + + value = default_initializer(sym, offset); } return value; } @@ -678,22 +737,26 @@ static int expand_dereference(struct expression *expr) * Is it "symbol" or "symbol + offset"? */ offset = 0; - if (unop->type == EXPR_BINOP && unop->op == '+') { + while (unop->type == EXPR_BINOP && unop->op == '+') { struct expression *right = unop->right; - if (right->type == EXPR_VALUE) { - offset = right->value; - unop = unop->left; - } + if (right->type != EXPR_VALUE) + break; + offset += right->value; + unop = unop->left; } if (unop->type == EXPR_SYMBOL) { struct symbol *sym = unop->symbol; + struct symbol *ctype = expr->ctype; struct expression *value = constant_symbol_value(sym, offset); /* Const symbol with a constant initializer? */ - if (value) { - /* FIXME! We should check that the size is right! */ + if (value && value->ctype) { + if (ctype->bit_size != value->ctype->bit_size) + return UNSAFE; if (value->type == EXPR_VALUE) { + if (!is_integral_type(ctype)) + return UNSAFE; if (is_bitfield_type(value->ctype)) return UNSAFE; expr->type = EXPR_VALUE; @@ -701,6 +764,8 @@ static int expand_dereference(struct expression *expr) expr->taint = 0; return 0; } else if (value->type == EXPR_FVALUE) { + if (!is_float_type(ctype)) + return UNSAFE; expr->type = EXPR_FVALUE; expr->fvalue = value->fvalue; return 0; @@ -82,6 +82,15 @@ static int local_pseudo(pseudo_t pseudo) && !address_taken(pseudo); } +static bool compatible_loads(struct instruction *a, struct instruction *b) +{ + if (is_integral_type(a->type) && is_float_type(b->type)) + return false; + if (is_float_type(a->type) && is_integral_type(b->type)) + return false; + return true; +} + static void simplify_loads(struct basic_block *bb) { struct instruction *insn; @@ -114,6 +123,8 @@ static void simplify_loads(struct basic_block *bb) continue; goto next_load; } + if (!compatible_loads(insn, dom)) + goto next_load; /* Yeehaa! Found one! */ convert_load_instruction(insn, dom->target); goto next_load; @@ -453,6 +453,25 @@ static struct symbol *examine_pointer_type(struct symbol *sym) return sym; } +static struct symbol *examine_typeof(struct symbol *sym) +{ + struct symbol *base = evaluate_expression(sym->initializer); + unsigned long mod = 0; + + if (!base) + base = &bad_ctype; + if (base->type == SYM_NODE) { + mod |= base->ctype.modifiers & MOD_TYPEOF; + base = base->ctype.base_type; + } + if (base->type == SYM_BITFIELD) + warning(base->pos, "typeof applied to bitfield type"); + sym->type = SYM_NODE; + sym->ctype.modifiers = mod; + sym->ctype.base_type = base; + return examine_node_type(sym); +} + /* * Fill in type size and alignment information for * regular SYM_TYPE things. @@ -486,26 +505,8 @@ struct symbol *examine_symbol_type(struct symbol * sym) case SYM_BASETYPE: /* Size and alignment had better already be set up */ return sym; - case SYM_TYPEOF: { - struct symbol *base = evaluate_expression(sym->initializer); - if (base) { - unsigned long mod = 0; - - if (is_bitfield_type(base)) - warning(base->pos, "typeof applied to bitfield type"); - if (base->type == SYM_NODE) { - mod |= base->ctype.modifiers & MOD_TYPEOF; - base = base->ctype.base_type; - } - sym->type = SYM_NODE; - sym->ctype.modifiers = mod; - sym->ctype.base_type = base; - return examine_node_type(sym); - } - sym->type = SYM_NODE; - sym->ctype.base_type = &bad_ctype; - return sym; - } + case SYM_TYPEOF: + return examine_typeof(sym); case SYM_PREPROCESSOR: sparse_error(sym->pos, "ctype on preprocessor command? (%s)", show_ident(sym->ident)); return NULL; diff --git a/validation/eval/addressable-complex.c b/validation/eval/addressable-complex.c new file mode 100644 index 00000000..e3d4aca4 --- /dev/null +++ b/validation/eval/addressable-complex.c @@ -0,0 +1,23 @@ +extern void def(void *); + +struct s1 { + int a; +}; + +int use1(void) +{ + struct s1 s = { 3 }; + + def(&s.a); + + return s.a; +} + +/* + * check-name: eval/addressable-complex + * check-command: test-linearize -Wno-decl -fdump-ir $file + * + * check-output-ignore + * check-output-contains: load\\. + * check-output-excludes: return\\..*\\$3 + */ diff --git a/validation/eval/addressable-degen.c b/validation/eval/addressable-degen.c new file mode 100644 index 00000000..d420927e --- /dev/null +++ b/validation/eval/addressable-degen.c @@ -0,0 +1,17 @@ +extern void def(void *, unsigned int); + +static int bar(void) +{ + int x[2] = { 1, 2 }; + + def(x, sizeof(x)); + return x[1]; +} + +/* + * check-name: eval/addressable-degen + * check-command: test-linearize -fdump-ir $file + * + * check-output-ignore + * check-output-contains: load\\. + */ diff --git a/validation/expand/constant-init-array.c b/validation/expand/constant-init-array.c new file mode 100644 index 00000000..94949be5 --- /dev/null +++ b/validation/expand/constant-init-array.c @@ -0,0 +1,15 @@ +int test_array(int i) +{ + static const int a[3] = { 1, 2, 3, }; + + return a[1]; +} + +/* + * check-name: constant-init-array + * check-command: test-linearize -Wno-decl -fdump-ir $file + * + * check-output-ignore + * check-output-excludes: phisrc\\..*return.*\\$2 + * check-output-contains: load\\. + */ diff --git a/validation/expand/constant-init-nested-array.c b/validation/expand/constant-init-nested-array.c new file mode 100644 index 00000000..0d50d955 --- /dev/null +++ b/validation/expand/constant-init-nested-array.c @@ -0,0 +1,15 @@ +int foo(void) +{ + int a[2][3] = {{0, 1, 2},{3, 4, 5}}; + return a[1][2]; +} + +/* + * check-name: constant-init-nested-array + * check-command: test-linearize -Wno-decl -fdump-ir $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: phisrc\\..*\\$5 + * check-output-excludes: load\\. + */ diff --git a/validation/expand/constant-init-nested-struct.c b/validation/expand/constant-init-nested-struct.c new file mode 100644 index 00000000..f27de556 --- /dev/null +++ b/validation/expand/constant-init-nested-struct.c @@ -0,0 +1,23 @@ +struct s { + int a; + struct { + int b; + int c; + } s; +}; + +int foo(void) +{ + struct s s = {1, {2, 3}}; + return s.s.c; +} + +/* + * check-name: constant-init-nested-struct + * check-command: test-linearize -Wno-decl -fdump-ir $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: phisrc\\..*\\$3 + * check-output-excludes: load\\. + */ diff --git a/validation/expand/constant-init-string.c b/validation/expand/constant-init-string.c new file mode 100644 index 00000000..42ae9bd3 --- /dev/null +++ b/validation/expand/constant-init-string.c @@ -0,0 +1,15 @@ +char foo(void) +{ + static const char s[] = "abc?"; + return s[3]; +} + +/* + * check-name: constant-init-nested-array + * check-command: test-linearize -Wno-decl -fdump-ir $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: phisrc\\..*\\$63 + * check-output-pattern(0,1): load\\. + */ diff --git a/validation/expand/constant-union-flt2int.c b/validation/expand/constant-union-flt2int.c new file mode 100644 index 00000000..5e25b592 --- /dev/null +++ b/validation/expand/constant-union-flt2int.c @@ -0,0 +1,20 @@ +union u { + int i; + float f; +}; + +static int foo(void) +{ + union u u = { .f = 0.123 }; + return u.i; +} + +/* + * check-name: constant-union-float-to-int + * check description: must not infer the int value from the float + * check-command: test-linearize -fdump-ir $file + * + * check-output-ignore + * check-output-pattern(1): setfval\\. + * check-output-pattern(1): load\\. + */ diff --git a/validation/expand/constant-union-int2flt.c b/validation/expand/constant-union-int2flt.c new file mode 100644 index 00000000..16ce1c6f --- /dev/null +++ b/validation/expand/constant-union-int2flt.c @@ -0,0 +1,19 @@ +union u { + int i; + float f; +}; + +static float foo(void) +{ + union u u = { .i = 3 }; + return u.f; +} + +/* + * check-name: constant-union-int-to-float + * check description: must not infer the float value from the int + * check-command: test-linearize -fdump-ir $file + * + * check-output-ignore + * check-output-pattern(1): load\\. + */ diff --git a/validation/bug-expand-union0.c b/validation/expand/constant-union-size.c index dd6d60c3..8a16bf3e 100644 --- a/validation/bug-expand-union0.c +++ b/validation/expand/constant-union-size.c @@ -10,10 +10,9 @@ static int foo(void) } /* - * check-name: bug-expand-union - * check description: must not infer the value from the float - * check-command: test-linearize $file - * check-known-to-fail + * check-name: constant-union-size + * check description: the size of the initializer doesn't match + * check-command: test-linearize -fdump-ir $file * * check-output-ignore * check-output-contains: load\\. diff --git a/validation/expand/cost-deref-nested.c b/validation/expand/cost-deref-nested.c new file mode 100644 index 00000000..d6b62396 --- /dev/null +++ b/validation/expand/cost-deref-nested.c @@ -0,0 +1,20 @@ +struct s { + struct { + int u, v; + } a, b; +}; + +static const struct s s; + +static int foo(int c) +{ + return c && s.b.v; +} + +/* + * check-name: cost-deref-nested + * check-command: test-linearize -fdump-ir $file + * + * check-output-ignore + * check-output-excludes: cbr + */ diff --git a/validation/expand/default-init-array.c b/validation/expand/default-init-array.c new file mode 100644 index 00000000..b372ea09 --- /dev/null +++ b/validation/expand/default-init-array.c @@ -0,0 +1,16 @@ +int test_array(int i) +{ + static const int a[3] = { [0] = 1, [2] = 3, }; + + return a[1]; +} + +/* + * check-name: default-init-array + * check-command: test-linearize -Wno-decl -fdump-ir $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: phisrc\\..*return.*\\$0 + * check-output-excludes: load\\. + */ diff --git a/validation/expand/default-init-struct.c b/validation/expand/default-init-struct.c new file mode 100644 index 00000000..085dd2d6 --- /dev/null +++ b/validation/expand/default-init-struct.c @@ -0,0 +1,22 @@ +struct s { + int a; + int b; + int c; +}; + + +int test_struct(void) +{ + struct s s = { .a = 1, .c = 3, }; + + return s.b; +} + +/* + * check-name: default-init-struct + * check-command: test-linearize -Wno-decl -fdump-ir $file + * + * check-output-ignore + * check-output-contains: phisrc\\..*return.*\\$0 + * check-output-excludes: load\\. + */ diff --git a/validation/expand/union-cast.c b/validation/expand/union-cast.c new file mode 100644 index 00000000..a28d01f2 --- /dev/null +++ b/validation/expand/union-cast.c @@ -0,0 +1,27 @@ +union u { + int i; + struct s { + int a; + } s; +}; + +int foo(void) +{ + struct s s = { 3 }; + union u u = (union u)s; + return u.s.a; +} + +/* + * check-name: union-cast + * check-command: test-linearize -Wno-decl -fdump-ir $file + * check-known-to-fail + * + * check-output-ignore + * check-output-excludes: load\\. + * + * check-error-start +union-cast.c:11:22: warning: cast to non-scalar +union-cast.c:11:22: warning: cast from non-scalar + * check-error-end + */ diff --git a/validation/bug-expand-union1.c b/validation/memops/type-punning-flt2int.c index 582a1f4f..fadaf687 100644 --- a/validation/bug-expand-union1.c +++ b/validation/memops/type-punning-flt2int.c @@ -10,10 +10,9 @@ static int foo(void) } /* - * check-name: bug-expand-union - * check description: must not infer the value from the float + * check-name: type-punning-float-to-int + * check description: must not infer the int value from the float * check-command: test-linearize $file - * check-known-to-fail * * check-output-ignore * check-output-contains: load\\. diff --git a/validation/memops/type-punning-int2flt.c b/validation/memops/type-punning-int2flt.c new file mode 100644 index 00000000..061b7423 --- /dev/null +++ b/validation/memops/type-punning-int2flt.c @@ -0,0 +1,19 @@ +union u { + int i; + float f; +}; + +static float foo(void) +{ + union u u = { .i = 3 }; + return u.f; +} + +/* + * check-name: type-punning-int-to-float + * check description: must not infer the float value from the int + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-contains: load\\. + */ diff --git a/validation/nocast.c b/validation/nocast.c index 6c5da968..2113227c 100644 --- a/validation/nocast.c +++ b/validation/nocast.c @@ -26,8 +26,8 @@ static ulong_nc_t good_deref(ulong_nc_t *t) /* assign value */ static ulong_nc_t t; -static ulong_nc_t good_assign_self = t; -static unsigned long good_assign_sametype = t; + + /* assign pointer */ static ulong_nc_t *good_ptr = &t; @@ -150,6 +150,12 @@ static unsigned long bad_fromcast(ulong_nc_t v) return (unsigned long) v; } +static void assign_value(void) +{ + ulong_nc_t good_assign_self = t; + unsigned long good_assign_sametype = t; +} + /* * check-name: nocast.c * |