diff --git a/builtin.c b/builtin.c index 5c7321ca..c7e7da3b 100644 --- a/builtin.c +++ b/builtin.c @@ -454,6 +454,77 @@ static struct symbol_op atomic_op = { }; +/// +// expand __builtin_object_size() +// +// :note: type 1 and type 3 are not supported because the +// needed information isn't available after evaluation. +static int expand_object_size(struct expression *expr, int cost) +{ + struct expression *arg = first_expression(expr->args); + int type = get_expression_value_silent(ptr_list_nth(expr->args, 1)); + unsigned long val = -1, off = 0; + + while (arg) { + switch (arg->type) { + case EXPR_IMPLIED_CAST: + case EXPR_CAST: + // ignore those + arg = arg->cast_expression; + continue; + case EXPR_BINOP: + // a constant add is (maybe) an offset + if (!arg->right || arg->op != '+' || arg->right->type != EXPR_VALUE) + break; + off += arg->right->value; + arg = arg->left; + continue; + case EXPR_PREOP: + // a deref is just intermediate variable + // and so the offset needs to be zeroed. + if (arg->op == '*') { + arg = arg->unop; + off = 0; + switch (arg->type) { + case EXPR_SYMBOL: + arg = arg->symbol->initializer; + continue; + default: + break; + } + } + break; + case EXPR_SYMBOL: + // the symbol we're looking after + val = bits_to_bytes(arg->symbol->bit_size); + break; + case EXPR_CALL: + // use alloc_size() attribute but only after linearization. + return UNSAFE; + default: + break; + } + break; + } + + if (val == -1) + val = (type & 2) ? 0 : val; + else if (type & 1) + return UNSAFE; + else + val -= off; + + expr->flags |= CEF_SET_ICE; + expr->type = EXPR_VALUE; + expr->value = val; + expr->taint = 0; + return 0; +} + +static struct symbol_op object_size_op = { + .expand = expand_object_size, +}; + /* * Builtin functions */ @@ -598,7 +669,7 @@ static const struct builtin_fn builtins_common[] = { { "__builtin_nan", &double_ctype, 0, { &const_string_ctype }}, { "__builtin_nanf", &float_ctype, 0, { &const_string_ctype }}, { "__builtin_nanl", &ldouble_ctype, 0, { &const_string_ctype }}, - { "__builtin_object_size", size_t_ctype, 0, { &const_ptr_ctype, &int_ctype }}, + { "__builtin_object_size", size_t_ctype, 0, { &const_ptr_ctype, &int_ctype }, .op = &object_size_op}, { "__builtin_parity", &int_ctype, 0, { &uint_ctype }, .op = &parity_op }, { "__builtin_parityl", &int_ctype, 0, { &ulong_ctype }, .op = &parity_op }, { "__builtin_parityll", &int_ctype, 0, { &ullong_ctype }, .op = &parity_op }, diff --git a/ptrlist.h b/ptrlist.h index 4bf8c709..c5fa4cdd 100644 --- a/ptrlist.h +++ b/ptrlist.h @@ -73,6 +73,10 @@ extern void __free_ptr_list(struct ptr_list **); __free_ptr_list((struct ptr_list **)(list)); \ } while (0) +#define ptr_list_nth(lst, nth) ({ \ + struct ptr_list* head = (struct ptr_list*)(lst); \ + (__typeof__((lst)->list[0])) ptr_list_nth_entry(head, nth);\ + }) //////////////////////////////////////////////////////////////////////// // API diff --git a/validation/builtin-objsize-dyn.c b/validation/builtin-objsize-dyn.c new file mode 100644 index 00000000..276c9204 --- /dev/null +++ b/validation/builtin-objsize-dyn.c @@ -0,0 +1,22 @@ +void *alloc(unsigned long)__attribute__((alloc_size(1))); + +_Bool sta(void) +{ + void *ptr = alloc(4); + return __builtin_object_size(ptr, 0) == 4; +} + +_Bool dyn(unsigned long n) +{ + void *ptr = alloc(n); + return __builtin_object_size(ptr, 0) == n; +} + +/* + * check-name: builtin-objsize-dyn + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-returns: 1 + */ diff --git a/validation/builtin-objsize0.c b/validation/builtin-objsize0.c new file mode 100644 index 00000000..9aab2ddd --- /dev/null +++ b/validation/builtin-objsize0.c @@ -0,0 +1,25 @@ +#define bos(O, T) __builtin_object_size(O, T) + +struct s { + char arr[8]; + __INT32_TYPE__ i; + __INT32_TYPE__ padding; +}; + +static struct s s; +static char *p = &s.arr[1]; +static int *q = &s.i; + +int obj_int0(void) { return bos(&s.i, 0) == 8; } +int obj_arr0(void) { return bos(&s.arr[1], 0) == 15; } + +int ptr_int(struct s *p) { return bos(&p->i, 0) == -1; } +int ptr_arr(struct s *p) { return bos(&p->arr[1], 0) == -1; } + +/* + * check-name: builtin-objsize0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-returns: 1 + */ diff --git a/validation/builtin-objsize1.c b/validation/builtin-objsize1.c new file mode 100644 index 00000000..1f285fc5 --- /dev/null +++ b/validation/builtin-objsize1.c @@ -0,0 +1,21 @@ +#define bos(O, T) __builtin_object_size(O, T) + +struct s { + char arr[8]; + __INT32_TYPE__ i; + __INT32_TYPE__ padding; +}; + +static struct s s; + +int obj_int1(void) { return bos(&s.i, 1) == 4; } +int obj_arr1(void) { return bos(&s.arr[1], 1) == 7; } + +/* + * check-name: builtin-objsize1 + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-returns: 1 + */