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
This commit is contained in:
Jakub Jelinek 2017-07-28 12:37:51 +02:00 committed by Jakub Jelinek
parent 70affe6aff
commit c9b39a4955
20 changed files with 593 additions and 21 deletions

View file

@ -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

View file

@ -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
};

View file

@ -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),

View file

@ -401,6 +401,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 *)
{

View file

@ -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)

View file

@ -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;

View file

@ -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 }

View file

@ -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,

View file

@ -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;

View file

@ -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.

View file

@ -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;
}

View file

@ -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 } } */

View file

@ -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:

View file

@ -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.

View file

@ -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))

View file

@ -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 *, ...);

View file

@ -1,3 +1,10 @@
2017-07-28 Jakub Jelinek <jakub@redhat.com>
PR sanitizer/80998
* ubsan/ubsan_handlers.cc: Cherry-pick upstream r304461.
* ubsan/ubsan_checks.inc: Likewise.
* ubsan/ubsan_handlers.h: Likewise.
2017-07-14 Jakub Jelinek <jakub@redhat.com>
PR sanitizer/81066

View file

@ -17,6 +17,7 @@
UBSAN_CHECK(GenericUB, "undefined-behavior", "undefined")
UBSAN_CHECK(NullPointerUse, "null-pointer-use", "null")
UBSAN_CHECK(PointerOverflow, "pointer-overflow", "pointer-overflow")
UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use", "alignment")
UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size", "object-size")
UBSAN_CHECK(SignedIntegerOverflow, "signed-integer-overflow",

View file

@ -521,6 +521,37 @@ void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
Die();
}
static void handlePointerOverflowImpl(PointerOverflowData *Data,
ValueHandle Base,
ValueHandle Result,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
ErrorType ET = ErrorType::PointerOverflow;
if (ignoreReport(Loc, Opts, ET))
return;
ScopedReport R(Opts, Loc, ET);
Diag(Loc, DL_Error, "pointer index expression with base %0 overflowed to %1")
<< (void *)Base << (void*)Result;
}
void __ubsan::__ubsan_handle_pointer_overflow(PointerOverflowData *Data,
ValueHandle Base,
ValueHandle Result) {
GET_REPORT_OPTIONS(false);
handlePointerOverflowImpl(Data, Base, Result, Opts);
}
void __ubsan::__ubsan_handle_pointer_overflow_abort(PointerOverflowData *Data,
ValueHandle Base,
ValueHandle Result) {
GET_REPORT_OPTIONS(true);
handlePointerOverflowImpl(Data, Base, Result, Opts);
Die();
}
static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function,
ReportOptions Opts) {
if (Data->CheckKind != CFITCK_ICall)

View file

@ -146,6 +146,13 @@ struct NonNullArgData {
/// \brief Handle passing null pointer to function with nonnull attribute.
RECOVERABLE(nonnull_arg, NonNullArgData *Data)
struct PointerOverflowData {
SourceLocation Loc;
};
RECOVERABLE(pointer_overflow, PointerOverflowData *Data, ValueHandle Base,
ValueHandle Result)
/// \brief Known CFI check kinds.
/// Keep in sync with the enum of the same name in CodeGenFunction.h
enum CFITypeCheckKind : unsigned char {