From eb55532d516c93a59946489114422c587a7cd0cd Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Mon, 8 Feb 2021 00:46:01 +0100 Subject: expand __builtin_object_size() __builtin_object_size() is one of these builtins that must be somehow expanded because it can't possibly be implemented at runtime. It's used by the kernel's copy_{to,from}_user() and the 'fortified' string functions, as well as by userspace's 'checked string/memory functions' like __builtin___memcpy_chk(). So, use the normal builtin expansion interface for this one too. This gets rid of 2/3 of them when used on the kernel and shaves ~0.5% of the total IR code (with x86's defconfig). Notes: 1) What is covered is an object symbol, with an optional designator of arbitrary complexity, ignoring casts and accessed via an optional chain of simple dereferences. Maybe some access path need to be added. 2) Anything with dynamic value is currently considered either as unknown (VLAs, variables or parameters) or left for a later stage (any function calls, including functions known to allocate memory given that attribute alloc_size() is not yet supported). 3) It's not totally clear to me when to give up (and thus return 'size unknown') and when things can or must be left to the simplification phase. This matters because __builtin_object_size() is relatively often used with __builtin_constant_p(). 4) Currently, only type 0 is really supported. Given the way designators are evaluated and expanded (information is lost because the expressions are overwritten), to support the other types, the expansion of __builtin_object_size() should be done during evaluation itself, much like it's done for sizeof() and offsetof(). Signed-off-by: Luc Van Oostenryck --- builtin.c | 73 +++++++++++++++++++++++++++++++++++++++- validation/builtin-objsize-dyn.c | 22 ++++++++++++ validation/builtin-objsize0.c | 25 ++++++++++++++ validation/builtin-objsize1.c | 21 ++++++++++++ 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 validation/builtin-objsize-dyn.c create mode 100644 validation/builtin-objsize0.c create mode 100644 validation/builtin-objsize1.c 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/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 + */ -- cgit 1.2.3-korg