aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2021-02-08 00:46:01 +0100
committerLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2021-02-25 00:18:26 +0100
commiteb55532d516c93a59946489114422c587a7cd0cd (patch)
tree74e537b7d87c64b2bf236c300f2a774d34999ff0
parent89a73382b14904cd762529a79ca74e8c2b389dbc (diff)
downloadsparse-eb55532d516c93a59946489114422c587a7cd0cd.tar.gz
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 <luc.vanoostenryck@gmail.com>
-rw-r--r--builtin.c73
-rw-r--r--validation/builtin-objsize-dyn.c22
-rw-r--r--validation/builtin-objsize0.c25
-rw-r--r--validation/builtin-objsize1.c21
4 files changed, 140 insertions, 1 deletions
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
+ */