summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>2014-09-10 09:23:16 +0000
committerjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>2014-09-10 09:23:16 +0000
commit4a94b9969ec93ed510cd233bff1a32103be2d78b (patch)
tree303e23d283cab6629b540e2e4d7e2702600c3851
parent73a69d0244f43445be2350831329b465c6bdd21b (diff)
downloadgcc-4a94b9969ec93ed510cd233bff1a32103be2d78b.tar.gz
gcc/
* flag-types.h (enum sanitize_code): Add SANITIZE_NONNULL_ATTRIBUTE and SANITIZE_RETURNS_NONNULL_ATTRIBUTE, or them into SANITIZE_UNDEFINED. * opts.c (common_handle_option): Handle SANITIZE_NONNULL_ATTRIBUTE and SANITIZE_RETURNS_NONNULL_ATTRIBUTE and disable flag_delete_null_pointer_checks for them. * sanitizer.def (BUILT_IN_UBSAN_HANDLE_NONNULL_ARG, BUILT_IN_UBSAN_HANDLE_NONNULL_ARG_ABORT, BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN, BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN_ABORT): New. * ubsan.c (instrument_bool_enum_load): Set *gsi back to stmt's iterator. (instrument_nonnull_arg, instrument_nonnull_return): New functions. (pass_ubsan::gate): Return true even for SANITIZE_NONNULL_ATTRIBUTE or SANITIZE_RETURNS_NONNULL_ATTRIBUTE. (pass_ubsan::execute): Call instrument_nonnull_{arg,return}. * doc/invoke.texi (-fsanitize=nonnull-attribute, -fsanitize=returns-nonnull-attribute): Document. gcc/testsuite/ * c-c++-common/ubsan/attrib-3.c: New test. * c-c++-common/ubsan/nonnull-1.c: New test. * c-c++-common/ubsan/nonnull-2.c: New test. * c-c++-common/ubsan/nonnull-3.c: New test. * c-c++-common/ubsan/nonnull-4.c: New test. * c-c++-common/ubsan/nonnull-5.c: New test. libsanitizer/ * ubsan/ubsan_handlers.cc, ubsan/ubsan_handlers.h: Cherry pick upstream r215485, r217389, r217391 and r217400. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@215118 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog18
-rw-r--r--gcc/doc/invoke.texi14
-rw-r--r--gcc/flag-types.h6
-rw-r--r--gcc/opts.c8
-rw-r--r--gcc/sanitizer.def16
-rw-r--r--gcc/testsuite/ChangeLog9
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/attrib-3.c23
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/nonnull-1.c38
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/nonnull-2.c36
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/nonnull-3.c36
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/nonnull-4.c34
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/nonnull-5.c34
-rw-r--r--gcc/ubsan.c141
-rw-r--r--libsanitizer/ChangeLog5
-rw-r--r--libsanitizer/ubsan/ubsan_handlers.cc40
-rw-r--r--libsanitizer/ubsan/ubsan_handlers.h17
16 files changed, 471 insertions, 4 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 1749b280799..5da8e477f5d 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,23 @@
2014-09-10 Jakub Jelinek <jakub@redhat.com>
+ * flag-types.h (enum sanitize_code): Add SANITIZE_NONNULL_ATTRIBUTE
+ and SANITIZE_RETURNS_NONNULL_ATTRIBUTE, or them into SANITIZE_UNDEFINED.
+ * opts.c (common_handle_option): Handle SANITIZE_NONNULL_ATTRIBUTE and
+ SANITIZE_RETURNS_NONNULL_ATTRIBUTE and disable
+ flag_delete_null_pointer_checks for them.
+ * sanitizer.def (BUILT_IN_UBSAN_HANDLE_NONNULL_ARG,
+ BUILT_IN_UBSAN_HANDLE_NONNULL_ARG_ABORT,
+ BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN,
+ BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN_ABORT): New.
+ * ubsan.c (instrument_bool_enum_load): Set *gsi back to
+ stmt's iterator.
+ (instrument_nonnull_arg, instrument_nonnull_return): New functions.
+ (pass_ubsan::gate): Return true even for SANITIZE_NONNULL_ATTRIBUTE
+ or SANITIZE_RETURNS_NONNULL_ATTRIBUTE.
+ (pass_ubsan::execute): Call instrument_nonnull_{arg,return}.
+ * doc/invoke.texi (-fsanitize=nonnull-attribute,
+ -fsanitize=returns-nonnull-attribute): Document.
+
* ubsan.h (struct ubsan_mismatch_data): Removed.
(ubsan_create_data): Remove MISMATCH argument, add LOCCNT argument.
* ubsan.c (ubsan_source_location): For unknown locations,
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a680be918fc..863b382e868 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -5582,6 +5582,20 @@ This option enables floating-point type to integer conversion checking.
We check that the result of the conversion does not overflow.
This option does not work well with @code{FE_INVALID} exceptions enabled.
+@item -fsanitize=nonnull-attribute
+@opindex fsanitize=nonnull-attribute
+
+This option enables instrumentation of calls, checking whether null values
+are not passed to arguments marked as requiring a non-null value by the
+@code{nonnull} function attribute.
+
+@item -fsanitize=returns-nonnull-attribute
+@opindex fsanitize=returns-nonnull-attribute
+
+This option enables instrumentation of return statements in functions
+marked with @code{returns_nonnull} function attribute, to detect returning
+of null values from such functions.
+
@end table
While @option{-ftrapv} causes traps for signed overflows to be emitted,
diff --git a/gcc/flag-types.h b/gcc/flag-types.h
index 135c3434bbf..d0818e56825 100644
--- a/gcc/flag-types.h
+++ b/gcc/flag-types.h
@@ -234,10 +234,14 @@ enum sanitize_code {
SANITIZE_FLOAT_CAST = 1 << 15,
SANITIZE_BOUNDS = 1 << 16,
SANITIZE_ALIGNMENT = 1 << 17,
+ SANITIZE_NONNULL_ATTRIBUTE = 1 << 18,
+ SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1 << 19,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
| SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
- | SANITIZE_BOUNDS | SANITIZE_ALIGNMENT,
+ | SANITIZE_BOUNDS | SANITIZE_ALIGNMENT
+ | SANITIZE_NONNULL_ATTRIBUTE
+ | SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
};
diff --git a/gcc/opts.c b/gcc/opts.c
index 337e6cc5d0c..0a49bc0b32f 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1499,6 +1499,11 @@ common_handle_option (struct gcc_options *opts,
sizeof "float-cast-overflow" - 1 },
{ "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 },
{ "alignment", SANITIZE_ALIGNMENT, sizeof "alignment" - 1 },
+ { "nonnull-attribute", SANITIZE_NONNULL_ATTRIBUTE,
+ sizeof "nonnull-attribute" - 1 },
+ { "returns-nonnull-attribute",
+ SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
+ sizeof "returns-nonnull-attribute" - 1 },
{ NULL, 0, 0 }
};
const char *comma;
@@ -1542,7 +1547,8 @@ common_handle_option (struct gcc_options *opts,
/* When instrumenting the pointers, we don't want to remove
the null pointer checks. */
- if (flag_sanitize & SANITIZE_NULL)
+ if (flag_sanitize & (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
+ | SANITIZE_RETURNS_NONNULL_ATTRIBUTE))
opts->x_flag_delete_null_pointer_checks = 0;
/* Kernel ASan implies normal ASan but does not yet support
diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def
index 1f5ef210ab4..bba28bde334 100644
--- a/gcc/sanitizer.def
+++ b/gcc/sanitizer.def
@@ -417,3 +417,19 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT,
"__ubsan_handle_out_of_bounds_abort",
BT_FN_VOID_PTR_PTR,
ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_ARG,
+ "__ubsan_handle_nonnull_arg",
+ BT_FN_VOID_PTR_PTRMODE,
+ ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_ARG_ABORT,
+ "__ubsan_handle_nonnull_arg_abort",
+ BT_FN_VOID_PTR_PTRMODE,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN,
+ "__ubsan_handle_nonnull_return",
+ BT_FN_VOID_PTR,
+ ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN_ABORT,
+ "__ubsan_handle_nonnull_return_abort",
+ BT_FN_VOID_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index e18eea341e3..b6b96fddcde 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,12 @@
+2014-09-10 Jakub Jelinek <jakub@redhat.com>
+
+ * c-c++-common/ubsan/attrib-3.c: New test.
+ * c-c++-common/ubsan/nonnull-1.c: New test.
+ * c-c++-common/ubsan/nonnull-2.c: New test.
+ * c-c++-common/ubsan/nonnull-3.c: New test.
+ * c-c++-common/ubsan/nonnull-4.c: New test.
+ * c-c++-common/ubsan/nonnull-5.c: New test.
+
2014-09-10 Jan Hubicka <hubicka@ucw.cz>
* g++.dg/lto/pr63166_0.ii: New testcase.
diff --git a/gcc/testsuite/c-c++-common/ubsan/attrib-3.c b/gcc/testsuite/c-c++-common/ubsan/attrib-3.c
new file mode 100644
index 00000000000..3aaf49d85a5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/attrib-3.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+/* Test that we don't instrument functions marked with
+ no_sanitize_undefined attribute. */
+
+__attribute__((no_sanitize_undefined, returns_nonnull))
+char *
+foo (char *x)
+{
+ return x;
+}
+
+__attribute__((nonnull)) void bar (char *, int, char *);
+
+__attribute__((no_sanitize_undefined))
+void
+baz (char *x, int y, char *z)
+{
+ bar (x, y, z);
+}
+
+/* { dg-final { scan-assembler-not "__ubsan_handle" } } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-1.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-1.c
new file mode 100644
index 00000000000..d3063ca4a6f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-1.c
@@ -0,0 +1,38 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=nonnull-attribute,returns-nonnull-attribute" } */
+
+int q, r;
+void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h;
+
+__attribute__((returns_nonnull, nonnull (1, 3)))
+void *
+foo (void *p, void *q, void *r)
+{
+ a = p;
+ b = r;
+ return q;
+}
+
+int
+bar (const void *a, const void *b)
+{
+ int c = *(const int *) a;
+ int d = *(const int *) b;
+ return c - d;
+}
+
+int
+main ()
+{
+ asm volatile ("" : : : "memory");
+ d = foo (c, b, c);
+ e = foo (e, c, f);
+ g = foo (c, f, g);
+ __builtin_memset (d, '\0', q);
+ return 0;
+}
+
+/* { dg-output "\.c:13:\[0-9]*:\[^\n\r]*null pointer returned from function declared to never return null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:29:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:30:\[0-9]*:\[^\n\r]*null pointer passed as argument 3, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\.c:31:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-2.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-2.c
new file mode 100644
index 00000000000..49a5cf208e0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-2.c
@@ -0,0 +1,36 @@
+/* { dg-do run } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover" } */
+
+int q, r;
+void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h;
+
+__attribute__((returns_nonnull, nonnull (1, 3)))
+void *
+foo (void *p, void *q, void *r)
+{
+ a = p;
+ b = r;
+ return q;
+}
+
+int
+bar (const void *a, const void *b)
+{
+ int c = *(const int *) a;
+ int d = *(const int *) b;
+ return c - d;
+}
+
+int
+main ()
+{
+ asm volatile ("" : : : "memory");
+ d = foo (c, b, c);
+ e = foo (e, c, f);
+ g = foo (c, f, g);
+ __builtin_memset (d, '\0', q);
+ return 0;
+}
+
+/* { dg-output "\.c:14:\[0-9]*:\[^\n\r]*null pointer returned from function declared to never return null" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-3.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-3.c
new file mode 100644
index 00000000000..80018c2ef26
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-3.c
@@ -0,0 +1,36 @@
+/* { dg-do run } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover" } */
+
+int q, r;
+void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h;
+
+__attribute__((returns_nonnull, nonnull (1, 3)))
+void *
+foo (void *p, void *q, void *r)
+{
+ a = p;
+ b = r;
+ return q;
+}
+
+int
+bar (const void *a, const void *b)
+{
+ int c = *(const int *) a;
+ int d = *(const int *) b;
+ return c - d;
+}
+
+int
+main ()
+{
+ asm volatile ("" : : : "memory");
+ d = foo (c, (void *) &r, c);
+ e = foo (e, c, f);
+ g = foo (c, f, g);
+ __builtin_memset (d, '\0', q);
+ return 0;
+}
+
+/* { dg-output "\.c:30:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null" } */
diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-4.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-4.c
new file mode 100644
index 00000000000..b49c72e345c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-4.c
@@ -0,0 +1,34 @@
+/* { dg-do run } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-options "-fsanitize=undefined -fsanitize-undefined-trap-on-error" } */
+
+int q, r;
+void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h;
+
+__attribute__((returns_nonnull, nonnull (1, 3)))
+void *
+foo (void *p, void *q, void *r)
+{
+ a = p;
+ b = r;
+ return q;
+}
+
+int
+bar (const void *a, const void *b)
+{
+ int c = *(const int *) a;
+ int d = *(const int *) b;
+ return c - d;
+}
+
+int
+main ()
+{
+ asm volatile ("" : : : "memory");
+ d = foo (c, b, c);
+ e = foo (e, c, f);
+ g = foo (c, f, g);
+ __builtin_memset (d, '\0', q);
+ return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-5.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-5.c
new file mode 100644
index 00000000000..fefbdc3b4ca
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-5.c
@@ -0,0 +1,34 @@
+/* { dg-do run } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-options "-fsanitize=undefined -fsanitize-undefined-trap-on-error" } */
+
+int q, r;
+void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h;
+
+__attribute__((returns_nonnull, nonnull (1, 3)))
+void *
+foo (void *p, void *q, void *r)
+{
+ a = p;
+ b = r;
+ return q;
+}
+
+int
+bar (const void *a, const void *b)
+{
+ int c = *(const int *) a;
+ int d = *(const int *) b;
+ return c - d;
+}
+
+int
+main ()
+{
+ asm volatile ("" : : : "memory");
+ d = foo (c, (void *) &r, c);
+ e = foo (e, c, f);
+ g = foo (c, f, g);
+ __builtin_memset (d, '\0', q);
+ return 0;
+}
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index 745ca80d413..e3128ad6177 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -1090,6 +1090,7 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
}
gimple_set_location (g, loc);
gsi_insert_before (&gsi2, g, GSI_SAME_STMT);
+ *gsi = gsi_for_stmt (stmt);
}
/* Instrument float point-to-integer conversion. TYPE is an integer type of
@@ -1215,6 +1216,122 @@ ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
fn, integer_zero_node);
}
+/* Instrument values passed to function arguments with nonnull attribute. */
+
+static void
+instrument_nonnull_arg (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ location_t loc[2];
+ /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
+ while for nonnull sanitization it is clear. */
+ int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
+ flag_delete_null_pointer_checks = 1;
+ loc[0] = gimple_location (stmt);
+ loc[1] = UNKNOWN_LOCATION;
+ for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++)
+ {
+ tree arg = gimple_call_arg (stmt, i);
+ if (POINTER_TYPE_P (TREE_TYPE (arg))
+ && infer_nonnull_range (stmt, arg, false, true))
+ {
+ gimple g;
+ if (!is_gimple_val (arg))
+ {
+ g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg), NULL),
+ arg);
+ gimple_set_location (g, loc[0]);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ arg = gimple_assign_lhs (g);
+ }
+
+ basic_block then_bb, fallthru_bb;
+ *gsi = create_cond_insert_point (gsi, true, false, true,
+ &then_bb, &fallthru_bb);
+ g = gimple_build_cond (EQ_EXPR, arg,
+ build_zero_cst (TREE_TYPE (arg)),
+ NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc[0]);
+ gsi_insert_after (gsi, g, GSI_NEW_STMT);
+
+ *gsi = gsi_after_labels (then_bb);
+ if (flag_sanitize_undefined_trap_on_error)
+ g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ tree data = ubsan_create_data ("__ubsan_nonnull_arg_data",
+ 2, loc, NULL_TREE,
+ build_int_cst (integer_type_node,
+ i + 1),
+ NULL_TREE);
+ data = build_fold_addr_expr_loc (loc[0], data);
+ enum built_in_function bcode
+ = flag_sanitize_recover
+ ? BUILT_IN_UBSAN_HANDLE_NONNULL_ARG
+ : BUILT_IN_UBSAN_HANDLE_NONNULL_ARG_ABORT;
+ tree fn = builtin_decl_explicit (bcode);
+
+ g = gimple_build_call (fn, 1, data);
+ }
+ gimple_set_location (g, loc[0]);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ }
+ *gsi = gsi_for_stmt (stmt);
+ }
+ flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
+}
+
+/* Instrument returns in functions with returns_nonnull attribute. */
+
+static void
+instrument_nonnull_return (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ location_t loc[2];
+ tree arg = gimple_return_retval (stmt);
+ /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
+ while for nonnull return sanitization it is clear. */
+ int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
+ flag_delete_null_pointer_checks = 1;
+ loc[0] = gimple_location (stmt);
+ loc[1] = UNKNOWN_LOCATION;
+ if (arg
+ && POINTER_TYPE_P (TREE_TYPE (arg))
+ && is_gimple_val (arg)
+ && infer_nonnull_range (stmt, arg, false, true))
+ {
+ basic_block then_bb, fallthru_bb;
+ *gsi = create_cond_insert_point (gsi, true, false, true,
+ &then_bb, &fallthru_bb);
+ gimple g = gimple_build_cond (EQ_EXPR, arg,
+ build_zero_cst (TREE_TYPE (arg)),
+ NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc[0]);
+ gsi_insert_after (gsi, g, GSI_NEW_STMT);
+
+ *gsi = gsi_after_labels (then_bb);
+ if (flag_sanitize_undefined_trap_on_error)
+ g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ tree data = ubsan_create_data ("__ubsan_nonnull_return_data",
+ 2, loc, NULL_TREE, NULL_TREE);
+ data = build_fold_addr_expr_loc (loc[0], data);
+ enum built_in_function bcode
+ = flag_sanitize_recover
+ ? BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN
+ : BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN_ABORT;
+ tree fn = builtin_decl_explicit (bcode);
+
+ g = gimple_build_call (fn, 1, data);
+ }
+ gimple_set_location (g, loc[0]);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ *gsi = gsi_for_stmt (stmt);
+ }
+ flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
+}
+
namespace {
const pass_data pass_data_ubsan =
@@ -1242,7 +1359,9 @@ public:
{
return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW
| SANITIZE_BOOL | SANITIZE_ENUM
- | SANITIZE_ALIGNMENT)
+ | SANITIZE_ALIGNMENT
+ | SANITIZE_NONNULL_ATTRIBUTE
+ | SANITIZE_RETURNS_NONNULL_ATTRIBUTE)
&& current_function_decl != NULL_TREE
&& !lookup_attribute ("no_sanitize_undefined",
DECL_ATTRIBUTES (current_function_decl));
@@ -1285,7 +1404,25 @@ pass_ubsan::execute (function *fun)
if (flag_sanitize & (SANITIZE_BOOL | SANITIZE_ENUM)
&& gimple_assign_load_p (stmt))
- instrument_bool_enum_load (&gsi);
+ {
+ instrument_bool_enum_load (&gsi);
+ bb = gimple_bb (stmt);
+ }
+
+ if ((flag_sanitize & SANITIZE_NONNULL_ATTRIBUTE)
+ && is_gimple_call (stmt)
+ && !gimple_call_internal_p (stmt))
+ {
+ instrument_nonnull_arg (&gsi);
+ bb = gimple_bb (stmt);
+ }
+
+ if ((flag_sanitize & SANITIZE_RETURNS_NONNULL_ATTRIBUTE)
+ && gimple_code (stmt) == GIMPLE_RETURN)
+ {
+ instrument_nonnull_return (&gsi);
+ bb = gimple_bb (stmt);
+ }
gsi_next (&gsi);
}
diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog
index 4d5b71af4b9..7b9d84c86ad 100644
--- a/libsanitizer/ChangeLog
+++ b/libsanitizer/ChangeLog
@@ -1,3 +1,8 @@
+2014-09-10 Jakub Jelinek <jakub@redhat.com>
+
+ * ubsan/ubsan_handlers.cc, ubsan/ubsan_handlers.h: Cherry pick
+ upstream r215485, r217389, r217391 and r217400.
+
2014-06-23 Paolo Carlini <paolo.carlini@oracle.com>
* sanitizer_common/sanitizer_common_interceptors.inc:
diff --git a/libsanitizer/ubsan/ubsan_handlers.cc b/libsanitizer/ubsan/ubsan_handlers.cc
index dd2e7bbf3e5..42f948ae2e6 100644
--- a/libsanitizer/ubsan/ubsan_handlers.cc
+++ b/libsanitizer/ubsan/ubsan_handlers.cc
@@ -277,3 +277,43 @@ void __ubsan::__ubsan_handle_function_type_mismatch_abort(
__ubsan_handle_function_type_mismatch(Data, Function);
Die();
}
+
+static void handleNonnullReturn(NonNullReturnData *Data) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (Loc.isDisabled())
+ return;
+
+ Diag(Loc, DL_Error, "null pointer returned from function declared to never "
+ "return null");
+ if (!Data->AttrLoc.isInvalid())
+ Diag(Data->AttrLoc, DL_Note, "returns_nonnull attribute specified here");
+}
+
+void __ubsan::__ubsan_handle_nonnull_return(NonNullReturnData *Data) {
+ handleNonnullReturn(Data);
+}
+
+void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData *Data) {
+ handleNonnullReturn(Data);
+ Die();
+}
+
+static void handleNonNullArg(NonNullArgData *Data) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (Loc.isDisabled())
+ return;
+
+ Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to "
+ "never be null") << Data->ArgIndex;
+ if (!Data->AttrLoc.isInvalid())
+ Diag(Data->AttrLoc, DL_Note, "nonnull attribute specified here");
+}
+
+void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData *Data) {
+ handleNonNullArg(Data);
+}
+
+void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
+ handleNonNullArg(Data);
+ Die();
+}
diff --git a/libsanitizer/ubsan/ubsan_handlers.h b/libsanitizer/ubsan/ubsan_handlers.h
index 226faadc287..641fbfe993f 100644
--- a/libsanitizer/ubsan/ubsan_handlers.h
+++ b/libsanitizer/ubsan/ubsan_handlers.h
@@ -119,6 +119,23 @@ RECOVERABLE(function_type_mismatch,
FunctionTypeMismatchData *Data,
ValueHandle Val)
+struct NonNullReturnData {
+ SourceLocation Loc;
+ SourceLocation AttrLoc;
+};
+
+/// \brief Handle returning null from function with returns_nonnull attribute.
+RECOVERABLE(nonnull_return, NonNullReturnData *Data)
+
+struct NonNullArgData {
+ SourceLocation Loc;
+ SourceLocation AttrLoc;
+ int ArgIndex;
+};
+
+/// \brief Handle passing null pointer to function with nonnull attribute.
+RECOVERABLE(nonnull_arg, NonNullArgData *Data)
+
}
#endif // UBSAN_HANDLERS_H