opts.c (common_handle_option): Handle -fsanitize=alignment.

* opts.c (common_handle_option): Handle -fsanitize=alignment.
	* ubsan.h (enum ubsan_null_ckind): Add UBSAN_CTOR_CALL.
	(ubsan_expand_bounds_ifn, ubsan_expand_null_ifn): Change return
	type to bool.
	* stor-layout.h (min_align_of_type): New prototype.
	* asan.c (pass_sanopt::execute): Don't perform gsi_next if
	ubsan_expand* told us not to do it.  Remove the extra gsi_end_p
	check.
	* ubsan.c: Include builtins.h.
	(ubsan_expand_bounds_ifn): Change return type to bool,
	always return true.
	(ubsan_expand_null_ifn): Change return type to bool, change
	argument to gimple_stmt_iterator *.  Handle both null and alignment
	sanitization, take type from ckind argument's type rather than
	first argument.
	(instrument_member_call): Removed.
	(instrument_mem_ref): Remove t argument, add mem and base arguments.
	Handle both null and alignment sanitization, don't say whole
	struct access is member access.  Build 3 argument IFN_UBSAN_NULL
	call instead of 2 argument.
	(instrument_null): Adjust instrument_mem_ref caller.  Don't
	instrument calls here.
	(pass_ubsan::gate, pass_ubsan::execute): Handle SANITIZE_ALIGNMENT
	like SANITIZE_NULL.
	* stor-layout.c (min_align_of_type): New function.
	* flag-types.h (enum sanitize_code): Add SANITIZE_ALIGNMENT.
	Or it into SANITIZE_UNDEFINED.
	* doc/invoke.texi (-fsanitize=alignment): Document.
cp/
	* cp-gimplify.c (cp_genericize_r): For -fsanitize=null and/or
	-fsanitize=alignment call ubsan_maybe_instrument_reference
	for casts to REFERENCE_TYPE and ubsan_maybe_instrument_member_call
	for calls to member functions.
c-family/
	* c-common.h (min_align_of_type): Removed prototype.
	* c-common.c (min_align_of_type): Removed.
	* c-ubsan.h (ubsan_maybe_instrument_reference,
	ubsan_maybe_instrument_member_call): New prototypes.
	* c-ubsan.c: Include stor-layout.h and builtins.h.
	(ubsan_maybe_instrument_reference_or_call,
	ubsan_maybe_instrument_reference, ubsan_maybe_instrument_call): New
	functions.
testsuite/
	* c-c++-common/ubsan/align-1.c: New test.
	* c-c++-common/ubsan/align-2.c: New test.
	* c-c++-common/ubsan/align-3.c: New test.
	* c-c++-common/ubsan/align-4.c: New test.
	* c-c++-common/ubsan/align-5.c: New test.
	* c-c++-common/ubsan/attrib-4.c: New test.
	* g++.dg/ubsan/align-1.C: New test.
	* g++.dg/ubsan/align-2.C: New test.
	* g++.dg/ubsan/align-3.C: New test.
	* g++.dg/ubsan/attrib-1.C: New test.
	* g++.dg/ubsan/null-1.C: New test.
	* g++.dg/ubsan/null-2.C: New test.

From-SVN: r213406
This commit is contained in:
Jakub Jelinek 2014-08-01 09:52:43 +02:00 committed by Jakub Jelinek
parent bbe2542f72
commit 944fa280bc
29 changed files with 787 additions and 76 deletions

View File

@ -1,3 +1,34 @@
2014-08-01 Jakub Jelinek <jakub@redhat.com>
* opts.c (common_handle_option): Handle -fsanitize=alignment.
* ubsan.h (enum ubsan_null_ckind): Add UBSAN_CTOR_CALL.
(ubsan_expand_bounds_ifn, ubsan_expand_null_ifn): Change return
type to bool.
* stor-layout.h (min_align_of_type): New prototype.
* asan.c (pass_sanopt::execute): Don't perform gsi_next if
ubsan_expand* told us not to do it. Remove the extra gsi_end_p
check.
* ubsan.c: Include builtins.h.
(ubsan_expand_bounds_ifn): Change return type to bool,
always return true.
(ubsan_expand_null_ifn): Change return type to bool, change
argument to gimple_stmt_iterator *. Handle both null and alignment
sanitization, take type from ckind argument's type rather than
first argument.
(instrument_member_call): Removed.
(instrument_mem_ref): Remove t argument, add mem and base arguments.
Handle both null and alignment sanitization, don't say whole
struct access is member access. Build 3 argument IFN_UBSAN_NULL
call instead of 2 argument.
(instrument_null): Adjust instrument_mem_ref caller. Don't
instrument calls here.
(pass_ubsan::gate, pass_ubsan::execute): Handle SANITIZE_ALIGNMENT
like SANITIZE_NULL.
* stor-layout.c (min_align_of_type): New function.
* flag-types.h (enum sanitize_code): Add SANITIZE_ALIGNMENT.
Or it into SANITIZE_UNDEFINED.
* doc/invoke.texi (-fsanitize=alignment): Document.
2014-07-31 Andi Kleen <ak@linux.intel.com>
* tree-ssa-tail-merge.c (same_succ_hash): Convert to inchash.

View File

@ -2750,21 +2750,25 @@ pass_sanopt::execute (function *fun)
FOR_EACH_BB_FN (bb, fun)
{
gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
{
gimple stmt = gsi_stmt (gsi);
bool no_next = false;
if (!is_gimple_call (stmt))
continue;
{
gsi_next (&gsi);
continue;
}
if (gimple_call_internal_p (stmt))
switch (gimple_call_internal_fn (stmt))
{
case IFN_UBSAN_NULL:
ubsan_expand_null_ifn (gsi);
no_next = ubsan_expand_null_ifn (&gsi);
break;
case IFN_UBSAN_BOUNDS:
ubsan_expand_bounds_ifn (&gsi);
no_next = ubsan_expand_bounds_ifn (&gsi);
break;
default:
break;
@ -2777,9 +2781,8 @@ pass_sanopt::execute (function *fun)
fprintf (dump_file, "\n");
}
/* ubsan_expand_bounds_ifn might move us to the end of the BB. */
if (gsi_end_p (gsi))
break;
if (!no_next)
gsi_next (&gsi);
}
}
return 0;

View File

@ -1,3 +1,14 @@
2014-08-01 Jakub Jelinek <jakub@redhat.com>
* c-common.h (min_align_of_type): Removed prototype.
* c-common.c (min_align_of_type): Removed.
* c-ubsan.h (ubsan_maybe_instrument_reference,
ubsan_maybe_instrument_member_call): New prototypes.
* c-ubsan.c: Include stor-layout.h and builtins.h.
(ubsan_maybe_instrument_reference_or_call,
ubsan_maybe_instrument_reference, ubsan_maybe_instrument_call): New
functions.
2014-07-31 Marc Glisse <marc.glisse@inria.fr>
PR c++/60517

View File

@ -4965,26 +4965,6 @@ c_common_get_alias_set (tree t)
return -1;
}
/* Return the least alignment required for type TYPE. */
unsigned int
min_align_of_type (tree type)
{
unsigned int align = TYPE_ALIGN (type);
align = MIN (align, BIGGEST_ALIGNMENT);
#ifdef BIGGEST_FIELD_ALIGNMENT
align = MIN (align, BIGGEST_FIELD_ALIGNMENT);
#endif
unsigned int field_align = align;
#ifdef ADJUST_FIELD_ALIGN
tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
type);
field_align = ADJUST_FIELD_ALIGN (field, field_align);
#endif
align = MIN (align, field_align);
return align / BITS_PER_UNIT;
}
/* Compute the value of 'sizeof (TYPE)' or '__alignof__ (TYPE)', where
the IS_SIZEOF parameter indicates which operator is being applied.
The COMPLAIN flag controls whether we should diagnose possibly

View File

@ -762,7 +762,6 @@ extern tree c_wrap_maybe_const (tree, bool);
extern tree c_save_expr (tree);
extern tree c_common_truthvalue_conversion (location_t, tree);
extern void c_apply_type_quals_to_decl (int, tree);
extern unsigned int min_align_of_type (tree);
extern tree c_sizeof_or_alignof_type (location_t, tree, bool, bool, int);
extern tree c_alignof_expr (location_t, tree);
/* Print an error message for invalid operands to arith operation CODE.

View File

@ -31,6 +31,8 @@ along with GCC; see the file COPYING3. If not see
#include "c-family/c-ubsan.h"
#include "asan.h"
#include "internal-fn.h"
#include "stor-layout.h"
#include "builtins.h"
/* Instrument division by zero and INT_MIN / -1. If not instrumenting,
return NULL_TREE. */
@ -350,3 +352,99 @@ ubsan_maybe_instrument_array_ref (tree *expr_p, bool ignore_off_by_one)
}
}
}
static tree
ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree type,
enum ubsan_null_ckind ckind)
{
tree orig_op = op;
bool instrument = false;
unsigned int mina = 0;
if (current_function_decl == NULL_TREE
|| lookup_attribute ("no_sanitize_undefined",
DECL_ATTRIBUTES (current_function_decl)))
return NULL_TREE;
if (flag_sanitize & SANITIZE_ALIGNMENT)
{
mina = min_align_of_type (type);
if (mina <= 1)
mina = 0;
}
while ((TREE_CODE (op) == NOP_EXPR
|| TREE_CODE (op) == NON_LVALUE_EXPR)
&& TREE_CODE (TREE_TYPE (op)) == POINTER_TYPE)
op = TREE_OPERAND (op, 0);
if (TREE_CODE (op) == NOP_EXPR
&& TREE_CODE (TREE_TYPE (op)) == REFERENCE_TYPE)
{
if (mina && mina > min_align_of_type (TREE_TYPE (TREE_TYPE (op))))
instrument = true;
}
else
{
if ((flag_sanitize & SANITIZE_NULL) && TREE_CODE (op) == ADDR_EXPR)
{
bool strict_overflow_p = false;
/* tree_single_nonzero_warnv_p will not return true for non-weak
non-automatic decls with -fno-delete-null-pointer-checks,
which is disabled during -fsanitize=null. We don't want to
instrument those, just weak vars though. */
int save_flag_delete_null_pointer_checks
= flag_delete_null_pointer_checks;
flag_delete_null_pointer_checks = 1;
if (!tree_single_nonzero_warnv_p (op, &strict_overflow_p)
|| strict_overflow_p)
instrument = true;
flag_delete_null_pointer_checks
= save_flag_delete_null_pointer_checks;
}
else if (flag_sanitize & SANITIZE_NULL)
instrument = true;
if (mina && mina > get_pointer_alignment (op) / BITS_PER_UNIT)
instrument = true;
}
if (!instrument)
return NULL_TREE;
op = save_expr (orig_op);
tree kind = build_int_cst (TREE_TYPE (op), ckind);
tree align = build_int_cst (pointer_sized_int_node, mina);
tree call
= build_call_expr_internal_loc (loc, IFN_UBSAN_NULL, void_type_node,
3, op, kind, align);
TREE_SIDE_EFFECTS (call) = 1;
return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), call, op);
}
/* Instrument a NOP_EXPR to REFERENCE_TYPE if needed. */
void
ubsan_maybe_instrument_reference (tree stmt)
{
tree op = TREE_OPERAND (stmt, 0);
op = ubsan_maybe_instrument_reference_or_call (EXPR_LOCATION (stmt), op,
TREE_TYPE (TREE_TYPE (stmt)),
UBSAN_REF_BINDING);
if (op)
TREE_OPERAND (stmt, 0) = op;
}
/* Instrument a CALL_EXPR to a method if needed. */
void
ubsan_maybe_instrument_member_call (tree stmt, bool is_ctor)
{
if (call_expr_nargs (stmt) == 0)
return;
tree op = CALL_EXPR_ARG (stmt, 0);
if (op == error_mark_node
|| !POINTER_TYPE_P (TREE_TYPE (op)))
return;
op = ubsan_maybe_instrument_reference_or_call (EXPR_LOCATION (stmt), op,
TREE_TYPE (TREE_TYPE (op)),
is_ctor ? UBSAN_CTOR_CALL
: UBSAN_MEMBER_CALL);
if (op)
CALL_EXPR_ARG (stmt, 0) = op;
}

View File

@ -28,5 +28,7 @@ extern tree ubsan_instrument_return (location_t);
extern tree ubsan_instrument_bounds (location_t, tree, tree *, bool);
extern bool ubsan_array_ref_instrumented_p (const_tree);
extern void ubsan_maybe_instrument_array_ref (tree *, bool);
extern void ubsan_maybe_instrument_reference (tree);
extern void ubsan_maybe_instrument_member_call (tree, bool);
#endif /* GCC_C_UBSAN_H */

View File

@ -1,3 +1,10 @@
2014-08-01 Jakub Jelinek <jakub@redhat.com>
* cp-gimplify.c (cp_genericize_r): For -fsanitize=null and/or
-fsanitize=alignment call ubsan_maybe_instrument_reference
for casts to REFERENCE_TYPE and ubsan_maybe_instrument_member_call
for calls to member functions.
2014-07-31 Marc Glisse <marc.glisse@inria.fr>
PR c++/60517

View File

@ -1198,6 +1198,27 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
*stmt_p = size_one_node;
return NULL;
}
else if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
{
if (TREE_CODE (stmt) == NOP_EXPR
&& TREE_CODE (TREE_TYPE (stmt)) == REFERENCE_TYPE)
ubsan_maybe_instrument_reference (stmt);
else if (TREE_CODE (stmt) == CALL_EXPR)
{
tree fn = CALL_EXPR_FN (stmt);
if (fn != NULL_TREE
&& !error_operand_p (fn)
&& POINTER_TYPE_P (TREE_TYPE (fn))
&& TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) == METHOD_TYPE)
{
bool is_ctor
= TREE_CODE (fn) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
&& DECL_CONSTRUCTOR_P (TREE_OPERAND (fn, 0));
ubsan_maybe_instrument_member_call (stmt, is_ctor);
}
}
}
pointer_set_insert (p_set, *stmt_p);

View File

@ -5463,7 +5463,8 @@ instead.
This option enables pointer checking. Particularly, the application
built with this option turned on will issue an error message when it
tries to dereference a NULL pointer, or if a reference (possibly an
rvalue reference) is bound to a NULL pointer.
rvalue reference) is bound to a NULL pointer, or if a method is invoked
on an object pointed by a NULL pointer.
@item -fsanitize=return
@opindex fsanitize=return
@ -5490,6 +5491,13 @@ 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=alignment
@opindex fsanitize=alignment
This option enables checking of alignment of pointers when they are
dereferenced, or when a reference is bound to insufficiently aligned target,
or when a method or constructor is invoked on insufficiently aligned object.
@item -fsanitize=float-divide-by-zero
@opindex fsanitize=float-divide-by-zero
Detect floating-point division by zero. Unlike other similar options,

View File

@ -233,10 +233,11 @@ enum sanitize_code {
SANITIZE_FLOAT_DIVIDE = 1 << 14,
SANITIZE_FLOAT_CAST = 1 << 15,
SANITIZE_BOUNDS = 1 << 16,
SANITIZE_ALIGNMENT = 1 << 17,
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_ALIGNMENT,
SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
};

View File

@ -1492,6 +1492,7 @@ common_handle_option (struct gcc_options *opts,
{ "float-cast-overflow", SANITIZE_FLOAT_CAST,
sizeof "float-cast-overflow" - 1 },
{ "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 },
{ "alignment", SANITIZE_ALIGNMENT, sizeof "alignment" - 1 },
{ NULL, 0, 0 }
};
const char *comma;

View File

@ -2390,6 +2390,27 @@ layout_type (tree type)
gcc_assert (!TYPE_ALIAS_SET_KNOWN_P (type));
}
/* Return the least alignment required for type TYPE. */
unsigned int
min_align_of_type (tree type)
{
unsigned int align = TYPE_ALIGN (type);
align = MIN (align, BIGGEST_ALIGNMENT);
#ifdef BIGGEST_FIELD_ALIGNMENT
align = MIN (align, BIGGEST_FIELD_ALIGNMENT);
#endif
unsigned int field_align = align;
#ifdef ADJUST_FIELD_ALIGN
tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
type);
field_align = ADJUST_FIELD_ALIGN (field, field_align);
ggc_free (field);
#endif
align = MIN (align, field_align);
return align / BITS_PER_UNIT;
}
/* Vector types need to re-check the target flags each time we report
the machine mode. We need to do this because attribute target can
change the result of vector_mode_supported_p and have_regs_of_mode

View File

@ -59,6 +59,9 @@ extern void layout_decl (tree, unsigned);
node, does nothing except for the first time. */
extern void layout_type (tree);
/* Return the least alignment in bytes required for type TYPE. */
extern unsigned int min_align_of_type (tree);
/* Construct various nodes representing fract or accum data types. */
extern tree make_fract_type (int, int, int);
extern tree make_accum_type (int, int, int);

View File

@ -1,3 +1,18 @@
2014-08-01 Jakub Jelinek <jakub@redhat.com>
* c-c++-common/ubsan/align-1.c: New test.
* c-c++-common/ubsan/align-2.c: New test.
* c-c++-common/ubsan/align-3.c: New test.
* c-c++-common/ubsan/align-4.c: New test.
* c-c++-common/ubsan/align-5.c: New test.
* c-c++-common/ubsan/attrib-4.c: New test.
* g++.dg/ubsan/align-1.C: New test.
* g++.dg/ubsan/align-2.C: New test.
* g++.dg/ubsan/align-3.C: New test.
* g++.dg/ubsan/attrib-1.C: New test.
* g++.dg/ubsan/null-1.C: New test.
* g++.dg/ubsan/null-2.C: New test.
2014-08-01 Tom de Vries <tom@codesourcery.com>
* lib/target-supports.exp (check_effective_target_glibc)

View File

@ -0,0 +1,41 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=undefined -fno-sanitize-recover" } */
struct S { int a; char b; long long c; short d[10]; };
struct T { char a; long long b; };
struct U { char a; int b; int c; long long d; struct S e; struct T f; };
struct V { long long a; struct S b; struct T c; struct U u; } v;
__attribute__((noinline, noclone)) void
f1 (int *p, int *q, char *r, long long *s)
{
*p = *q + *r + *s;
}
__attribute__((noinline, noclone)) int
f2 (struct S *p)
{
return p->a;
}
__attribute__((noinline, noclone)) long long
f3 (struct S *p, int i)
{
return p->c + p->d[1] + p->d[i];
}
__attribute__((noinline, noclone)) long long
f4 (long long *p)
{
return *p;
}
int
main ()
{
f1 (&v.u.b, &v.u.c, &v.u.a, &v.u.d);
if (f2 (&v.u.e) + f3 (&v.u.e, 4) + f4 (&v.u.f.b) != 0)
__builtin_abort ();
return 0;
}

View File

@ -0,0 +1,56 @@
/* Limit this to known non-strict alignment targets. */
/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
/* { dg-options "-fsanitize=alignment" } */
struct S { int a; char b; long long c; short d[10]; };
struct T { char a; long long b; };
struct U { char a; int b; int c; long long d; struct S e; struct T f; } __attribute__((packed));
struct V { long long a; struct S b; struct T c; struct U u; } v;
__attribute__((noinline, noclone)) void
f1 (int *p, int *q, char *r, long long *s)
{
*p =
*q
+ *r
+ *s;
}
__attribute__((noinline, noclone)) int
f2 (struct S *p)
{
return p->a;
}
__attribute__((noinline, noclone)) long long
f3 (struct S *p, int i)
{
return p->c
+ p->d[1]
+ p->d[i];
}
__attribute__((noinline, noclone)) long long
f4 (long long *p)
{
return *p;
}
int
main ()
{
f1 (&v.u.b, &v.u.c, &v.u.a, &v.u.d);
if (f2 (&v.u.e) + f3 (&v.u.e, 4) + f4 (&v.u.f.b) != 0)
__builtin_abort ();
return 0;
}
/* { dg-output "\.c:(14|15):\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
/* { dg-output "\.c:16:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment.*" } */
/* { dg-output "\.c:(13|16):\[0-9]*: \[^\n\r]*store to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
/* { dg-output "\.c:23:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
/* { dg-output "\.c:(29|30):\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
/* { dg-output "\.c:30:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
/* { dg-output "\.c:31:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
/* { dg-output "\.c:37:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment" } */

View File

@ -0,0 +1,66 @@
/* { dg-do run } */
/* { dg-options "-fsanitize=undefined -fno-sanitize-recover" } */
int c;
__attribute__((noinline, noclone)) void
f1 (int *a, char *b)
{
__builtin_memcpy (a, b, sizeof (*a));
}
__attribute__((noinline, noclone)) void
f2 (int *a, char *b)
{
__builtin_memcpy (b, a, sizeof (*a));
}
__attribute__((noinline, noclone)) void
f3 (char *b)
{
__builtin_memcpy (&c, b, sizeof (c));
}
__attribute__((noinline, noclone)) void
f4 (char *b)
{
__builtin_memcpy (b, &c, sizeof (c));
}
struct T
{
char a;
short b;
int c;
long d;
long long e;
short f;
float g;
double h;
long double i;
} __attribute__((packed));
__attribute__((noinline, noclone)) int
f5 (struct T *p)
{
return p->a + p->b + p->c + p->d + p->e + p->f + p->g + p->h + p->i;
}
int
main ()
{
struct S { int a; char b[sizeof (int) + 1]; } s;
s.a = 6;
f2 (&s.a, &s.b[1]);
f1 (&s.a, &s.b[1]);
c = s.a + 1;
f4 (&s.b[1]);
f3 (&s.b[1]);
if (c != 7 || s.a != 6)
__builtin_abort ();
struct U { long long a; long double b; char c; struct T d; } u;
__builtin_memset (&u, 0, sizeof (u));
if (f5 (&u.d) != 0)
__builtin_abort ();
return 0;
}

View File

@ -0,0 +1,14 @@
/* Limit this to known non-strict alignment targets. */
/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
/* { dg-options "-fsanitize=null,alignment" } */
#include "align-2.c"
/* { dg-output "\.c:(14|15):\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
/* { dg-output "\[^\n\r]*\.c:16:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment.*" } */
/* { dg-output "\[^\n\r]*\.c:(13|16):\[0-9]*: \[^\n\r]*store to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */
/* { dg-output "\[^\n\r]*\.c:23:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
/* { dg-output "\[^\n\r]*\.c:(29|30):\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
/* { dg-output "\[^\n\r]*\.c:30:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
/* { dg-output "\[^\n\r]*\.c:31:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */
/* { dg-output "\[^\n\r]*\.c:37:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment" } */

View File

@ -0,0 +1,15 @@
/* { dg-do compile } */
/* { dg-options "-fno-sanitize=null -fsanitize=alignment -O2" } */
/* Check that when optimizing if we know the alignment is right
and we are not doing -fsanitize=null instrumentation we don't
instrument the alignment check. */
__attribute__((noinline, noclone)) int
foo (char *p)
{
p = (char *) __builtin_assume_aligned (p, __alignof__(int));
int *q = (int *) p;
return *q;
}
/* { dg-final { scan-assembler-not "__ubsan_handle" } } */

View File

@ -0,0 +1,15 @@
/* { dg-do compile } */
/* { dg-options "-fsanitize=undefined" } */
/* Test that we don't instrument functions marked with
no_sanitize_undefined attribute. */
struct S { int a[16]; };
__attribute__((no_sanitize_undefined)) long long
foo (int *a, long long *b, struct S *c)
{
return a[1] + *b + c->a[a[0]];
}
/* { dg-final { scan-assembler-not "__ubsan_handle" } } */

View File

@ -0,0 +1,27 @@
// { dg-do run }
// { dg-options "-fsanitize=alignment -Wall -Wno-unused-variable -std=c++11" }
typedef const long int L;
int a = 1;
L b = 2;
int
main (void)
{
int *p = &a;
L *l = &b;
int &r = *p;
auto &r2 = *p;
L &lr = *l;
// Try an rvalue reference.
auto &&r3 = *p;
// Don't evaluate the reference initializer twice.
int i = 1;
int *q = &i;
int &qr = ++*q;
if (i != 2)
__builtin_abort ();
}

View File

@ -0,0 +1,45 @@
// Limit this to known non-strict alignment targets.
// { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } }
// { dg-options "-fsanitize=alignment -Wall -Wno-unused-variable -std=c++11" }
typedef const long int L;
struct S { long int l; char buf[1 + sizeof (int) + sizeof (L)]; } s;
struct T { char a; int b; long int c; } __attribute__((packed));
struct U { long int a; struct T b; } u;
int
main (void)
{
int *p = (int *) &s.buf[1];
L *l = (L *) &s.buf[1 + sizeof(int)];
int &r = *p;
auto &r2 = *p;
L &lr = *l;
// Try an rvalue reference.
auto &&r3 = *p;
// Don't evaluate the reference initializer twice.
int i = 1;
int *q = &i;
int &qr = ++*q;
if (i != 2)
__builtin_abort ();
int *s = &u.b.b;
L *t = &u.b.c;
int &r4 = *s;
auto &r5 = *s;
L &lr2 = *t;
auto &&r6 = *s;
}
// { dg-output "\.C:16:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
// { dg-output "\.C:17:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
// { dg-output "\.C:18:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'const L', which requires \[48] byte alignment.*" }
// { dg-output "\.C:21:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
// { dg-output "\.C:32:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
// { dg-output "\.C:33:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
// { dg-output "\.C:34:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'const L', which requires \[48] byte alignment.*" }
// { dg-output "\.C:35:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment" }

View File

@ -0,0 +1,45 @@
// Limit this to known non-strict alignment targets.
// { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } }
// { dg-options "-fsanitize=alignment -Wall -Wno-unused-variable -std=c++11" }
#include <new>
struct U
{
int a;
void foo () {}
};
struct V
{
V () : a (0) {};
~V () { a = 0; };
int a;
void foo () {}
static void bar () {}
};
struct S { long int l; char buf[1 + sizeof (U) + 2 * sizeof (V)]; } s;
int
main (void)
{
U *p = (U *) &s.buf[1];
p->foo ();
char *q = &s.buf[1 + sizeof (U)];
V *u = new (q) V;
u->a = 1;
u->~V ();
V *v = new (&s.buf[1 + sizeof (U) + sizeof (V)]) V;
v->foo ();
v->bar (); // We don't instrument this right now.
v->~V ();
}
// { dg-output "\.C:26:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct U', which requires 4 byte alignment.*" }
// { dg-output "\.C:28:\[0-9]*:\[\^\n\r]*constructor call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
// { dg-output "\.C:14:\[0-9]*:\[\^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
// { dg-output "\.C:29:\[0-9]*:\[\^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
// { dg-output "\.C:30:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
// { dg-output "\.C:15:\[0-9]*:\[\^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
// { dg-output "\.C:31:\[0-9]*:\[\^\n\r]*constructor call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
// { dg-output "\.C:32:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
// { dg-output "\.C:34:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment" }

View File

@ -0,0 +1,27 @@
// { dg-do compile }
// { dg-options "-fsanitize=undefined -Wall -Wno-unused-variable -std=c++11" }
typedef const long int L;
__attribute__((no_sanitize_undefined)) void
foo (int *p, L *l)
{
int &r = *p;
auto &r2 = *p;
L &lr = *l;
auto &&r3 = *p;
}
struct U
{
int a;
void foo () {}
};
__attribute__((no_sanitize_undefined)) void
bar (U *p)
{
p->foo ();
}
// { dg-final { scan-assembler-not "__ubsan_handle" } }

View File

@ -0,0 +1,30 @@
// { dg-do run }
// { dg-options "-fsanitize=null -Wall -Wno-unused-variable -std=c++11" }
typedef const long int L;
int
main (void)
{
int *p = 0;
L *l = 0;
int &r = *p;
auto &r2 = *p;
L &lr = *l;
// Try an rvalue reference.
auto &&r3 = *p;
// Don't evaluate the reference initializer twice.
int i = 1;
int *q = &i;
int &qr = ++*q;
if (i != 2)
__builtin_abort ();
}
// { dg-output "reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
// { dg-output "\[^\n\r]*reference binding to null pointer of type 'const L'(\n|\r\n|\r)" }
// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }

View File

@ -0,0 +1,39 @@
// Limit this to known non-strict alignment targets.
// { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } }
// { dg-options "-fsanitize=null -Wall -Wno-unused-variable -std=c++11" }
#include <new>
struct U
{
int a;
void foo () {}
};
struct V
{
V () {};
~V () {};
int a;
void foo () {}
static void bar () {}
};
struct S { long int l; char buf[1 + sizeof (U) + 2 * sizeof (V)]; } s;
int
main (void)
{
U *p = 0;
p->foo ();
char *q = 0;
V *u = new (q) V;
u->~V ();
V *v = new (q) V;
v->foo ();
v->bar (); // We don't instrument this right now.
v->~V ();
}
// { dg-output "\.C:26:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct U'.*" }
// { dg-output "\.C:29:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'.*" }
// { dg-output "\.C:31:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'.*" }
// { dg-output "\.C:33:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'" }

View File

@ -49,6 +49,7 @@ along with GCC; see the file COPYING3. If not see
#include "intl.h"
#include "realmpfr.h"
#include "dfp.h"
#include "builtins.h"
/* Map from a tree to a VAR_DECL tree. */
@ -586,7 +587,7 @@ is_ubsan_builtin_p (tree t)
/* Expand the UBSAN_BOUNDS special builtin function. */
void
bool
ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
{
gimple stmt = gsi_stmt (*gsi);
@ -645,21 +646,52 @@ ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
/* Point GSI to next logical statement. */
*gsi = gsi_start_bb (fallthru_bb);
return true;
}
/* Expand UBSAN_NULL internal call. */
/* Expand UBSAN_NULL internal call. The type is kept on the ckind
argument which is a constant, because the middle-end treats pointer
conversions as useless and therefore the type of the first argument
could be changed to any other pointer type. */
void
ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
bool
ubsan_expand_null_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);
gcc_assert (gimple_call_num_args (stmt) == 3);
tree ptr = gimple_call_arg (stmt, 0);
tree ckind = gimple_call_arg (stmt, 1);
tree align = gimple_call_arg (stmt, 2);
tree check_align = NULL_TREE;
bool check_null;
basic_block cur_bb = gsi_bb (gsi);
gimple g;
if (!integer_zerop (align))
{
unsigned int ptralign = get_pointer_alignment (ptr) / BITS_PER_UNIT;
if (compare_tree_int (align, ptralign) == 1)
{
check_align = make_ssa_name (pointer_sized_int_node, NULL);
g = gimple_build_assign_with_ops (NOP_EXPR, check_align,
ptr, NULL_TREE);
gimple_set_location (g, loc);
gsi_insert_before (&gsi, g, GSI_SAME_STMT);
}
}
check_null = (flag_sanitize & SANITIZE_NULL) != 0;
if (check_align == NULL_TREE && !check_null)
{
gsi_remove (gsip, true);
/* Unlink the UBSAN_NULLs vops before replacing it. */
unlink_stmt_vdef (stmt);
return true;
}
/* Split the original block holding the pointer dereference. */
edge e = split_block (cur_bb, stmt);
@ -689,12 +721,11 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
/* Update dominance info for the newly created then_bb; note that
fallthru_bb's dominance info has already been updated by
split_bock. */
split_block. */
if (dom_info_available_p (CDI_DOMINATORS))
set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb);
/* Put the ubsan builtin call into the newly created BB. */
gimple g;
if (flag_sanitize_undefined_trap_on_error)
g = gimple_build_call (builtin_decl_implicit (BUILT_IN_TRAP), 0);
else
@ -705,54 +736,115 @@ ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
: BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT;
tree fn = builtin_decl_implicit (bcode);
const struct ubsan_mismatch_data m
= { build_zero_cst (pointer_sized_int_node), ckind };
= { align, fold_convert (unsigned_char_type_node, ckind) };
tree data
= ubsan_create_data ("__ubsan_null_data", &loc, &m,
ubsan_type_descriptor (TREE_TYPE (ptr),
ubsan_type_descriptor (TREE_TYPE (ckind),
UBSAN_PRINT_POINTER),
NULL_TREE);
data = build_fold_addr_expr_loc (loc, data);
g = gimple_build_call (fn, 2, data,
build_zero_cst (pointer_sized_int_node));
check_align ? check_align
: build_zero_cst (pointer_sized_int_node));
}
gimple_set_location (g, loc);
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_NULLs vops before replacing it. */
unlink_stmt_vdef (stmt);
g = gimple_build_cond (EQ_EXPR, ptr, build_int_cst (TREE_TYPE (ptr), 0),
NULL_TREE, NULL_TREE);
gimple_set_location (g, loc);
if (check_null)
{
g = gimple_build_cond (EQ_EXPR, ptr, build_int_cst (TREE_TYPE (ptr), 0),
NULL_TREE, NULL_TREE);
gimple_set_location (g, loc);
/* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */
gsi_replace (&gsi, g, false);
/* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */
gsi_replace (&gsi, g, false);
}
if (check_align)
{
if (check_null)
{
/* Split the block with the condition again. */
e = split_block (cond_bb, stmt);
basic_block cond1_bb = e->src;
basic_block cond2_bb = e->dest;
/* Make an edge coming from the 'cond1 block' into the 'then block';
this edge is unlikely taken, so set up the probability
accordingly. */
e = make_edge (cond1_bb, then_bb, EDGE_TRUE_VALUE);
e->probability = PROB_VERY_UNLIKELY;
/* Set up the fallthrough basic block. */
e = find_edge (cond1_bb, cond2_bb);
e->flags = EDGE_FALSE_VALUE;
e->count = cond1_bb->count;
e->probability = REG_BR_PROB_BASE - PROB_VERY_UNLIKELY;
/* Update dominance info. */
if (dom_info_available_p (CDI_DOMINATORS))
{
set_immediate_dominator (CDI_DOMINATORS, fallthru_bb, cond1_bb);
set_immediate_dominator (CDI_DOMINATORS, then_bb, cond1_bb);
}
gsi2 = gsi_start_bb (cond2_bb);
}
tree mask = build_int_cst (pointer_sized_int_node,
tree_to_uhwi (align) - 1);
g = gimple_build_assign_with_ops (BIT_AND_EXPR,
make_ssa_name (pointer_sized_int_node,
NULL),
check_align, mask);
gimple_set_location (g, loc);
if (check_null)
gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
else
gsi_insert_before (&gsi, g, GSI_SAME_STMT);
g = gimple_build_cond (NE_EXPR, gimple_assign_lhs (g),
build_int_cst (pointer_sized_int_node, 0),
NULL_TREE, NULL_TREE);
gimple_set_location (g, loc);
if (check_null)
gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
else
/* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */
gsi_replace (&gsi, g, false);
}
return false;
}
/* Instrument a member call. We check whether 'this' is NULL. */
static void
instrument_member_call (gimple_stmt_iterator *iter)
{
tree this_parm = gimple_call_arg (gsi_stmt (*iter), 0);
tree kind = build_int_cst (unsigned_char_type_node, UBSAN_MEMBER_CALL);
gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, this_parm, kind);
gimple_set_location (g, gimple_location (gsi_stmt (*iter)));
gsi_insert_before (iter, g, GSI_SAME_STMT);
}
/* Instrument a memory reference. T is the pointer, IS_LHS says
/* Instrument a memory reference. BASE is the base of MEM, IS_LHS says
whether the pointer is on the left hand side of the assignment. */
static void
instrument_mem_ref (tree t, gimple_stmt_iterator *iter, bool is_lhs)
instrument_mem_ref (tree mem, tree base, gimple_stmt_iterator *iter,
bool is_lhs)
{
enum ubsan_null_ckind ikind = is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF;
if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_TYPE (t))))
unsigned int align = 0;
if (flag_sanitize & SANITIZE_ALIGNMENT)
{
align = min_align_of_type (TREE_TYPE (base));
if (align <= 1)
align = 0;
}
if (align == 0 && (flag_sanitize & SANITIZE_NULL) == 0)
return;
tree t = TREE_OPERAND (base, 0);
if (!POINTER_TYPE_P (TREE_TYPE (t)))
return;
if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_TYPE (t))) && mem != base)
ikind = UBSAN_MEMBER_ACCESS;
tree kind = build_int_cst (unsigned_char_type_node, ikind);
gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, t, kind);
tree kind = build_int_cst (TREE_TYPE (t), ikind);
tree alignt = build_int_cst (pointer_sized_int_node, align);
gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 3, t, kind, alignt);
gimple_set_location (g, gimple_location (gsi_stmt (*iter)));
gsi_insert_before (iter, g, GSI_SAME_STMT);
}
@ -764,15 +856,11 @@ instrument_null (gimple_stmt_iterator gsi, bool is_lhs)
{
gimple stmt = gsi_stmt (gsi);
tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt);
t = get_base_address (t);
const enum tree_code code = TREE_CODE (t);
tree base = get_base_address (t);
const enum tree_code code = TREE_CODE (base);
if (code == MEM_REF
&& TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME)
instrument_mem_ref (TREE_OPERAND (t, 0), &gsi, is_lhs);
else if (code == ADDR_EXPR
&& POINTER_TYPE_P (TREE_TYPE (t))
&& TREE_CODE (TREE_TYPE (TREE_TYPE (t))) == METHOD_TYPE)
instrument_member_call (&gsi);
&& TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME)
instrument_mem_ref (t, base, &gsi, is_lhs);
}
/* Build an ubsan builtin call for the signed-integer-overflow
@ -1147,7 +1235,8 @@ public:
virtual bool gate (function *)
{
return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW
| SANITIZE_BOOL | SANITIZE_ENUM)
| SANITIZE_BOOL | SANITIZE_ENUM
| SANITIZE_ALIGNMENT)
&& current_function_decl != NULL_TREE
&& !lookup_attribute ("no_sanitize_undefined",
DECL_ATTRIBUTES (current_function_decl));
@ -1180,7 +1269,7 @@ pass_ubsan::execute (function *fun)
&& is_gimple_assign (stmt))
instrument_si_overflow (gsi);
if (flag_sanitize & SANITIZE_NULL)
if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
{
if (gimple_store_p (stmt))
instrument_null (gsi, true);

View File

@ -27,7 +27,8 @@ enum ubsan_null_ckind {
UBSAN_STORE_OF,
UBSAN_REF_BINDING,
UBSAN_MEMBER_ACCESS,
UBSAN_MEMBER_CALL
UBSAN_MEMBER_CALL,
UBSAN_CTOR_CALL
};
/* This controls how ubsan prints types. Used in ubsan_type_descriptor. */
@ -43,8 +44,8 @@ struct ubsan_mismatch_data {
tree ckind;
};
extern void ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
extern bool ubsan_expand_bounds_ifn (gimple_stmt_iterator *);
extern bool ubsan_expand_null_ifn (gimple_stmt_iterator *);
extern tree ubsan_instrument_unreachable (location_t);
extern tree ubsan_create_data (const char *, const location_t *,
const struct ubsan_mismatch_data *, ...);