The following is an attempt to implement -fsanitize=object-size. When it sees a MEM_REF, it goes through the definition statements and stops on sth like ptr = &sth. Then it tries to determine the object size using __builtin_object_size and generates an internal call (in .ubsan pass). The .sanopt pass then expands this internal call, and if the pointer points outside of the object, it issues a runtime error.
Bootstrapped(-ubsan)/regtested on x86_64-linux, ok for trunk? 2014-07-13 Marek Polacek <pola...@redhat.com> * ubsan.h (struct ubsan_mismatch_data): * asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE. * doc/invoke.texi: Document -fsanitize=object-size. * flag-types.h (enum sanitize_code): Add SANITIZE_OBJECT_SIZE and or it into SANITIZE_UNDEFINED. * internal-fn.c (expand_UBSAN_OBJECT_SIZE): New function. * internal-fn.def (UBSAN_OBJECT_SIZE): Define. * opts.c (common_handle_option): Handle -fsanitize=object-size. * ubsan.c: Include "tree-object-size.h". (ubsan_expand_objsize_ifn): New function. (instrument_object_size): New function. (pass_ubsan::execute): Add object size instrumentation. * ubsan.h (ubsan_expand_objsize_ifn): Declare. testsuite/ * c-c++-common/ubsan/object-size-1.c: New test. * c-c++-common/ubsan/object-size-2.c: New test. * c-c++-common/ubsan/object-size-3.c: New test. * c-c++-common/ubsan/object-size-4.c: New test. * c-c++-common/ubsan/object-size-5.c: New test. * c-c++-common/ubsan/object-size-6.c: New test. * c-c++-common/ubsan/object-size-7.c: New test. diff --git gcc/asan.c gcc/asan.c index b9a4a91..5954f95 100644 --- gcc/asan.c +++ gcc/asan.c @@ -2764,6 +2764,9 @@ pass_sanopt::execute (function *fun) case IFN_UBSAN_BOUNDS: ubsan_expand_bounds_ifn (&gsi); break; + case IFN_UBSAN_OBJECT_SIZE: + ubsan_expand_objsize_ifn (&gsi); + break; default: break; } diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi index 4807ffc..fbaac9d 100644 --- gcc/doc/invoke.texi +++ gcc/doc/invoke.texi @@ -5477,6 +5477,12 @@ This option enables instrumentation of array bounds. Various out of bounds accesses are detected. Flexible array members and initializers of variables with static storage are not instrumented. +@item -fsanitize=object-size +@opindex fsanitize=object-size +This option enables instrumentation of memory references using the +@code{__builtin_object_size} function. Various out of bounds pointer +accesses are detected. + @item -fsanitize=float-divide-by-zero @opindex fsanitize=float-divide-by-zero Detect floating-point division by zero. Unlike other similar options, diff --git gcc/flag-types.h gcc/flag-types.h index 2849455..63d199c 100644 --- gcc/flag-types.h +++ gcc/flag-types.h @@ -231,10 +231,11 @@ enum sanitize_code { SANITIZE_FLOAT_DIVIDE = 1 << 12, SANITIZE_FLOAT_CAST = 1 << 13, SANITIZE_BOUNDS = 1 << 14, + SANITIZE_OBJECT_SIZE = 1 << 15, SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM - | SANITIZE_BOUNDS, + | SANITIZE_BOUNDS | SANITIZE_OBJECT_SIZE, SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST }; diff --git gcc/internal-fn.c gcc/internal-fn.c index 78f59d6..eee3e85 100644 --- gcc/internal-fn.c +++ gcc/internal-fn.c @@ -167,6 +167,14 @@ expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED) gcc_unreachable (); } +/* This should get expanded in the sanopt pass. */ + +static void +expand_UBSAN_OBJECT_SIZE (gimple stmt ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); +} + /* Add sub/add overflow checking to the statement STMT. CODE says whether the operation is +, or -. */ diff --git gcc/internal-fn.def gcc/internal-fn.def index f0766bc..a2caf94 100644 --- gcc/internal-fn.def +++ gcc/internal-fn.def @@ -49,6 +49,7 @@ DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF) DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW) +DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW) DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW) diff --git gcc/opts.c gcc/opts.c index 419a074..00ec76f 100644 --- gcc/opts.c +++ gcc/opts.c @@ -1475,6 +1475,8 @@ common_handle_option (struct gcc_options *opts, { "float-cast-overflow", SANITIZE_FLOAT_CAST, sizeof "float-cast-overflow" - 1 }, { "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 }, + { "object-size", SANITIZE_OBJECT_SIZE, + sizeof "object-size" - 1 }, { NULL, 0, 0 } }; const char *comma; diff --git gcc/testsuite/c-c++-common/ubsan/object-size-1.c gcc/testsuite/c-c++-common/ubsan/object-size-1.c index e69de29..5295bed 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-1.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-1.c @@ -0,0 +1,124 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=undefined -O2" } */ + +/* Sanity-test -fsanitize=object-size. We use -fsanitize=undefined option + to check that this feature doesn't clash with -fsanitize=bounds et al. */ + +#define N 20 + +__attribute__((noinline, noclone)) void +f1 (int i) +{ + volatile int j; + char *p, *orig; + orig = p = (char *) __builtin_calloc (N, 1); + j = *(p + i); + j = p[i]; + p++; + j = p[i - 1]; + j = *(p + i - 1); + __builtin_free (orig); +} + +/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +f2 (int i) +{ + volatile int j; + char a[N]; + __builtin_memset (a, 0, N); + j = *(a + i); + char *p = a; + j = *(p + i); + j = p[i]; + p += 10; + j = *(p + i - 10); + j = p[i - 10]; +} + +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +f3 (int i) +{ + volatile int j; + int *p = (int *) __builtin_calloc (N, sizeof (*p)); + int *o = &p[i]; + j = *o; + j = o[0]; + __builtin_free (p); +} + +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +f4 (void) +{ + /* The second argument to __builtin_calloc is intentional. */ + int *p = (int *) __builtin_calloc (3, 1); + *p = 42; + __builtin_free (p); +} + +/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +f5 (int *p) +{ + /* This is not instrumented. But don't ICE, etc. */ + volatile int i = p[N]; +} + +int +main () +{ + f1 (N); + f2 (N); + f3 (N); + f4 (); + int *p = (int *) __builtin_calloc (N, sizeof (*p)); + f5 (p); + __builtin_free (p); + return 0; +} diff --git gcc/testsuite/c-c++-common/ubsan/object-size-2.c gcc/testsuite/c-c++-common/ubsan/object-size-2.c index e69de29..e604c81 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-2.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-2.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=undefined -O2" } */ + +void +foo (unsigned long ul) +{ + unsigned int u; + u = *(unsigned long *) ul; +} diff --git gcc/testsuite/c-c++-common/ubsan/object-size-3.c gcc/testsuite/c-c++-common/ubsan/object-size-3.c index e69de29..be08ab1 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-3.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-3.c @@ -0,0 +1,55 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=object-size -O2 -fno-sanitize-recover" } */ + +/* Test valid uses. */ + +#define N 20 + +__attribute__((noinline, noclone)) void +f1 (int i) +{ + volatile int j; + char *p, *orig; + orig = p = (char *) __builtin_calloc (N, 1); + j = *(p + i); + j = p[i]; + p++; + j = p[i - 1]; + j = *(p + i - 1); + __builtin_free (orig); +} + +__attribute__((noinline, noclone)) void +f2 (int i) +{ + volatile int j; + char a[N]; + __builtin_memset (a, 0, N); + j = *(a + i); + char *p = a; + j = *(p + i); + j = p[i]; + p += 10; + j = *(p + i - 10); + j = p[i - 10]; +} + +__attribute__((noinline, noclone)) void +f3 (int i) +{ + volatile int j; + int *p = (int *) __builtin_calloc (N, sizeof (*p)); + int *o = &p[i]; + j = *o; + j = o[0]; + __builtin_free (p); +} + +int +main () +{ + f1 (N - 1); + f2 (N - 1); + f3 (N - 1); + return 0; +} diff --git gcc/testsuite/c-c++-common/ubsan/object-size-4.c gcc/testsuite/c-c++-common/ubsan/object-size-4.c index e69de29..cc85bec 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-4.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-4.c @@ -0,0 +1,19 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=object-size -O2 -fno-sanitize-recover" } */ + +/* Test that we don't instrument flexible array members. */ + +struct T { int l; int a[]; }; +struct U { int l; int a[0]; }; + +int +main (void) +{ + /* Don't instrument flexible array members. */ + struct T *t = (struct T *) __builtin_malloc (sizeof (struct T) + 10); + t->a[1] = 1; + + struct U *u = (struct U *) __builtin_malloc (sizeof (struct U) + 10); + u->a[1] = 1; + return 0; +} diff --git gcc/testsuite/c-c++-common/ubsan/object-size-5.c gcc/testsuite/c-c++-common/ubsan/object-size-5.c index e69de29..9427590 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-5.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-5.c @@ -0,0 +1,61 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=object-size -O2" } */ + +/* Test structures with -fsanitize=object-size. */ + +#define N 20 + +struct S { char *p; int i; }; +struct T { struct S *s; }; + +__attribute__((noinline, noclone)) void +f1 (int i) +{ + volatile int j; + struct S s; + s.p = (char *) __builtin_calloc (N, 1); + j = s.p[i]; + j = *(s.p + i); + __builtin_free (s.p); +} + +/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +f2 (int i) +{ + volatile int j; + struct T t; + struct S s; + t.s = &s; + t.s->p = (char *) __builtin_calloc (N, 1); + j = t.s->p[i]; + j = *(t.s->p + i); + __builtin_free (t.s->p); +} + +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +int +main () +{ + f1 (N); + f2 (N); + f1 (N - 1); + f2 (N - 1); + return 0; +} diff --git gcc/testsuite/c-c++-common/ubsan/object-size-6.c gcc/testsuite/c-c++-common/ubsan/object-size-6.c index e69de29..08ba069 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-6.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-6.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=object-size -O2" } */ + +char +foo (void *v) +{ + return *(char *) v; +} diff --git gcc/testsuite/c-c++-common/ubsan/object-size-7.c gcc/testsuite/c-c++-common/ubsan/object-size-7.c index e69de29..67c4c11 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-7.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-7.c @@ -0,0 +1,28 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=object-size -O2" } */ + +#define N 20 + +struct S { int a; }; + +__attribute__((noinline, noclone)) struct S +f1 (int i) +{ + struct S a[N]; + struct S *p = a; + struct S s; + s = p[i]; + return s; +} + +int +main () +{ + f1 (N); + return 0; +} + +/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ diff --git gcc/ubsan.c gcc/ubsan.c index fd9bf20..3142f74 100644 --- gcc/ubsan.c +++ gcc/ubsan.c @@ -49,6 +49,7 @@ along with GCC; see the file COPYING3. If not see #include "intl.h" #include "realmpfr.h" #include "dfp.h" +#include "tree-object-size.h" /* Map from a tree to a VAR_DECL tree. */ @@ -604,12 +605,12 @@ ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi) /* Create condition "if (index > bound)". */ basic_block then_bb, fallthru_bb; gimple_stmt_iterator cond_insert_point - = create_cond_insert_point (gsi, 0/*before_p*/, false, true, + = create_cond_insert_point (gsi, false, false, true, &then_bb, &fallthru_bb); index = fold_convert (TREE_TYPE (bound), index); index = force_gimple_operand_gsi (&cond_insert_point, index, - true/*simple_p*/, NULL_TREE, - false/*before*/, GSI_NEW_STMT); + true, NULL_TREE, + false, GSI_NEW_STMT); gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE); gimple_set_location (g, loc); gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT); @@ -730,6 +731,87 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi) gsi_replace (&gsi, g, false); } +/* Expand UBSAN_OBJECT_SIZE internal call. */ + +void +ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi) +{ + gimple stmt = gsi_stmt (*gsi); + location_t loc = gimple_location (stmt); + gcc_assert (gimple_call_num_args (stmt) == 4); + + tree base = gimple_call_arg (stmt, 0); + tree ptr = gimple_call_arg (stmt, 1); + tree size = gimple_call_arg (stmt, 2); + tree ckind = gimple_call_arg (stmt, 3); + gimple_stmt_iterator gsi_orig = *gsi; + gimple g; + + gcc_assert (TREE_CODE (size) == INTEGER_CST); + /* See if we can discard the check. */ + if (tree_to_uhwi (size) == (unsigned HOST_WIDE_INT) -1) + /* Yes, __builtin_object_size couldn't determine the + object size. */; + else + { + /* if (ptr + sizeof (*ptr) > base + size) */ + basic_block then_bb, fallthru_bb; + gimple_stmt_iterator cond_insert_point + = create_cond_insert_point (gsi, false, false, true, + &then_bb, &fallthru_bb); + /* base + size */ + tree end = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (base), base, size); + end = force_gimple_operand_gsi (&cond_insert_point, end, true, NULL_TREE, + false, GSI_NEW_STMT); + /* ptr + sizeof (*ptr) */ + tree elm = TREE_TYPE (TREE_TYPE (ptr)); + if (TREE_CODE (elm) == ARRAY_TYPE) + elm = TREE_TYPE (elm); + tree ptrp1 = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr, + VOID_TYPE_P (elm) ? build_zero_cst (sizetype) + : TYPE_SIZE_UNIT (elm)); + ptrp1 = force_gimple_operand_gsi (&cond_insert_point, ptrp1, true, + NULL_TREE, false, GSI_NEW_STMT); + g = gimple_build_cond (GT_EXPR, ptrp1, end, NULL_TREE, NULL_TREE); + gimple_set_location (g, loc); + gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT); + + /* Generate __ubsan_handle_type_mismatch call. */ + *gsi = gsi_after_labels (then_bb); + if (flag_sanitize_undefined_trap_on_error) + g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + const struct ubsan_mismatch_data m + = { build_zero_cst (pointer_sized_int_node), ckind }; + tree data + = ubsan_create_data ("__ubsan_objsz_data", &loc, &m, + ubsan_type_descriptor (TREE_TYPE (ptr), + UBSAN_PRINT_POINTER), + NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + enum built_in_function bcode + = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH + : BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT; + tree p = make_ssa_name (pointer_sized_int_node, NULL); + g = gimple_build_assign_with_ops (NOP_EXPR, p, ptr, NULL_TREE); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + g = gimple_build_call (builtin_decl_explicit (bcode), 2, data, p); + } + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + + /* Point GSI to next logical statement. */ + *gsi = gsi_start_bb (fallthru_bb); + } + + /* Get rid of the UBSAN_OBJECT_SIZE call from the IR. */ + unlink_stmt_vdef (stmt); + gsi_remove (&gsi_orig, true); +} + /* Instrument a member call. We check whether 'this' is NULL. */ static void @@ -1121,6 +1203,62 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr) fn, integer_zero_node); } +/* Instrument memory references. Here we check whether the pointer + points to out-of-bounds location. */ + +static void +instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs) +{ + gimple stmt = gsi_stmt (*gsi); + location_t loc = gimple_location (stmt); + tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt); + + if (TREE_CODE (t) != MEM_REF) + return; + + tree ptr = TREE_OPERAND (t, 0); + tree sizet, base = ptr; + gimple g; + gimple def_stmt; + + while (TREE_CODE (base) == SSA_NAME) + { + def_stmt = SSA_NAME_DEF_STMT (base); + if (is_gimple_assign (def_stmt)) + base = gimple_assign_rhs1 (def_stmt); + else + break; + } + + if (!POINTER_TYPE_P (TREE_TYPE (base))) + return; + + unsigned HOST_WIDE_INT size; + size = compute_builtin_object_size (base, 0); + if (size != (unsigned HOST_WIDE_INT) -1) + sizet = build_int_cst (sizetype, size); + else if (optimize) + { + /* Generate __builtin_object_size call. */ + sizet = builtin_decl_explicit (BUILT_IN_OBJECT_SIZE); + sizet = build_call_expr_loc (loc, sizet, 2, base, integer_zero_node); + sizet = force_gimple_operand_gsi (gsi, sizet, false, NULL_TREE, true, + GSI_SAME_STMT); + } + else + return; + + /* Generate UBSAN_OBJECT_SIZE (base, ptr, size, ckind) call. */ + base = force_gimple_operand_gsi (gsi, base, true, NULL_TREE, true, + GSI_SAME_STMT); + enum ubsan_null_ckind ikind = is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF; + tree ckind = build_int_cst (unsigned_char_type_node, ikind); + g = gimple_build_call_internal (IFN_UBSAN_OBJECT_SIZE, 4, + base, ptr, sizet, ckind); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); +} + namespace { const pass_data pass_data_ubsan = @@ -1147,7 +1285,8 @@ public: virtual bool gate (function *) { return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW - | SANITIZE_BOOL | SANITIZE_ENUM) + | SANITIZE_BOOL | SANITIZE_ENUM + | SANITIZE_OBJECT_SIZE) && current_function_decl != NULL_TREE && !lookup_attribute ("no_sanitize_undefined", DECL_ATTRIBUTES (current_function_decl)); @@ -1192,6 +1331,14 @@ pass_ubsan::execute (function *fun) && gimple_assign_load_p (stmt)) instrument_bool_enum_load (&gsi); + if (flag_sanitize & SANITIZE_OBJECT_SIZE) + { + if (gimple_store_p (stmt)) + instrument_object_size (&gsi, true); + if (gimple_assign_load_p (stmt)) + instrument_object_size (&gsi, false); + } + gsi_next (&gsi); } } diff --git gcc/ubsan.h gcc/ubsan.h index 485449c..ae61c2d 100644 --- gcc/ubsan.h +++ gcc/ubsan.h @@ -44,6 +44,7 @@ struct ubsan_mismatch_data { }; extern void ubsan_expand_bounds_ifn (gimple_stmt_iterator *); +extern void ubsan_expand_objsize_ifn (gimple_stmt_iterator *); extern void ubsan_expand_null_ifn (gimple_stmt_iterator); extern tree ubsan_instrument_unreachable (location_t); extern tree ubsan_create_data (const char *, const location_t *, Marek