aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2017-07-28 12:37:51 +0200
committerJakub Jelinek <jakub@gcc.gnu.org>2017-07-28 12:37:51 +0200
commitc9b39a4955f56fe609ef54784f7bf48c4cba6b1a (patch)
tree6ddac4284a4bae1e7241b22e28dcaaeb311277f9 /gcc
parent70affe6aff39d347a0e2b7f12a27e1cad4cae405 (diff)
downloadgcc-c9b39a4955f56fe609ef54784f7bf48c4cba6b1a.zip
gcc-c9b39a4955f56fe609ef54784f7bf48c4cba6b1a.tar.gz
gcc-c9b39a4955f56fe609ef54784f7bf48c4cba6b1a.tar.bz2
re PR sanitizer/80998 (Implement -fsanitize=pointer-overflow)
PR sanitizer/80998 * sanopt.c (pass_sanopt::execute): Handle IFN_UBSAN_PTR. * tree-ssa-alias.c (call_may_clobber_ref_p_1): Likewise. * flag-types.h (enum sanitize_code): Add SANITIZER_POINTER_OVERFLOW. Or it into SANITIZER_UNDEFINED. * ubsan.c: Include gimple-fold.h and varasm.h. (ubsan_expand_ptr_ifn): New function. (instrument_pointer_overflow): New function. (maybe_instrument_pointer_overflow): New function. (instrument_object_size): Formatting fix. (pass_ubsan::execute): Call instrument_pointer_overflow and maybe_instrument_pointer_overflow. * internal-fn.c (expand_UBSAN_PTR): New function. * ubsan.h (ubsan_expand_ptr_ifn): Declare. * sanitizer.def (__ubsan_handle_pointer_overflow, __ubsan_handle_pointer_overflow_abort): New builtins. * tree-ssa-tail-merge.c (merge_stmts_p): Handle IFN_UBSAN_PTR. * internal-fn.def (UBSAN_PTR): New internal function. * opts.c (sanitizer_opts): Add pointer-overflow. * lto-streamer-in.c (input_function): Handle IFN_UBSAN_PTR. * fold-const.c (build_range_check): Compute pointer range check in integral type if pointer arithmetics would be needed. Formatting fixes. gcc/testsuite/ * c-c++-common/ubsan/ptr-overflow-1.c: New test. * c-c++-common/ubsan/ptr-overflow-2.c: New test. libsanitizer/ * ubsan/ubsan_handlers.cc: Cherry-pick upstream r304461. * ubsan/ubsan_checks.inc: Likewise. * ubsan/ubsan_handlers.h: Likewise. From-SVN: r250656
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog26
-rw-r--r--gcc/flag-types.h4
-rw-r--r--gcc/fold-const.c26
-rw-r--r--gcc/internal-fn.c8
-rw-r--r--gcc/internal-fn.def1
-rw-r--r--gcc/lto-streamer-in.c4
-rw-r--r--gcc/opts.c1
-rw-r--r--gcc/sanitizer.def8
-rw-r--r--gcc/sanopt.c3
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/ptr-overflow-1.c65
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/ptr-overflow-2.c113
-rw-r--r--gcc/tree-ssa-alias.c1
-rw-r--r--gcc/tree-ssa-tail-merge.c1
-rw-r--r--gcc/ubsan.c302
-rw-r--r--gcc/ubsan.h1
16 files changed, 547 insertions, 21 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index e5b2be5..61d389e 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,29 @@
+2017-07-28 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/80998
+ * sanopt.c (pass_sanopt::execute): Handle IFN_UBSAN_PTR.
+ * tree-ssa-alias.c (call_may_clobber_ref_p_1): Likewise.
+ * flag-types.h (enum sanitize_code): Add SANITIZER_POINTER_OVERFLOW.
+ Or it into SANITIZER_UNDEFINED.
+ * ubsan.c: Include gimple-fold.h and varasm.h.
+ (ubsan_expand_ptr_ifn): New function.
+ (instrument_pointer_overflow): New function.
+ (maybe_instrument_pointer_overflow): New function.
+ (instrument_object_size): Formatting fix.
+ (pass_ubsan::execute): Call instrument_pointer_overflow
+ and maybe_instrument_pointer_overflow.
+ * internal-fn.c (expand_UBSAN_PTR): New function.
+ * ubsan.h (ubsan_expand_ptr_ifn): Declare.
+ * sanitizer.def (__ubsan_handle_pointer_overflow,
+ __ubsan_handle_pointer_overflow_abort): New builtins.
+ * tree-ssa-tail-merge.c (merge_stmts_p): Handle IFN_UBSAN_PTR.
+ * internal-fn.def (UBSAN_PTR): New internal function.
+ * opts.c (sanitizer_opts): Add pointer-overflow.
+ * lto-streamer-in.c (input_function): Handle IFN_UBSAN_PTR.
+ * fold-const.c (build_range_check): Compute pointer range check in
+ integral type if pointer arithmetics would be needed. Formatting
+ fixes.
+
2017-07-28 Martin Liska <mliska@suse.cz>
PR sanitizer/81460
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 5faade5..6372d3c 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -238,6 +238,7 @@ enum sanitize_code {
SANITIZE_OBJECT_SIZE = 1UL << 21,
SANITIZE_VPTR = 1UL << 22,
SANITIZE_BOUNDS_STRICT = 1UL << 23,
+ SANITIZE_POINTER_OVERFLOW = 1UL << 24,
SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
@@ -245,7 +246,8 @@ enum sanitize_code {
| SANITIZE_BOUNDS | SANITIZE_ALIGNMENT
| SANITIZE_NONNULL_ATTRIBUTE
| SANITIZE_RETURNS_NONNULL_ATTRIBUTE
- | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR,
+ | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR
+ | SANITIZE_POINTER_OVERFLOW,
SANITIZE_UNDEFINED_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
| SANITIZE_BOUNDS_STRICT
};
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index ecba3ff..d40b9aa 100644
--- a/gcc/fold-const.c
+++ b/gcc/fold-const.c
@@ -4859,21 +4859,21 @@ build_range_check (location_t loc, tree type, tree exp, int in_p,
if (low == 0)
return fold_build2_loc (loc, LE_EXPR, type, exp,
- fold_convert_loc (loc, etype, high));
+ fold_convert_loc (loc, etype, high));
if (high == 0)
return fold_build2_loc (loc, GE_EXPR, type, exp,
- fold_convert_loc (loc, etype, low));
+ fold_convert_loc (loc, etype, low));
if (operand_equal_p (low, high, 0))
return fold_build2_loc (loc, EQ_EXPR, type, exp,
- fold_convert_loc (loc, etype, low));
+ fold_convert_loc (loc, etype, low));
if (TREE_CODE (exp) == BIT_AND_EXPR
&& maskable_range_p (low, high, etype, &mask, &value))
return fold_build2_loc (loc, EQ_EXPR, type,
fold_build2_loc (loc, BIT_AND_EXPR, etype,
- exp, mask),
+ exp, mask),
value);
if (integer_zerop (low))
@@ -4905,7 +4905,7 @@ build_range_check (location_t loc, tree type, tree exp, int in_p,
exp = fold_convert_loc (loc, etype, exp);
}
return fold_build2_loc (loc, GT_EXPR, type, exp,
- build_int_cst (etype, 0));
+ build_int_cst (etype, 0));
}
}
@@ -4915,25 +4915,15 @@ build_range_check (location_t loc, tree type, tree exp, int in_p,
if (etype == NULL_TREE)
return NULL_TREE;
+ if (POINTER_TYPE_P (etype))
+ etype = unsigned_type_for (etype);
+
high = fold_convert_loc (loc, etype, high);
low = fold_convert_loc (loc, etype, low);
exp = fold_convert_loc (loc, etype, exp);
value = const_binop (MINUS_EXPR, high, low);
-
- if (POINTER_TYPE_P (etype))
- {
- if (value != 0 && !TREE_OVERFLOW (value))
- {
- low = fold_build1_loc (loc, NEGATE_EXPR, TREE_TYPE (low), low);
- return build_range_check (loc, type,
- fold_build_pointer_plus_loc (loc, exp, low),
- 1, build_int_cst (etype, 0), value);
- }
- return 0;
- }
-
if (value != 0 && !TREE_OVERFLOW (value))
return build_range_check (loc, type,
fold_build2_loc (loc, MINUS_EXPR, etype, exp, low),
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 1c9d1ea..e24ed16 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -402,6 +402,14 @@ expand_UBSAN_VPTR (internal_fn, gcall *)
/* This should get expanded in the sanopt pass. */
static void
+expand_UBSAN_PTR (internal_fn, gcall *)
+{
+ gcc_unreachable ();
+}
+
+/* This should get expanded in the sanopt pass. */
+
+static void
expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
{
gcc_unreachable ();
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 79c19fb..a9a3f76 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -166,6 +166,7 @@ DEF_INTERNAL_FN (UBSAN_VPTR, ECF_LEAF | ECF_NOTHROW, ".RR..")
DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (UBSAN_PTR, ECF_LEAF | ECF_NOTHROW, ".R.")
DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
diff --git a/gcc/lto-streamer-in.c b/gcc/lto-streamer-in.c
index ec47fe4..5710e8f 100644
--- a/gcc/lto-streamer-in.c
+++ b/gcc/lto-streamer-in.c
@@ -1143,6 +1143,10 @@ input_function (tree fn_decl, struct data_in *data_in,
if ((flag_sanitize & SANITIZE_OBJECT_SIZE) == 0)
remove = true;
break;
+ case IFN_UBSAN_PTR:
+ if ((flag_sanitize & SANITIZE_POINTER_OVERFLOW) == 0)
+ remove = true;
+ break;
case IFN_ASAN_MARK:
if ((flag_sanitize & SANITIZE_ADDRESS) == 0)
remove = true;
diff --git a/gcc/opts.c b/gcc/opts.c
index ec45a0f..2f9a638 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1521,6 +1521,7 @@ const struct sanitizer_opts_s sanitizer_opts[] =
true),
SANITIZER_OPT (object-size, SANITIZE_OBJECT_SIZE, true),
SANITIZER_OPT (vptr, SANITIZE_VPTR, true),
+ SANITIZER_OPT (pointer-overflow, SANITIZE_POINTER_OVERFLOW, true),
SANITIZER_OPT (all, ~0U, true),
#undef SANITIZER_OPT
{ NULL, 0U, 0UL, false }
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 91759a8..c90fa94 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -448,6 +448,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE,
"__ubsan_handle_load_invalid_value",
BT_FN_VOID_PTR_PTR,
ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_POINTER_OVERFLOW,
+ "__ubsan_handle_pointer_overflow",
+ BT_FN_VOID_PTR_PTR_PTR,
+ ATTR_COLD_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT,
"__ubsan_handle_divrem_overflow_abort",
BT_FN_VOID_PTR_PTR_PTR,
@@ -484,6 +488,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE_ABORT,
"__ubsan_handle_load_invalid_value_abort",
BT_FN_VOID_PTR_PTR,
ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_POINTER_OVERFLOW_ABORT,
+ "__ubsan_handle_pointer_overflow_abort",
+ BT_FN_VOID_PTR_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW,
"__ubsan_handle_float_cast_overflow",
BT_FN_VOID_PTR_PTR,
diff --git a/gcc/sanopt.c b/gcc/sanopt.c
index f6a3d6e..b845f2f 100644
--- a/gcc/sanopt.c
+++ b/gcc/sanopt.c
@@ -1063,6 +1063,9 @@ pass_sanopt::execute (function *fun)
case IFN_UBSAN_OBJECT_SIZE:
no_next = ubsan_expand_objsize_ifn (&gsi);
break;
+ case IFN_UBSAN_PTR:
+ no_next = ubsan_expand_ptr_ifn (&gsi);
+ break;
case IFN_UBSAN_VPTR:
no_next = ubsan_expand_vptr_ifn (&gsi);
break;
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 26c47b2..5c41f43 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -5,6 +5,10 @@
2017-07-28 Jakub Jelinek <jakub@redhat.com>
+ PR sanitizer/80998
+ * c-c++-common/ubsan/ptr-overflow-1.c: New test.
+ * c-c++-common/ubsan/ptr-overflow-2.c: New test.
+
PR tree-optimization/81578
* gcc.dg/pr81578.c: New test.
diff --git a/gcc/testsuite/c-c++-common/ubsan/ptr-overflow-1.c b/gcc/testsuite/c-c++-common/ubsan/ptr-overflow-1.c
new file mode 100644
index 0000000..8edfbce
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/ptr-overflow-1.c
@@ -0,0 +1,65 @@
+/* PR sanitizer/80998 */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow -Wall" } */
+
+struct S { int a; int b; int c[64]; };
+__attribute__((noinline, noclone)) char *f1 (char *p) { return p + 1; }
+__attribute__((noinline, noclone)) char *f2 (char *p) { return p - 1; }
+__attribute__((noinline, noclone)) char *f3 (char *p, int i) { return p + i; }
+__attribute__((noinline, noclone)) char *f4 (char *p, int i) { return p - i; }
+__attribute__((noinline, noclone)) char *f5 (char *p, unsigned long int i) { return p + i; }
+__attribute__((noinline, noclone)) char *f6 (char *p, unsigned long int i) { return p - i; }
+__attribute__((noinline, noclone)) int *f7 (struct S *p) { return &p->a; }
+__attribute__((noinline, noclone)) int *f8 (struct S *p) { return &p->b; }
+__attribute__((noinline, noclone)) int *f9 (struct S *p) { return &p->c[64]; }
+__attribute__((noinline, noclone)) int *f10 (struct S *p, int i) { return &p->c[i]; }
+
+char *volatile p;
+struct S *volatile q;
+char a[64];
+struct S s;
+int *volatile r;
+
+int
+main ()
+{
+ struct S t;
+ p = &a[32];
+ p = f1 (p);
+ p = f1 (p);
+ p = f2 (p);
+ p = f3 (p, 1);
+ p = f3 (p, -1);
+ p = f3 (p, 3);
+ p = f3 (p, -6);
+ p = f4 (p, 1);
+ p = f4 (p, -1);
+ p = f4 (p, 3);
+ p = f4 (p, -6);
+ p = f5 (p, 1);
+ p = f5 (p, 3);
+ p = f6 (p, 1);
+ p = f6 (p, 3);
+ if (sizeof (unsigned long) >= sizeof (char *))
+ {
+ p = f5 (p, -1);
+ p = f5 (p, -6);
+ p = f6 (p, -1);
+ p = f6 (p, -6);
+ }
+ q = &s;
+ r = f7 (q);
+ r = f8 (q);
+ r = f9 (q);
+ r = f10 (q, 0);
+ r = f10 (q, 10);
+ r = f10 (q, 64);
+ q = &t;
+ r = f7 (q);
+ r = f8 (q);
+ r = f9 (q);
+ r = f10 (q, 0);
+ r = f10 (q, 10);
+ r = f10 (q, 64);
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/ptr-overflow-2.c b/gcc/testsuite/c-c++-common/ubsan/ptr-overflow-2.c
new file mode 100644
index 0000000..a1110a2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/ptr-overflow-2.c
@@ -0,0 +1,113 @@
+/* PR sanitizer/80998 */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=pointer-overflow -fsanitize-recover=pointer-overflow -fno-ipa-icf -Wall" } */
+
+__attribute__((noinline, noclone)) char * f1 (char *p) { return p + 1; }
+__attribute__((noinline, noclone)) char * f2 (char *p) { return p - 1; }
+__attribute__((noinline, noclone)) char * f3 (char *p, int i) { return p + i; }
+__attribute__((noinline, noclone)) char * f4 (char *p, int i) { return p + i; }
+__attribute__((noinline, noclone)) char * f5 (char *p, int i) { return p - i; }
+__attribute__((noinline, noclone)) char * f6 (char *p, int i) { return p - i; }
+__attribute__((noinline, noclone)) char * f7 (char *p, unsigned long int i) { return p + i; }
+__attribute__((noinline, noclone)) char * f8 (char *p, unsigned long int i) { return p + i; }
+__attribute__((noinline, noclone)) char * f9 (char *p, unsigned long int i) { return p - i; }
+__attribute__((noinline, noclone)) char * f10 (char *p, unsigned long int i) { return p - i; }
+struct S { int a; int b; int c[64]; };
+__attribute__((noinline, noclone)) int *f11 (struct S *p) { return &p->a; }
+__attribute__((noinline, noclone)) int *f12 (struct S *p) { return &p->b; }
+__attribute__((noinline, noclone)) int *f13 (struct S *p) { return &p->c[64]; }
+__attribute__((noinline, noclone)) int *f14 (struct S *p, int i) { return &p->c[i]; }
+__attribute__((noinline, noclone)) int *f15 (struct S *p, int i) { return &p->c[i]; }
+__attribute__((noinline, noclone)) int *f16 (struct S *p) { return &p->a; }
+__attribute__((noinline, noclone)) int *f17 (struct S *p) { return &p->b; }
+__attribute__((noinline, noclone)) int *f18 (struct S *p) { return &p->c[64]; }
+__attribute__((noinline, noclone)) int *f19 (struct S *p, int i) { return &p->c[i]; }
+__attribute__((noinline, noclone)) int *f20 (struct S *p, int i) { return &p->c[i]; }
+__attribute__((noinline, noclone)) int *f21 (struct S *p) { return &p->a; }
+__attribute__((noinline, noclone)) int *f22 (struct S *p) { return &p->b; }
+__attribute__((noinline, noclone)) int *f23 (struct S *p) { return &p->c[64]; }
+__attribute__((noinline, noclone)) int *f24 (struct S *p, int i) { return &p->c[i]; }
+__attribute__((noinline, noclone)) int *f25 (struct S *p, int i) { return &p->c[i]; }
+
+char *volatile p;
+__UINTPTR_TYPE__ volatile u;
+struct S *volatile q;
+int *volatile r;
+
+int
+main ()
+{
+ u = ~(__UINTPTR_TYPE__) 0;
+ p = (char *) u;
+ p = f1 (p);
+ u = 0;
+ p = (char *) u;
+ p = f2 (p);
+ u = -(__UINTPTR_TYPE__) 7;
+ p = (char *) u;
+ p = f3 (p, 7);
+ u = 3;
+ p = (char *) u;
+ p = f4 (p, -4);
+ u = 23;
+ p = (char *) u;
+ p = f5 (p, 27);
+ u = -(__UINTPTR_TYPE__) 15;
+ p = (char *) u;
+ p = f6 (p, -15);
+ u = -(__UINTPTR_TYPE__) 29;
+ p = (char *) u;
+ p = f7 (p, 31);
+ u = 23;
+ p = (char *) u;
+ p = f9 (p, 24);
+ if (sizeof (unsigned long) < sizeof (char *))
+ return 0;
+ u = 7;
+ p = (char *) u;
+ p = f8 (p, -8);
+ u = -(__UINTPTR_TYPE__) 25;
+ p = (char *) u;
+ p = f10 (p, -25);
+ u = ~(__UINTPTR_TYPE__) 0;
+ q = (struct S *) u;
+ r = f11 (q);
+ r = f12 (q);
+ r = f13 (q);
+ r = f14 (q, 0);
+ r = f15 (q, 63);
+ u = ~(__UINTPTR_TYPE__) 0 - (17 * sizeof (int));
+ q = (struct S *) u;
+ r = f16 (q);
+ r = f17 (q);
+ r = f18 (q);
+ r = f19 (q, 0);
+ r = f20 (q, 63);
+ u = 3 * sizeof (int);
+ q = (struct S *) u;
+ r = f21 (q);
+ r = f22 (q);
+ r = f23 (q);
+ r = f24 (q, -2);
+ r = f25 (q, -6);
+ return 0;
+}
+
+/* { dg-output ":5:6\[79]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?\[fF]\+ overflowed to (0\[xX])?0\+(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*:6:6\[79]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?0\+ overflowed to (0\[xX])?\[fF]\+(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*:7:7\[46]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?\[fF]\+9 overflowed to (0\[xX])?0\+(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*:8:7\[46]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?0\+3 overflowed to (0\[xX])?\[fF]\+(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*:9:7\[46]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?0\+17 overflowed to (0\[xX])?\[fF]\+\[cC](\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*:10:7\[46]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?\[fF]\+1 overflowed to (0\[xX])?0\+(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*:11:\[89]\[80]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?\[fF]\+\[eE]3 overflowed to (0\[xX])?0\+2(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*:13:\[89]\[80]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?0\+17 overflowed to (0\[xX])?\[fF]\+(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*:12:\[89]\[80]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?0\+7 overflowed to (0\[xX])?\[fF]\+(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*:14:\[89]\[91]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?\[fF]\+\[eE]7 overflowed to (0\[xX])?0\+" } */
+/* { dg-output "(\n|\r\n|\r)" { target int32 } } */
+/* { dg-output "\[^\n\r]*:17:\[67]\[82]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?\[fF]\+ overflowed to (0\[xX])?0\+3(\n|\r\n|\r)" { target int32 } } */
+/* { dg-output "\[^\n\r]*:18:\[67]\[86]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?\[fF]\+ overflowed to (0\[xX])?0\+107(\n|\r\n|\r)" { target int32 } } */
+/* { dg-output "\[^\n\r]*:19:\[78]\[52]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?\[fF]\+ overflowed to (0\[xX])?0\+7(\n|\r\n|\r)" { target int32 } } */
+/* { dg-output "\[^\n\r]*:20:\[78]\[52]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?\[fF]\+ overflowed to (0\[xX])?0\+103(\n|\r\n|\r)" { target int32 } } */
+/* { dg-output "\[^\n\r]*:23:\[67]\[86]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?\[fF]\+\[bB]\[bB] overflowed to (0\[xX])?0\+\[cC]3(\n|\r\n|\r)" { target int32 } } */
+/* { dg-output "\[^\n\r]*:25:\[78]\[52]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?\[fF]\+\[bB]\[bB] overflowed to (0\[xX])?0\+\[bB]\[fF](\n|\r\n|\r)" { target int32 } } */
+/* { dg-output "\[^\n\r]*:30:\[78]\[52]\[^\n\r]*runtime error: pointer index expression with base (0\[xX])?0\+\[cC] overflowed to (0\[xX])?\[fF]\+\[cC]" { target int32 } } */
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 74ee2b0..5794d22 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -1991,6 +1991,7 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
case IFN_UBSAN_BOUNDS:
case IFN_UBSAN_VPTR:
case IFN_UBSAN_OBJECT_SIZE:
+ case IFN_UBSAN_PTR:
case IFN_ASAN_CHECK:
return false;
default:
diff --git a/gcc/tree-ssa-tail-merge.c b/gcc/tree-ssa-tail-merge.c
index b11911b..a65ff31 100644
--- a/gcc/tree-ssa-tail-merge.c
+++ b/gcc/tree-ssa-tail-merge.c
@@ -1241,6 +1241,7 @@ merge_stmts_p (gimple *stmt1, gimple *stmt2)
case IFN_UBSAN_CHECK_SUB:
case IFN_UBSAN_CHECK_MUL:
case IFN_UBSAN_OBJECT_SIZE:
+ case IFN_UBSAN_PTR:
case IFN_ASAN_CHECK:
/* For these internal functions, gimple_location is an implicit
parameter, which will be used explicitly after expansion.
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index 8ea352a..cca3c2d 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -45,6 +45,8 @@ along with GCC; see the file COPYING3. If not see
#include "builtins.h"
#include "tree-object-size.h"
#include "tree-cfg.h"
+#include "gimple-fold.h"
+#include "varasm.h"
/* Map from a tree to a VAR_DECL tree. */
@@ -1029,6 +1031,170 @@ ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi)
return true;
}
+/* Expand UBSAN_PTR internal call. */
+
+bool
+ubsan_expand_ptr_ifn (gimple_stmt_iterator *gsip)
+{
+ gimple_stmt_iterator gsi = *gsip;
+ gimple *stmt = gsi_stmt (gsi);
+ location_t loc = gimple_location (stmt);
+ gcc_assert (gimple_call_num_args (stmt) == 2);
+ tree ptr = gimple_call_arg (stmt, 0);
+ tree off = gimple_call_arg (stmt, 1);
+
+ if (integer_zerop (off))
+ {
+ gsi_remove (gsip, true);
+ unlink_stmt_vdef (stmt);
+ return true;
+ }
+
+ basic_block cur_bb = gsi_bb (gsi);
+ tree ptrplusoff = make_ssa_name (pointer_sized_int_node);
+ tree ptri = make_ssa_name (pointer_sized_int_node);
+ int pos_neg = get_range_pos_neg (off);
+
+ /* Split the original block holding the pointer dereference. */
+ edge e = split_block (cur_bb, stmt);
+
+ /* Get a hold on the 'condition block', the 'then block' and the
+ 'else block'. */
+ basic_block cond_bb = e->src;
+ basic_block fallthru_bb = e->dest;
+ basic_block then_bb = create_empty_bb (cond_bb);
+ basic_block cond_pos_bb = NULL, cond_neg_bb = NULL;
+ add_bb_to_loop (then_bb, cond_bb->loop_father);
+ loops_state_set (LOOPS_NEED_FIXUP);
+
+ /* Set up the fallthrough basic block. */
+ e->flags = EDGE_FALSE_VALUE;
+ if (pos_neg != 3)
+ {
+ e->count = cond_bb->count;
+ e->probability = profile_probability::very_likely ();
+
+ /* Connect 'then block' with the 'else block'. This is needed
+ as the ubsan routines we call in the 'then block' are not noreturn.
+ The 'then block' only has one outcoming edge. */
+ make_single_succ_edge (then_bb, fallthru_bb, EDGE_FALLTHRU);
+
+ /* Make an edge coming from the 'cond block' into the 'then block';
+ this edge is unlikely taken, so set up the probability
+ accordingly. */
+ e = make_edge (cond_bb, then_bb, EDGE_TRUE_VALUE);
+ e->probability = profile_probability::very_unlikely ();
+ }
+ else
+ {
+ profile_count count = cond_bb->count.apply_probability (PROB_EVEN);
+ e->count = count;
+ e->probability = profile_probability::even ();
+
+ e = split_block (fallthru_bb, (gimple *) NULL);
+ cond_neg_bb = e->src;
+ fallthru_bb = e->dest;
+ e->count = count;
+ e->probability = profile_probability::very_likely ();
+ e->flags = EDGE_FALSE_VALUE;
+
+ e = make_edge (cond_neg_bb, then_bb, EDGE_TRUE_VALUE);
+ e->probability = profile_probability::very_unlikely ();
+
+ cond_pos_bb = create_empty_bb (cond_bb);
+ add_bb_to_loop (cond_pos_bb, cond_bb->loop_father);
+
+ e = make_edge (cond_bb, cond_pos_bb, EDGE_TRUE_VALUE);
+ e->count = count;
+ e->probability = profile_probability::even ();
+
+ e = make_edge (cond_pos_bb, then_bb, EDGE_TRUE_VALUE);
+ e->probability = profile_probability::very_unlikely ();
+
+ e = make_edge (cond_pos_bb, fallthru_bb, EDGE_FALSE_VALUE);
+ e->count = count;
+ e->probability = profile_probability::very_likely ();
+
+ make_single_succ_edge (then_bb, fallthru_bb, EDGE_FALLTHRU);
+ }
+
+ gimple *g = gimple_build_assign (ptri, NOP_EXPR, ptr);
+ gimple_set_location (g, loc);
+ gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+ g = gimple_build_assign (ptrplusoff, PLUS_EXPR, ptri, off);
+ gimple_set_location (g, loc);
+ gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+
+ /* Update dominance info for the newly created then_bb; note that
+ fallthru_bb's dominance info has already been updated by
+ split_block. */
+ if (dom_info_available_p (CDI_DOMINATORS))
+ {
+ set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb);
+ if (pos_neg == 3)
+ {
+ set_immediate_dominator (CDI_DOMINATORS, cond_pos_bb, cond_bb);
+ set_immediate_dominator (CDI_DOMINATORS, fallthru_bb, cond_bb);
+ }
+ }
+
+ /* Put the ubsan builtin call into the newly created BB. */
+ if (flag_sanitize_undefined_trap_on_error)
+ g = gimple_build_call (builtin_decl_implicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ enum built_in_function bcode
+ = (flag_sanitize_recover & SANITIZE_POINTER_OVERFLOW)
+ ? BUILT_IN_UBSAN_HANDLE_POINTER_OVERFLOW
+ : BUILT_IN_UBSAN_HANDLE_POINTER_OVERFLOW_ABORT;
+ tree fn = builtin_decl_implicit (bcode);
+ tree data
+ = ubsan_create_data ("__ubsan_ptrovf_data", 1, &loc,
+ NULL_TREE, NULL_TREE);
+ data = build_fold_addr_expr_loc (loc, data);
+ g = gimple_build_call (fn, 3, data, ptr, ptrplusoff);
+ }
+ gimple_stmt_iterator gsi2 = gsi_start_bb (then_bb);
+ gimple_set_location (g, loc);
+ gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
+
+ /* Unlink the UBSAN_PTRs vops before replacing it. */
+ unlink_stmt_vdef (stmt);
+
+ if (TREE_CODE (off) == INTEGER_CST)
+ g = gimple_build_cond (wi::neg_p (off) ? LT_EXPR : GE_EXPR, ptri,
+ fold_build1 (NEGATE_EXPR, sizetype, off),
+ NULL_TREE, NULL_TREE);
+ else if (pos_neg != 3)
+ g = gimple_build_cond (pos_neg == 1 ? LT_EXPR : GT_EXPR,
+ ptrplusoff, ptri, NULL_TREE, NULL_TREE);
+ else
+ {
+ gsi2 = gsi_start_bb (cond_pos_bb);
+ g = gimple_build_cond (LT_EXPR, ptrplusoff, ptri, NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc);
+ gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
+
+ gsi2 = gsi_start_bb (cond_neg_bb);
+ g = gimple_build_cond (GT_EXPR, ptrplusoff, ptri, NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc);
+ gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
+
+ gimple_seq seq = NULL;
+ tree t = gimple_build (&seq, loc, NOP_EXPR, ssizetype, off);
+ t = gimple_build (&seq, loc, GE_EXPR, boolean_type_node,
+ t, ssize_int (0));
+ gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT);
+ g = gimple_build_cond (NE_EXPR, t, boolean_false_node,
+ NULL_TREE, NULL_TREE);
+ }
+ gimple_set_location (g, loc);
+ /* Replace the UBSAN_PTR with a GIMPLE_COND stmt. */
+ gsi_replace (&gsi, g, false);
+ return false;
+}
+
+
/* Cached __ubsan_vptr_type_cache decl. */
static GTY(()) tree ubsan_vptr_type_cache_decl;
@@ -1234,6 +1400,111 @@ instrument_null (gimple_stmt_iterator gsi, tree t, bool is_lhs)
instrument_mem_ref (t, base, &gsi, is_lhs);
}
+/* Instrument pointer arithmetics PTR p+ OFF. */
+
+static void
+instrument_pointer_overflow (gimple_stmt_iterator *gsi, tree ptr, tree off)
+{
+ if (TYPE_PRECISION (sizetype) != POINTER_SIZE)
+ return;
+ gcall *g = gimple_build_call_internal (IFN_UBSAN_PTR, 2, ptr, off);
+ gimple_set_location (g, gimple_location (gsi_stmt (*gsi)));
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+}
+
+/* Instrument pointer arithmetics if any. */
+
+static void
+maybe_instrument_pointer_overflow (gimple_stmt_iterator *gsi, tree t)
+{
+ if (TYPE_PRECISION (sizetype) != POINTER_SIZE)
+ return;
+
+ /* Handle also e.g. &s->i. */
+ if (TREE_CODE (t) == ADDR_EXPR)
+ t = TREE_OPERAND (t, 0);
+
+ if (!handled_component_p (t) && TREE_CODE (t) != MEM_REF)
+ return;
+
+ HOST_WIDE_INT bitsize, bitpos, bytepos;
+ tree offset;
+ machine_mode mode;
+ int volatilep = 0, reversep, unsignedp = 0;
+ tree inner = get_inner_reference (t, &bitsize, &bitpos, &offset, &mode,
+ &unsignedp, &reversep, &volatilep);
+ tree moff = NULL_TREE;
+
+ bool decl_p = DECL_P (inner);
+ tree base;
+ if (decl_p)
+ {
+ if (DECL_REGISTER (inner))
+ return;
+ base = inner;
+ /* If BASE is a fixed size automatic variable or
+ global variable defined in the current TU and bitpos
+ fits, don't instrument anything. */
+ if (offset == NULL_TREE
+ && bitpos > 0
+ && (VAR_P (base)
+ || TREE_CODE (base) == PARM_DECL
+ || TREE_CODE (base) == RESULT_DECL)
+ && DECL_SIZE (base)
+ && TREE_CODE (DECL_SIZE (base)) == INTEGER_CST
+ && compare_tree_int (DECL_SIZE (base), bitpos) >= 0
+ && (!is_global_var (base) || decl_binds_to_current_def_p (base)))
+ return;
+ }
+ else if (TREE_CODE (inner) == MEM_REF)
+ {
+ base = TREE_OPERAND (inner, 0);
+ if (TREE_CODE (base) == ADDR_EXPR
+ && DECL_P (TREE_OPERAND (base, 0))
+ && !TREE_ADDRESSABLE (TREE_OPERAND (base, 0))
+ && !is_global_var (TREE_OPERAND (base, 0)))
+ return;
+ moff = TREE_OPERAND (inner, 1);
+ if (integer_zerop (moff))
+ moff = NULL_TREE;
+ }
+ else
+ return;
+
+ if (!POINTER_TYPE_P (TREE_TYPE (base)) && !DECL_P (base))
+ return;
+ bytepos = bitpos / BITS_PER_UNIT;
+ if (offset == NULL_TREE && bytepos == 0 && moff == NULL_TREE)
+ return;
+
+ tree base_addr = base;
+ if (decl_p)
+ base_addr = build1 (ADDR_EXPR,
+ build_pointer_type (TREE_TYPE (base)), base);
+ t = offset;
+ if (bytepos)
+ {
+ if (t)
+ t = fold_build2 (PLUS_EXPR, TREE_TYPE (t), t,
+ build_int_cst (TREE_TYPE (t), bytepos));
+ else
+ t = size_int (bytepos);
+ }
+ if (moff)
+ {
+ if (t)
+ t = fold_build2 (PLUS_EXPR, TREE_TYPE (t), t,
+ fold_convert (TREE_TYPE (t), moff));
+ else
+ t = fold_convert (sizetype, moff);
+ }
+ t = force_gimple_operand_gsi (gsi, t, true, NULL_TREE, true,
+ GSI_SAME_STMT);
+ base_addr = force_gimple_operand_gsi (gsi, base_addr, true, NULL_TREE, true,
+ GSI_SAME_STMT);
+ instrument_pointer_overflow (gsi, base_addr, t);
+}
+
/* Build an ubsan builtin call for the signed-integer-overflow
sanitization. CODE says what kind of builtin are we building,
LOC is a location, LHSTYPE is the type of LHS, OP0 and OP1
@@ -1849,7 +2120,7 @@ instrument_object_size (gimple_stmt_iterator *gsi, tree t, bool is_lhs)
{
tree rhs1 = gimple_assign_rhs1 (def_stmt);
if (TREE_CODE (rhs1) == SSA_NAME
- && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs1))
+ && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs1))
break;
else
base = rhs1;
@@ -1973,7 +2244,8 @@ public:
| SANITIZE_ALIGNMENT
| SANITIZE_NONNULL_ATTRIBUTE
| SANITIZE_RETURNS_NONNULL_ATTRIBUTE
- | SANITIZE_OBJECT_SIZE));
+ | SANITIZE_OBJECT_SIZE
+ | SANITIZE_POINTER_OVERFLOW));
}
virtual unsigned int execute (function *);
@@ -2065,6 +2337,32 @@ pass_ubsan::execute (function *fun)
}
}
+ if (sanitize_flags_p (SANITIZE_POINTER_OVERFLOW, fun->decl))
+ {
+ if (is_gimple_assign (stmt)
+ && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+ instrument_pointer_overflow (&gsi,
+ gimple_assign_rhs1 (stmt),
+ gimple_assign_rhs2 (stmt));
+ if (gimple_store_p (stmt))
+ maybe_instrument_pointer_overflow (&gsi,
+ gimple_get_lhs (stmt));
+ if (gimple_assign_single_p (stmt))
+ maybe_instrument_pointer_overflow (&gsi,
+ gimple_assign_rhs1 (stmt));
+ if (is_gimple_call (stmt))
+ {
+ unsigned args_num = gimple_call_num_args (stmt);
+ for (unsigned i = 0; i < args_num; ++i)
+ {
+ tree arg = gimple_call_arg (stmt, i);
+ if (is_gimple_reg (arg))
+ continue;
+ maybe_instrument_pointer_overflow (&gsi, arg);
+ }
+ }
+ }
+
gsi_next (&gsi);
}
if (gimple_purge_dead_eh_edges (bb))
diff --git a/gcc/ubsan.h b/gcc/ubsan.h
index 8d990b6..20a3347 100644
--- a/gcc/ubsan.h
+++ b/gcc/ubsan.h
@@ -52,6 +52,7 @@ enum ubsan_encode_value_phase {
extern bool ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
extern bool ubsan_expand_null_ifn (gimple_stmt_iterator *);
extern bool ubsan_expand_objsize_ifn (gimple_stmt_iterator *);
+extern bool ubsan_expand_ptr_ifn (gimple_stmt_iterator *);
extern bool ubsan_expand_vptr_ifn (gimple_stmt_iterator *);
extern bool ubsan_instrument_unreachable (gimple_stmt_iterator *);
extern tree ubsan_create_data (const char *, int, const location_t *, ...);