public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Implement -fsanitize=object-size
@ 2014-07-13 17:55 Marek Polacek
  2014-07-13 21:10 ` Gerald Pfeifer
  2014-07-14 11:54 ` Jakub Jelinek
  0 siblings, 2 replies; 9+ messages in thread
From: Marek Polacek @ 2014-07-13 17:55 UTC (permalink / raw)
  To: Jakub Jelinek, GCC Patches; +Cc: Jeff Law

The following is an attempt to implement -fsanitize=object-size.
When it sees a MEM_REF, it goes through the definition statements
and stops on sth like ptr = &sth.  Then it tries to determine the
object size using __builtin_object_size and generates an internal
call (in .ubsan pass).  The .sanopt pass then expands this internal
call, and if the pointer points outside of the object, it issues
a runtime error.

Bootstrapped(-ubsan)/regtested on x86_64-linux, ok for trunk?

2014-07-13  Marek Polacek  <polacek@redhat.com>

	* ubsan.h (struct ubsan_mismatch_data):
	* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE.
	* doc/invoke.texi: Document -fsanitize=object-size.
	* flag-types.h (enum sanitize_code): Add SANITIZE_OBJECT_SIZE and
	or it into SANITIZE_UNDEFINED.
	* internal-fn.c	(expand_UBSAN_OBJECT_SIZE): New function.
	* internal-fn.def (UBSAN_OBJECT_SIZE): Define.
	* opts.c (common_handle_option): Handle -fsanitize=object-size.
	* ubsan.c: Include "tree-object-size.h".
	(ubsan_expand_objsize_ifn): New function.
	(instrument_object_size): New function.
	(pass_ubsan::execute): Add object size instrumentation.
	* ubsan.h (ubsan_expand_objsize_ifn): Declare.
testsuite/
	* c-c++-common/ubsan/object-size-1.c: New test.
	* c-c++-common/ubsan/object-size-2.c: New test.
	* c-c++-common/ubsan/object-size-3.c: New test.
	* c-c++-common/ubsan/object-size-4.c: New test.
	* c-c++-common/ubsan/object-size-5.c: New test.
	* c-c++-common/ubsan/object-size-6.c: New test.
	* c-c++-common/ubsan/object-size-7.c: New test.

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

	Marek

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Implement -fsanitize=object-size
  2014-07-13 17:55 [PATCH] Implement -fsanitize=object-size Marek Polacek
@ 2014-07-13 21:10 ` Gerald Pfeifer
  2014-07-14 11:54 ` Jakub Jelinek
  1 sibling, 0 replies; 9+ messages in thread
From: Gerald Pfeifer @ 2014-07-13 21:10 UTC (permalink / raw)
  To: Marek Polacek; +Cc: Jakub Jelinek, GCC Patches, Jeff Law

Hi Marek,

On Sun, 13 Jul 2014, Marek Polacek wrote:
> --- gcc/doc/invoke.texi
> +++ gcc/doc/invoke.texi
> @@ -5477,6 +5477,12 @@ This option enables instrumentation of array bounds.  Various out of bounds
>  accesses are detected.  Flexible array members and initializers of variables
>  with static storage are not instrumented.
>  
> +@item -fsanitize=object-size
> +@opindex fsanitize=object-size
> +This option enables instrumentation of memory references using the
> +@code{__builtin_object_size} function.  Various out of bounds pointer
> +accesses are detected.

I believe this should be "out-of-bounds" and, yes, there is a variant
without the dashes just above your patch already which would be good
to adjust as well.

(Best let's see what a native speaker suggests.)

Thanks,
Gerald

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Implement -fsanitize=object-size
  2014-07-13 17:55 [PATCH] Implement -fsanitize=object-size Marek Polacek
  2014-07-13 21:10 ` Gerald Pfeifer
@ 2014-07-14 11:54 ` Jakub Jelinek
  2014-09-11 17:48   ` Marek Polacek
  1 sibling, 1 reply; 9+ messages in thread
From: Jakub Jelinek @ 2014-07-14 11:54 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches, Jeff Law

On Sun, Jul 13, 2014 at 07:55:44PM +0200, Marek Polacek wrote:
> 2014-07-13  Marek Polacek  <polacek@redhat.com>
> 
> 	* ubsan.h (struct ubsan_mismatch_data):

Missing description.


> +/* Expand UBSAN_OBJECT_SIZE internal call.  */
> +
> +void
> +ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi)
> +{
> +  gimple stmt = gsi_stmt (*gsi);
> +  location_t loc = gimple_location (stmt);
> +  gcc_assert (gimple_call_num_args (stmt) == 4);
> +
> +  tree base = gimple_call_arg (stmt, 0);
> +  tree ptr = gimple_call_arg (stmt, 1);
> +  tree size = gimple_call_arg (stmt, 2);
> +  tree ckind = gimple_call_arg (stmt, 3);
> +  gimple_stmt_iterator gsi_orig = *gsi;
> +  gimple g;
> +
> +  gcc_assert (TREE_CODE (size) == INTEGER_CST);
> +  /* See if we can discard the check.  */
> +  if (tree_to_uhwi (size) == (unsigned HOST_WIDE_INT) -1)

This would be integer_all_onesp (size).

> +static void
> +instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs)
> +{
> +  gimple stmt = gsi_stmt (*gsi);
> +  location_t loc = gimple_location (stmt);
> +  tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt);
> +
> +  if (TREE_CODE (t) != MEM_REF)
> +    return;

I think this is undesirable.  IMHO you want to call here
get_inner_reference, and if the given size is equal to maxsize, consider
instrumenting it, otherwise you don't instrument e.g. COMPONENT_REFs and
many other things.  Look at what e.g. asan.c or even ubsan.c does; the
question is what exactly to do with bitfields, but supposedly we should
require that the DECL_BIT_FIELD_REPRESENTATIVE is accessible in that case.

Also, I wonder if using base, ptr, objsz, ckind arguments are best for the
builtin, I'd think you want instead base, ptr+size-base, objsz, ckind.
Reasons:
a) the size addition when expanding UBSAN_OBJECT_SIZE will not work
   reliably, the middle end considers all pointer conversions useless,
   so you can very well end up with a different TREE_TYPE of the pointer
   type
b) sanopt runs very late, there aren't many GIMPLE optimization passes,
   so to optimize the condition checks you pretty much rely on RTL passes
c) for e.g. gimple_fold_call it will be much easier if it can remove
   redundant UBSAN_OBJECT_SIZE calls if it can just compare two constants

> +  tree ptr = TREE_OPERAND (t, 0);
> +  tree sizet, base = ptr;
> +  gimple g;
> +  gimple def_stmt;
> +
> +  while (TREE_CODE (base) == SSA_NAME)
> +    {
> +      def_stmt = SSA_NAME_DEF_STMT (base);
> +      if (is_gimple_assign (def_stmt))
> +	base = gimple_assign_rhs1 (def_stmt);

This looks too dangerous.  All you should look through are:
a) gimple_assign_ssa_name_copy_p
b) gimple_assign_cast_p if the rhs1 also has POINTER_TYPE_P
c) gimple_assign_rhs_code == POINTER_PLUS_EXPR

I'm also including a testcase, which shows why instrumenting
also COMPONENT_REFs etc. is important (see my reference to
get_inner_reference above) and also that IMHO we should instrument
not just when the base is a pointer, but also when it is a decl,
but in that case we should avoid instrumenting when -fsanitize=bounds
is on and we know it will handle it (in particular, if there was e.g.
char d[8]; int e; in the struct definition instead).

Note, the testcase ICEs with -O2 -fsanitize=bounds, can you please look
at that first and fix it separately?

struct S { int a; int b; };
struct T { int c; char d[]; };

static inline __attribute__((always_inline)) int
foo (struct S *p)
{
  return p->b;
}

int
bar (void)
{
  struct S *p = __builtin_calloc (sizeof (int) + sizeof (int) / 2, 1);
  return foo (p);
}

struct T t = { 1, "abcdefg" };

int
baz (int i)
{
  return t.d[i];
}

Other comments, in a form of a patch:
1) the gimple_fold_call bit shows that we should for the quite common
   case where __bos is folded into -1 remove the UBSAN_OBJECT_SIZE call
   immediately, not worth keeping it around through many other passes
2) if you add -O2 to the dg-options, that just means the tests are done
   8 times or how many with -O2 all the time.  Better skip it unless
   -O2
3) when the second argument is something that can be directly compared
   against the third argument, you can in gimple_fold_call fold not just
   the "don't know" cases, but also when the third argument is >= the
   second and both are INTEGER_CSTs - then we know at compile time
   we are ok.

--- gcc/gimple-fold.c.jj	2014-07-07 10:39:45.000000000 +0200
+++ gcc/gimple-fold.c	2014-07-14 12:41:15.419687543 +0200
@@ -1209,6 +1209,15 @@ gimple_fold_call (gimple_stmt_iterator *
 					gimple_call_arg (stmt, 1),
 					gimple_call_arg (stmt, 2));
 	  break;
+        case IFN_UBSAN_OBJECT_SIZE:
+	  if (integer_all_onesp (gimple_call_arg (stmt, 2)))
+	    {
+	      gsi_replace (gsi, gimple_build_nop (), true);
+	      unlink_stmt_vdef (stmt);
+	      release_defs (stmt);
+	      return true;
+	    }
+	  break;
 	case IFN_UBSAN_CHECK_ADD:
 	  subcode = PLUS_EXPR;
 	  break;
--- gcc/testsuite/c-c++-common/ubsan/object-size-3.c.jj	2014-07-14 10:42:24.000000000 +0200
+++ gcc/testsuite/c-c++-common/ubsan/object-size-3.c	2014-07-14 12:08:00.379413090 +0200
@@ -1,5 +1,6 @@
 /* { dg-do run } */
-/* { dg-options "-fsanitize=object-size -O2 -fno-sanitize-recover" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size -fno-sanitize-recover" } */
 
 /* Test valid uses.  */
 
--- gcc/testsuite/c-c++-common/ubsan/object-size-2.c.jj	2014-07-14 10:42:24.000000000 +0200
+++ gcc/testsuite/c-c++-common/ubsan/object-size-2.c	2014-07-14 12:07:54.760440751 +0200
@@ -1,5 +1,6 @@
 /* { dg-do compile } */
-/* { dg-options "-fsanitize=undefined -O2" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
 
 void
 foo (unsigned long ul)
--- gcc/testsuite/c-c++-common/ubsan/object-size-6.c.jj	2014-07-14 10:42:24.000000000 +0200
+++ gcc/testsuite/c-c++-common/ubsan/object-size-6.c	2014-07-14 12:08:21.063305726 +0200
@@ -1,5 +1,6 @@
 /* { dg-do compile } */
-/* { dg-options "-fsanitize=object-size -O2" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
 
 char
 foo (void *v)
--- gcc/testsuite/c-c++-common/ubsan/object-size-4.c.jj	2014-07-14 10:42:24.000000000 +0200
+++ gcc/testsuite/c-c++-common/ubsan/object-size-4.c	2014-07-14 12:08:05.867384036 +0200
@@ -1,5 +1,6 @@
 /* { dg-do run } */
-/* { dg-options "-fsanitize=object-size -O2 -fno-sanitize-recover" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size -fno-sanitize-recover" } */
 
 /* Test that we don't instrument flexible array members.  */
 
--- gcc/testsuite/c-c++-common/ubsan/object-size-1.c.jj	2014-07-14 10:42:24.000000000 +0200
+++ gcc/testsuite/c-c++-common/ubsan/object-size-1.c	2014-07-14 12:07:49.463467643 +0200
@@ -1,5 +1,6 @@
 /* { dg-do run } */
-/* { dg-options "-fsanitize=undefined -O2" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
 
 /* Sanity-test -fsanitize=object-size.  We use -fsanitize=undefined option
    to check that this feature doesn't clash with -fsanitize=bounds et al.  */
--- gcc/testsuite/c-c++-common/ubsan/object-size-7.c.jj	2014-07-14 10:42:24.000000000 +0200
+++ gcc/testsuite/c-c++-common/ubsan/object-size-7.c	2014-07-14 12:08:26.685276937 +0200
@@ -1,5 +1,6 @@
 /* { dg-do run } */
-/* { dg-options "-fsanitize=object-size -O2" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
 
 #define N 20
 
--- gcc/testsuite/c-c++-common/ubsan/object-size-5.c.jj	2014-07-14 10:42:24.000000000 +0200
+++ gcc/testsuite/c-c++-common/ubsan/object-size-5.c	2014-07-14 12:08:10.901364754 +0200
@@ -1,5 +1,6 @@
 /* { dg-do run } */
-/* { dg-options "-fsanitize=object-size -O2" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
 
 /* Test structures with -fsanitize=object-size.  */
 

	Jakub

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Implement -fsanitize=object-size
  2014-07-14 11:54 ` Jakub Jelinek
@ 2014-09-11 17:48   ` Marek Polacek
  2014-10-02 12:04     ` Jakub Jelinek
  0 siblings, 1 reply; 9+ messages in thread
From: Marek Polacek @ 2014-09-11 17:48 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

Sorry I let this slide.

On Mon, Jul 14, 2014 at 01:54:13PM +0200, Jakub Jelinek wrote:
> On Sun, Jul 13, 2014 at 07:55:44PM +0200, Marek Polacek wrote:
> > 2014-07-13  Marek Polacek  <polacek@redhat.com>
> > 
> > 	* ubsan.h (struct ubsan_mismatch_data):
> 
> Missing description.

Fixed.
 
> > +  gcc_assert (TREE_CODE (size) == INTEGER_CST);
> > +  /* See if we can discard the check.  */
> > +  if (tree_to_uhwi (size) == (unsigned HOST_WIDE_INT) -1)
> 
> This would be integer_all_onesp (size).

Fixed.

> > +static void
> > +instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs)
> > +{
> > +  gimple stmt = gsi_stmt (*gsi);
> > +  location_t loc = gimple_location (stmt);
> > +  tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt);
> > +
> > +  if (TREE_CODE (t) != MEM_REF)
> > +    return;
> 
> I think this is undesirable.  IMHO you want to call here
> get_inner_reference, and if the given size is equal to maxsize, consider
> instrumenting it, otherwise you don't instrument e.g. COMPONENT_REFs and
> many other things.  Look at what e.g. asan.c or even ubsan.c does; the
> question is what exactly to do with bitfields, but supposedly we should
> require that the DECL_BIT_FIELD_REPRESENTATIVE is accessible in that case.

Adjusted.  For bit-fields I just bail out; it's this check:
  || bitsize != size_in_bytes * BITS_PER_UNIT)

> Also, I wonder if using base, ptr, objsz, ckind arguments are best for the
> builtin, I'd think you want instead base, ptr+size-base, objsz, ckind.
> Reasons:
> a) the size addition when expanding UBSAN_OBJECT_SIZE will not work
>    reliably, the middle end considers all pointer conversions useless,
>    so you can very well end up with a different TREE_TYPE of the pointer
>    type
> b) sanopt runs very late, there aren't many GIMPLE optimization passes,
>    so to optimize the condition checks you pretty much rely on RTL passes
> c) for e.g. gimple_fold_call it will be much easier if it can remove
>    redundant UBSAN_OBJECT_SIZE calls if it can just compare two constants
 
Ok, that's better.  I rewrote this part.

> > +  tree ptr = TREE_OPERAND (t, 0);
> > +  tree sizet, base = ptr;
> > +  gimple g;
> > +  gimple def_stmt;
> > +
> > +  while (TREE_CODE (base) == SSA_NAME)
> > +    {
> > +      def_stmt = SSA_NAME_DEF_STMT (base);
> > +      if (is_gimple_assign (def_stmt))
> > +	base = gimple_assign_rhs1 (def_stmt);
> 
> This looks too dangerous.  All you should look through are:
> a) gimple_assign_ssa_name_copy_p
> b) gimple_assign_cast_p if the rhs1 also has POINTER_TYPE_P
> c) gimple_assign_rhs_code == POINTER_PLUS_EXPR

Fixed.

> I'm also including a testcase, which shows why instrumenting
> also COMPONENT_REFs etc. is important (see my reference to
> get_inner_reference above) and also that IMHO we should instrument
> not just when the base is a pointer, but also when it is a decl,

Fixed, we now properly instrument the testcase you posted, because I've
added instrumentation even of VAR_DECLs.

> but in that case we should avoid instrumenting when -fsanitize=bounds
> is on and we know it will handle it (in particular, if there was e.g.
> char d[8]; int e; in the struct definition instead).
 
I haven't fixed this, because it seems that when doing the object-size
instrumentation I can't tell whether the array has been
bounds-instrumented or not.  So for some structs we can issue both
=bounds and =object-size diagnostics.

> Note, the testcase ICEs with -O2 -fsanitize=bounds, can you please look
> at that first and fix it separately?

Already fixed.

> Other comments, in a form of a patch:
> 1) the gimple_fold_call bit shows that we should for the quite common
>    case where __bos is folded into -1 remove the UBSAN_OBJECT_SIZE call
>    immediately, not worth keeping it around through many other passes

Sure.

> 2) if you add -O2 to the dg-options, that just means the tests are done
>    8 times or how many with -O2 all the time.  Better skip it unless
>    -O2

Ugh.

> 3) when the second argument is something that can be directly compared
>    against the third argument, you can in gimple_fold_call fold not just
>    the "don't know" cases, but also when the third argument is >= the
>    second and both are INTEGER_CSTs - then we know at compile time
>    we are ok.

Thanks, I applied the patch.  And I've added some more optimizations to
gimple_fold_call.

So, how does this look now?

Bootstrapped/regtested on x86_64-linux, passes even bootstrap-ubsan.

2014-09-11  Marek Polacek  <polacek@redhat.com>

	* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE.
	* doc/invoke.texi: Document -fsanitize=object-size.
	* flag-types.h (sanitize_code): Add SANITIZE_OBJECT_SIZE and
	or it into SANITIZE_UNDEFINED.
	* gimple-fold.c (gimple_fold_call): Optimize IFN_UBSAN_OBJECT_SIZE.
	* internal-fn.c	(expand_UBSAN_OBJECT_SIZE): New function.
	* internal-fn.def (UBSAN_OBJECT_SIZE): Define.
	* opts.c (common_handle_option): Handle -fsanitize=object-size.
	* ubsan.c: Include "tree-object-size.h".
	(ubsan_type_descriptor): Call tree_to_uhwi instead of tree_to_shwi. 
	(ubsan_expand_bounds_ifn): Use false instead of 0.
	(ubsan_expand_objsize_ifn): New function.
	(instrument_object_size): New function.
	(pass_ubsan::execute): Add object size instrumentation.
	* ubsan.h (ubsan_expand_objsize_ifn): Declare.
testsuite/
	* c-c++-common/ubsan/object-size-1.c: New test.
	* c-c++-common/ubsan/object-size-2.c: New test.
	* c-c++-common/ubsan/object-size-3.c: New test.
	* c-c++-common/ubsan/object-size-4.c: New test.
	* c-c++-common/ubsan/object-size-5.c: New test.
	* c-c++-common/ubsan/object-size-6.c: New test.
	* c-c++-common/ubsan/object-size-7.c: New test.
	* c-c++-common/ubsan/object-size-8.c: New test.
	* g++.dg/ubsan/object-size-1.C: New test.
	* gcc.dg/ubsan/object-size-9.c: New test.

diff --git gcc/asan.c gcc/asan.c
index cf5de27..44bc627 100644
--- gcc/asan.c
+++ gcc/asan.c
@@ -2822,6 +2822,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_UBSAN_BOUNDS:
 		  no_next = ubsan_expand_bounds_ifn (&gsi);
 		  break;
+		case IFN_UBSAN_OBJECT_SIZE:
+		  no_next = ubsan_expand_objsize_ifn (&gsi);
+		  break;
 		case IFN_ASAN_CHECK:
 		  {
 		    no_next = asan_expand_check_ifn (&gsi, use_calls);
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index 863b382..bb97b88 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -5569,6 +5569,12 @@ 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=object-size
+@opindex fsanitize=object-size
+This option enables instrumentation of memory references using the
+@code{__builtin_object_size} function.  Various out of bounds pointer
+accesses are detected.
+
 @item -fsanitize=float-divide-by-zero
 @opindex fsanitize=float-divide-by-zero
 Detect floating-point division by zero.  Unlike other similar options,
diff --git gcc/flag-types.h gcc/flag-types.h
index d0818e5..3d01c49 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -236,12 +236,14 @@ enum sanitize_code {
   SANITIZE_ALIGNMENT = 1 << 17,
   SANITIZE_NONNULL_ATTRIBUTE = 1 << 18,
   SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1 << 19,
+  SANITIZE_OBJECT_SIZE = 1 << 20,
   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_NONNULL_ATTRIBUTE
-		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
+		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
+		       | SANITIZE_OBJECT_SIZE,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
diff --git gcc/gimple-fold.c gcc/gimple-fold.c
index 4aa1f4c..a6f6892 100644
--- gcc/gimple-fold.c
+++ gcc/gimple-fold.c
@@ -2661,6 +2661,19 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 					gimple_call_arg (stmt, 1),
 					gimple_call_arg (stmt, 2));
 	  break;
+        case IFN_UBSAN_OBJECT_SIZE:
+	  if (integer_all_onesp (gimple_call_arg (stmt, 2))
+	      || (TREE_CODE (gimple_call_arg (stmt, 1)) == INTEGER_CST
+		  && TREE_CODE (gimple_call_arg (stmt, 2)) == INTEGER_CST
+		  && tree_int_cst_le (gimple_call_arg (stmt, 1),
+				      gimple_call_arg (stmt, 2))))
+	    {
+	      gsi_replace (gsi, gimple_build_nop (), true);
+	      unlink_stmt_vdef (stmt);
+	      release_defs (stmt);
+	      return true;
+	    }
+	  break;
 	case IFN_UBSAN_CHECK_ADD:
 	  subcode = PLUS_EXPR;
 	  break;
diff --git gcc/internal-fn.c gcc/internal-fn.c
index c2a44b6..c71259d 100644
--- gcc/internal-fn.c
+++ gcc/internal-fn.c
@@ -184,6 +184,14 @@ expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED)
 /* This should get expanded in the sanopt pass.  */
 
 static void
+expand_UBSAN_OBJECT_SIZE (gimple stmt ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
 expand_ASAN_CHECK (gimple stmt ATTRIBUTE_UNUSED)
 {
   gcc_unreachable ();
diff --git gcc/internal-fn.def gcc/internal-fn.def
index 7ae60f3..fdb2d11 100644
--- gcc/internal-fn.def
+++ gcc/internal-fn.def
@@ -53,6 +53,7 @@ DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW, NULL)
 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_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)
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".W..")
diff --git gcc/opts.c gcc/opts.c
index 0a49bc0..835f859 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1504,6 +1504,8 @@ common_handle_option (struct gcc_options *opts,
 	      { "returns-nonnull-attribute",
 		SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
 		sizeof "returns-nonnull-attribute" - 1 },
+	      { "object-size", SANITIZE_OBJECT_SIZE,
+		sizeof "object-size" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-1.c gcc/testsuite/c-c++-common/ubsan/object-size-1.c
index e69de29..7a3c87a 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-1.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-1.c
@@ -0,0 +1,125 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+/* Sanity-test -fsanitize=object-size.  We use -fsanitize=undefined option
+   to check that this feature doesn't clash with -fsanitize=bounds et al.  */
+
+#define N 20
+
+__attribute__((noinline, noclone)) void
+f1 (int i)
+{
+  volatile int j;
+  char *p, *orig;
+  orig = p = (char *) __builtin_calloc (N, 1);
+  j = *(p + i);
+  j = p[i];
+  p++;
+  j = p[i - 1];
+  j = *(p + i - 1);
+  __builtin_free (orig);
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f2 (int i)
+{
+  volatile int j;
+  char a[N];
+  __builtin_memset (a, 0, N);
+  j = *(a + i);
+  char *p = a;
+  j = *(p + i);
+  j = p[i];
+  p += 10;
+  j = *(p + i - 10);
+  j = p[i - 10];
+}
+
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f3 (int i)
+{
+  volatile int j;
+  int *p = (int *) __builtin_calloc (N, sizeof (*p));
+  int *o = &p[i];
+  j = *o;
+  j = o[0];
+  __builtin_free (p);
+}
+
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f4 (void)
+{
+  /* The second argument to __builtin_calloc is intentional.  */
+  int *p = (int *) __builtin_calloc (3, 1);
+  *p = 42;
+  __builtin_free (p);
+}
+
+/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f5 (int *p)
+{
+  /* This is not instrumented.  But don't ICE, etc.  */
+  volatile int i = p[N];
+}
+
+int
+main ()
+{
+  f1 (N);
+  f2 (N);
+  f3 (N);
+  f4 ();
+  int *p = (int *) __builtin_calloc (N, sizeof (*p));
+  f5 (p);
+  __builtin_free (p);
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-2.c gcc/testsuite/c-c++-common/ubsan/object-size-2.c
index e69de29..dba1243 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-2.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-2.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+void
+foo (unsigned long ul)
+{
+  unsigned int u;
+  u = *(unsigned long *) ul;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-3.c gcc/testsuite/c-c++-common/ubsan/object-size-3.c
index e69de29..62dc76f 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-3.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-3.c
@@ -0,0 +1,56 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size -fno-sanitize-recover" } */
+
+/* Test valid uses.  */
+
+#define N 20
+
+__attribute__((noinline, noclone)) void
+f1 (int i)
+{
+  volatile int j;
+  char *p, *orig;
+  orig = p = (char *) __builtin_calloc (N, 1);
+  j = *(p + i);
+  j = p[i];
+  p++;
+  j = p[i - 1];
+  j = *(p + i - 1);
+  __builtin_free (orig);
+}
+
+__attribute__((noinline, noclone)) void
+f2 (int i)
+{
+  volatile int j;
+  char a[N];
+  __builtin_memset (a, 0, N);
+  j = *(a + i);
+  char *p = a;
+  j = *(p + i);
+  j = p[i];
+  p += 10;
+  j = *(p + i - 10);
+  j = p[i - 10];
+}
+
+__attribute__((noinline, noclone)) void
+f3 (int i)
+{
+  volatile int j;
+  int *p = (int *) __builtin_calloc (N, sizeof (*p));
+  int *o = &p[i];
+  j = *o;
+  j = o[0];
+  __builtin_free (p);
+}
+
+int
+main ()
+{
+  f1 (N - 1);
+  f2 (N - 1);
+  f3 (N - 1);
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-4.c gcc/testsuite/c-c++-common/ubsan/object-size-4.c
index e69de29..8b95ec9 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-4.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-4.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+/* Test that we instrument flexible array members.  */
+
+struct T { int l; int a[]; };
+struct U { int l; int a[0]; };
+
+int
+main (void)
+{
+  volatile int i;
+  struct T *t = (struct T *) __builtin_calloc (sizeof (struct T)
+					       + sizeof (int), 1);
+  i = t->a[1];
+
+  struct U *u = (struct U *) __builtin_calloc (sizeof (struct U)
+					       + sizeof (int), 1);
+  i = u->a[1];
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-5.c gcc/testsuite/c-c++-common/ubsan/object-size-5.c
index e69de29..3dada10 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-5.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-5.c
@@ -0,0 +1,38 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+/* Test structures with -fsanitize=object-size.  */
+
+#define N 20
+
+struct S { char *p; int i; };
+struct T { struct S *s; };
+
+__attribute__((noinline, noclone)) void
+f1 (int i)
+{
+  volatile int j;
+  struct S s;
+  s.p = (char *) __builtin_calloc (N, 1);
+  j = s.p[i];
+  j = *(s.p + i);
+  __builtin_free (s.p);
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+int
+main ()
+{
+  f1 (N);
+  f1 (N - 1);
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-6.c gcc/testsuite/c-c++-common/ubsan/object-size-6.c
index e69de29..0e6035d 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-6.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-6.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+char
+foo (void *v)
+{
+  return *(char *) v;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-7.c gcc/testsuite/c-c++-common/ubsan/object-size-7.c
index e69de29..f5b26e5 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-7.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-7.c
@@ -0,0 +1,29 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+#define N 20
+
+struct S { int a; };
+
+__attribute__((noinline, noclone)) struct S
+f1 (int i)
+{
+  struct S a[N];
+  struct S *p = a;
+  struct S s;
+  s = p[i];
+  return s;
+}
+
+int
+main ()
+{
+  f1 (N);
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-8.c gcc/testsuite/c-c++-common/ubsan/object-size-8.c
index e69de29..ee0945b 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-8.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-8.c
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+struct S { int a; int b; };
+
+static inline __attribute__((always_inline)) int
+foo (struct S *p)
+{
+  volatile int a;
+  a = p->a; /* OK */
+  return p->b;
+}
+
+int
+bar (void)
+{
+  struct S *p = (struct S *) __builtin_calloc (sizeof (int) + sizeof (int) / 2, 1);
+  return foo (p);
+}
+
+int
+main (void)
+{
+  bar ();
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/g++.dg/ubsan/object-size-1.C gcc/testsuite/g++.dg/ubsan/object-size-1.C
index e69de29..e2aad46 100644
--- gcc/testsuite/g++.dg/ubsan/object-size-1.C
+++ gcc/testsuite/g++.dg/ubsan/object-size-1.C
@@ -0,0 +1,18 @@
+// { dg-do compile }
+// { dg-options "-fsanitize=undefined -fpermissive" }
+
+struct T { int c; char d[]; };
+
+struct T t = { 1, "a" }; // { dg-warning "initializer-string for array of chars is too long" }
+
+int
+baz (int i)
+{
+  return t.d[i];
+}
+
+int
+main (void)
+{
+  baz (3);
+}
diff --git gcc/testsuite/gcc.dg/ubsan/object-size-9.c gcc/testsuite/gcc.dg/ubsan/object-size-9.c
index e69de29..bb9aa1b 100644
--- gcc/testsuite/gcc.dg/ubsan/object-size-9.c
+++ gcc/testsuite/gcc.dg/ubsan/object-size-9.c
@@ -0,0 +1,24 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+struct T { int c; char d[]; };
+struct T t = { 1, "a" };
+
+int
+baz (int i)
+{
+  return t.d[i];
+}
+
+int
+main (void)
+{
+  baz (2);
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/ubsan.c gcc/ubsan.c
index e3128ad..739a6e7 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "realmpfr.h"
 #include "dfp.h"
 #include "builtins.h"
+#include "tree-object-size.h"
 
 /* Map from a tree to a VAR_DECL tree.  */
 
@@ -384,7 +385,7 @@ ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 	  tree dom = TYPE_DOMAIN (t);
 	  if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST)
 	    pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC,
-			    tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1);
+			    tree_to_uhwi (TYPE_MAX_VALUE (dom)) + 1);
 	  else
 	    /* ??? We can't determine the variable name; print VLA unspec.  */
 	    pretty_name[pos++] = '*';
@@ -607,12 +608,12 @@ ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
   /* Create condition "if (index > bound)".  */
   basic_block then_bb, fallthru_bb;
   gimple_stmt_iterator cond_insert_point
-    = create_cond_insert_point (gsi, 0/*before_p*/, false, true,
+    = create_cond_insert_point (gsi, false, false, true,
 				&then_bb, &fallthru_bb);
   index = fold_convert (TREE_TYPE (bound), index);
   index = force_gimple_operand_gsi (&cond_insert_point, index,
-				    true/*simple_p*/, NULL_TREE,
-				    false/*before*/, GSI_NEW_STMT);
+				    true, NULL_TREE,
+				    false, GSI_NEW_STMT);
   gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE);
   gimple_set_location (g, loc);
   gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
@@ -823,6 +824,76 @@ ubsan_expand_null_ifn (gimple_stmt_iterator *gsip)
   return false;
 }
 
+/* Expand UBSAN_OBJECT_SIZE internal call.  */
+
+bool
+ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  gcc_assert (gimple_call_num_args (stmt) == 4);
+
+  tree ptr = gimple_call_arg (stmt, 0);
+  tree offset = gimple_call_arg (stmt, 1);
+  tree size = gimple_call_arg (stmt, 2);
+  tree ckind = gimple_call_arg (stmt, 3);
+  gimple_stmt_iterator gsi_orig = *gsi;
+  gimple g;
+
+  gcc_assert (TREE_CODE (size) == INTEGER_CST);
+  /* See if we can discard the check.  */
+  if (integer_all_onesp (size))
+    /* Yes, __builtin_object_size couldn't determine the
+       object size.  */;
+  else
+    {
+      /* if (offset > objsize) */
+      basic_block then_bb, fallthru_bb;
+      gimple_stmt_iterator cond_insert_point
+	= create_cond_insert_point (gsi, false, false, true,
+				    &then_bb, &fallthru_bb);
+      g = gimple_build_cond (GT_EXPR, offset, size, NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+      gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+
+      /* Generate __ubsan_handle_type_mismatch call.  */
+      *gsi = gsi_after_labels (then_bb);
+      if (flag_sanitize_undefined_trap_on_error)
+	g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+      else
+	{
+	  tree data
+	    = ubsan_create_data ("__ubsan_objsz_data", 1, &loc,
+				 ubsan_type_descriptor (TREE_TYPE (ptr),
+							UBSAN_PRINT_POINTER),
+				 NULL_TREE,
+				 build_zero_cst (pointer_sized_int_node),
+				 ckind,
+				 NULL_TREE);
+	  data = build_fold_addr_expr_loc (loc, data);
+	  enum built_in_function bcode
+	    = flag_sanitize_recover
+	      ? BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH
+	      : BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT;
+	  tree p = make_ssa_name (pointer_sized_int_node, NULL);
+	  g = gimple_build_assign_with_ops (NOP_EXPR, p, ptr, NULL_TREE);
+	  gimple_set_location (g, loc);
+	  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+	  g = gimple_build_call (builtin_decl_explicit (bcode), 2, data, p);
+	}
+      gimple_set_location (g, loc);
+      gsi_insert_before (gsi, g, GSI_SAME_STMT);
+
+      /* Point GSI to next logical statement.  */
+      *gsi = gsi_start_bb (fallthru_bb);
+    }
+
+  /* Get rid of the UBSAN_OBJECT_SIZE call from the IR.  */
+  unlink_stmt_vdef (stmt);
+  gsi_remove (&gsi_orig, true);
+  return gsi_end_p (*gsi);
+}
+
 /* 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.  */
 
@@ -1332,6 +1403,118 @@ instrument_nonnull_return (gimple_stmt_iterator *gsi)
   flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
 }
 
+/* Instrument memory references.  Here we check whether the pointer
+   points to an out-of-bounds location.  */
+
+static void
+instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt);
+  tree type;
+  HOST_WIDE_INT size_in_bytes;
+
+  type = TREE_TYPE (t);
+  if (VOID_TYPE_P (type))
+    return;
+
+  switch (TREE_CODE (t))
+    {
+    case ARRAY_REF:
+    case COMPONENT_REF:
+    case INDIRECT_REF:
+    case MEM_REF:
+    case VAR_DECL:
+      break;
+    default:
+      return;
+    }
+
+  size_in_bytes = int_size_in_bytes (type);
+  if (size_in_bytes <= 0)
+    return;
+
+  HOST_WIDE_INT bitsize, bitpos;
+  tree offset;
+  enum machine_mode mode;
+  int volatilep = 0, unsignedp = 0;
+  tree inner = get_inner_reference (t, &bitsize, &bitpos, &offset, &mode,
+				    &unsignedp, &volatilep, false);
+
+  if (bitpos % BITS_PER_UNIT != 0
+      || bitsize != size_in_bytes * BITS_PER_UNIT)
+    return;
+
+  bool decl_p = VAR_P (inner) || TREE_CODE (inner) == PARM_DECL
+		|| TREE_CODE (inner) == RESULT_DECL;
+  tree base = decl_p ? inner : TREE_OPERAND (inner, 0);
+  tree ptr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (t)), t);
+
+  while (TREE_CODE (base) == SSA_NAME)
+    {
+      gimple def_stmt = SSA_NAME_DEF_STMT (base);
+      if (gimple_assign_ssa_name_copy_p (def_stmt)
+	  || (gimple_assign_cast_p (def_stmt)
+	      && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (def_stmt))))
+	  || (is_gimple_assign (def_stmt)
+	      && gimple_assign_rhs_code (def_stmt) == POINTER_PLUS_EXPR))
+	base = gimple_assign_rhs1 (def_stmt);
+      else
+	break;
+    }
+
+  if (!POINTER_TYPE_P (TREE_TYPE (base)) && !VAR_P (base))
+    return;
+
+  tree sizet;
+  tree base_addr = build1 (ADDR_EXPR,
+			   build_pointer_type (TREE_TYPE (base)), base);
+  unsigned HOST_WIDE_INT size = compute_builtin_object_size (decl_p ? base_addr
+								    : base, 0);
+  if (size != (unsigned HOST_WIDE_INT) -1)
+    sizet = build_int_cst (sizetype, size);
+  else if (optimize)
+    {
+      /* Generate __builtin_object_size call.  */
+      sizet = builtin_decl_explicit (BUILT_IN_OBJECT_SIZE);
+      
+      sizet = build_call_expr_loc (loc, sizet, 2, decl_p ? base_addr : base,
+				   integer_zero_node);
+      sizet = force_gimple_operand_gsi (gsi, sizet, false, NULL_TREE, true,
+					GSI_SAME_STMT);
+    }
+  else
+    return;
+
+  /* Generate UBSAN_OBJECT_SIZE (ptr, ptr+sizeof(*ptr)-base, objsize, ckind)
+     call.  */
+  /* ptr + sizeof (*ptr) - base */
+  t = fold_build2 (MINUS_EXPR, sizetype,
+		   fold_convert (pointer_sized_int_node, ptr),
+		   fold_convert (pointer_sized_int_node,
+				 decl_p ? base_addr : base));
+  t = fold_build2 (PLUS_EXPR, sizetype, t, TYPE_SIZE_UNIT (type));
+
+  /* Perhaps we can omit the check.  */
+  if (TREE_CODE (t) == INTEGER_CST
+      && TREE_CODE (sizet) == INTEGER_CST
+      && tree_int_cst_le (t, sizet))
+    return;
+
+  /* Nope.  Emit the check.  */
+  t = force_gimple_operand_gsi (gsi, t, true, NULL_TREE, true,
+				GSI_SAME_STMT);
+  ptr = force_gimple_operand_gsi (gsi, ptr, true, NULL_TREE, true,
+				  GSI_SAME_STMT);
+  tree ckind = build_int_cst (unsigned_char_type_node,
+			      is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF);
+  gimple g = gimple_build_call_internal (IFN_UBSAN_OBJECT_SIZE, 4,
+					 ptr, t, sizet, ckind);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+}
+
 namespace {
 
 const pass_data pass_data_ubsan =
@@ -1361,7 +1544,8 @@ public:
 			      | SANITIZE_BOOL | SANITIZE_ENUM
 			      | SANITIZE_ALIGNMENT
 			      | SANITIZE_NONNULL_ATTRIBUTE
-			      | SANITIZE_RETURNS_NONNULL_ATTRIBUTE)
+			      | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
+			      | SANITIZE_OBJECT_SIZE)
 	     && current_function_decl != NULL_TREE
 	     && !lookup_attribute ("no_sanitize_undefined",
 				   DECL_ATTRIBUTES (current_function_decl));
@@ -1424,6 +1608,14 @@ pass_ubsan::execute (function *fun)
 	      bb = gimple_bb (stmt);
 	    }
 
+	  if (flag_sanitize & SANITIZE_OBJECT_SIZE)
+	    {
+	      if (gimple_store_p (stmt))
+		instrument_object_size (&gsi, true);
+	      if (gimple_assign_load_p (stmt))
+		instrument_object_size (&gsi, false);
+	    }
+
 	  gsi_next (&gsi);
 	}
     }
diff --git gcc/ubsan.h gcc/ubsan.h
index cd26940..2a261d8 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -40,6 +40,7 @@ enum ubsan_print_style {
 
 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 tree ubsan_instrument_unreachable (location_t);
 extern tree ubsan_create_data (const char *, int, const location_t *, ...);
 extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);

	Marek

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Implement -fsanitize=object-size
  2014-09-11 17:48   ` Marek Polacek
@ 2014-10-02 12:04     ` Jakub Jelinek
  2014-10-10 10:26       ` Marek Polacek
  0 siblings, 1 reply; 9+ messages in thread
From: Jakub Jelinek @ 2014-10-02 12:04 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 4041 bytes --]

On Thu, Sep 11, 2014 at 07:47:51PM +0200, Marek Polacek wrote:
> So, how does this look now?

Looks much better.

There are some nits I'd change, like:
1) no need not to handle bitfields
2) IMHO it should handle PARM_DECL and RESULT_DECL alongside of VAR_DECL
3) decl_p IMHO should use just DECL_P
4) it doesn't make sense to build ADDR_EXPR if you are never going to use
   it

Attaching some (incomplete) testcase I've used to look at the
implementation, e.g. f1 is for testing how it plays together with
-fsanitize=bounds (perhaps, maybe as a follow-up, we could optimize by
looking for UBSAN_BOUNDS call with expected arguments in a few statements
before array access (e.g. remember last UBSAN_BOUNDS seen in the same
basic block) if inner is DECL_P), f2 to show a case which -fsanitize=bounds
doesn't instrument, but -fsanitize=object-size should, f3/f4 the same with
PARM_DECL, f5/f6 unsuccessful attempt for RESULT_DECL, f7/f8 bitfield
tests, f9 something where __bos is folded very early (already before objsz1
pass), f10 where __bos is folded during objsz1 pass.
If you want to turn parts of that testcase into real /ubsan/ tests, go
ahead.  Other than that, if you are fine with following changes, can you
incorporate them into the patch and retest?

Thanks.

--- gcc/ubsan.c.jj	2014-10-02 12:26:30.000000000 +0200
+++ gcc/ubsan.c	2014-10-02 12:57:40.131267225 +0200
@@ -1421,11 +1421,21 @@ instrument_object_size (gimple_stmt_iter
 
   switch (TREE_CODE (t))
     {
-    case ARRAY_REF:
     case COMPONENT_REF:
+      if (TREE_CODE (t) == COMPONENT_REF
+	  && DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1)) != NULL_TREE)
+	{
+	  tree repr = DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1));
+	  t = build3 (COMPONENT_REF, TREE_TYPE (repr), TREE_OPERAND (t, 0),
+		      repr, NULL_TREE);
+	}
+      break;
+    case ARRAY_REF:
     case INDIRECT_REF:
     case MEM_REF:
     case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
       break;
     default:
       return;
@@ -1446,8 +1456,7 @@ instrument_object_size (gimple_stmt_iter
       || bitsize != size_in_bytes * BITS_PER_UNIT)
     return;
 
-  bool decl_p = VAR_P (inner) || TREE_CODE (inner) == PARM_DECL
-		|| TREE_CODE (inner) == RESULT_DECL;
+  bool decl_p = DECL_P (inner);
   tree base = decl_p ? inner : TREE_OPERAND (inner, 0);
   tree ptr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (t)), t);
 
@@ -1464,14 +1473,15 @@ instrument_object_size (gimple_stmt_iter
 	break;
     }
 
-  if (!POINTER_TYPE_P (TREE_TYPE (base)) && !VAR_P (base))
+  if (!POINTER_TYPE_P (TREE_TYPE (base)) && !DECL_P (base))
     return;
 
   tree sizet;
-  tree base_addr = build1 (ADDR_EXPR,
-			   build_pointer_type (TREE_TYPE (base)), base);
-  unsigned HOST_WIDE_INT size = compute_builtin_object_size (decl_p ? base_addr
-								    : base, 0);
+  tree base_addr = base;
+  if (decl_p)
+    base_addr = build1 (ADDR_EXPR,
+			build_pointer_type (TREE_TYPE (base)), base);
+  unsigned HOST_WIDE_INT size = compute_builtin_object_size (base_addr, 0);
   if (size != (unsigned HOST_WIDE_INT) -1)
     sizet = build_int_cst (sizetype, size);
   else if (optimize)
@@ -1479,7 +1489,7 @@ instrument_object_size (gimple_stmt_iter
       /* Generate __builtin_object_size call.  */
       sizet = builtin_decl_explicit (BUILT_IN_OBJECT_SIZE);
       
-      sizet = build_call_expr_loc (loc, sizet, 2, decl_p ? base_addr : base,
+      sizet = build_call_expr_loc (loc, sizet, 2, base_addr,
 				   integer_zero_node);
       sizet = force_gimple_operand_gsi (gsi, sizet, false, NULL_TREE, true,
 					GSI_SAME_STMT);
@@ -1492,8 +1502,7 @@ instrument_object_size (gimple_stmt_iter
   /* ptr + sizeof (*ptr) - base */
   t = fold_build2 (MINUS_EXPR, sizetype,
 		   fold_convert (pointer_sized_int_node, ptr),
-		   fold_convert (pointer_sized_int_node,
-				 decl_p ? base_addr : base));
+		   fold_convert (pointer_sized_int_node, base_addr));
   t = fold_build2 (PLUS_EXPR, sizetype, t, TYPE_SIZE_UNIT (type));
 
   /* Perhaps we can omit the check.  */


	Jakub

[-- Attachment #2: objsz.c --]
[-- Type: text/plain, Size: 937 bytes --]

struct T { char d[8]; int e; };
struct T t = { "abcdefg", 1 };
#ifdef __cplusplus
struct C { C () : d("abcdefg"), e(1) {} C (const C &x) { __builtin_memcpy (d, x.d, 8); e = x.e; } ~C () {} char d[8]; int e; };
#endif
struct U { int a : 5; int b : 19; int c : 8; };
struct S { struct U d[10]; };
struct S s;

int
f1 (int i)
{
  return t.d[i];
}

int
f2 (int i)
{
  char *p = t.d;
  p += i;
  return *p;
}

int
f3 (struct T x, int i)
{
  return x.d[i];
}

int
f4 (struct T x, int i)
{
  char *p = x.d;
  p += i;
  return *p;
}

#ifdef __cplusplus
struct C
f5 (int i)
{
  struct C x;
  x.d[i] = 'z';
  return x;
}

struct C
f6 (int i)
{
  struct C x;
  char *p = x.d;
  p += i;
  *p = 'z';
  return x;
}
#endif

int
f7 (int i)
{
  return s.d[i].b;
}

int
f8 (int i)
{
  struct U *u = s.d;
  u += i;
  return u->b;
}

int
f9 (void)
{
  char *p = t.d;
  p += 4;
  return *p;
}

int
f10 (int x)
{
  char *p = t.d + (x ? 6 : 3);
  return *p;
}

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Implement -fsanitize=object-size
  2014-10-02 12:04     ` Jakub Jelinek
@ 2014-10-10 10:26       ` Marek Polacek
  2014-10-10 10:27         ` Jakub Jelinek
  0 siblings, 1 reply; 9+ messages in thread
From: Marek Polacek @ 2014-10-10 10:26 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

On Thu, Oct 02, 2014 at 02:04:24PM +0200, Jakub Jelinek wrote:
> Looks much better.

Cool.

> There are some nits I'd change, like:
> 1) no need not to handle bitfields
> 2) IMHO it should handle PARM_DECL and RESULT_DECL alongside of VAR_DECL
> 3) decl_p IMHO should use just DECL_P
> 4) it doesn't make sense to build ADDR_EXPR if you are never going to use
>    it

All these look good - I applied the patch.
 
> Attaching some (incomplete) testcase I've used to look at the
> implementation, e.g. f1 is for testing how it plays together with
> -fsanitize=bounds (perhaps, maybe as a follow-up, we could optimize by
> looking for UBSAN_BOUNDS call with expected arguments in a few statements
> before array access (e.g. remember last UBSAN_BOUNDS seen in the same
> basic block) if inner is DECL_P), f2 to show a case which -fsanitize=bounds
> doesn't instrument, but -fsanitize=object-size should, f3/f4 the same with
> PARM_DECL, f5/f6 unsuccessful attempt for RESULT_DECL, f7/f8 bitfield
> tests, f9 something where __bos is folded very early (already before objsz1
> pass), f10 where __bos is folded during objsz1 pass.
> If you want to turn parts of that testcase into real /ubsan/ tests, go
> ahead.  Other than that, if you are fine with following changes, can you
> incorporate them into the patch and retest?

I created a new testcase based on that, thanks.

I couldn't test bootstrap-ubsan, because of error:
/home/polacek/x/trunk/prev-x86_64-unknown-linux-gnu/libsanitizer/ubsan/.libs/libubsan.a(ubsan_init.o):
.preinit_array section is not allowed in DSO
but I remember that the previous version of the patch passed fine.

I had to add the following

+      if (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION)
+	loc = input_location;

the reason is that on pr59250.C we ICEd, since we hit this assert in
tree-object-size.c:pass_object_sizes::execute;
    gcc_assert (TREE_CODE (result) == INTEGER_CST);
Because fold_call_stmt returned an INTEGER_CST wrapped in NOP_EXPR.
This NOP_EXPR is created in fold_builtin_n so we can a location on it,
and should be discarded in fold_call_stmt: 
            ret = fold_builtin_n (loc, fndecl, args, nargs, ignore);
          if (ret) 
            {
              /* Propagate location information from original call to
                 expansion of builtin.  Otherwise things like
                 maybe_emit_chk_warning, that operate on the expansion
                 of a builtin, will use the wrong location information.  */
              if (gimple_has_location (stmt))
                {
                  tree realret = ret;
                  if (TREE_CODE (ret) == NOP_EXPR)
                    realret = TREE_OPERAND (ret, 0);

However, STMT was without location.  We set the location properly in
instrument_object_size, but in this case the location was UNKNOWN_LOCATION.
So for UNKNOWN_LOCATION I use input_location.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2014-10-09  Marek Polacek  <polacek@redhat.com>

	* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE.
	* doc/invoke.texi: Document -fsanitize=object-size.
	* flag-types.h (enum sanitize_code): Add SANITIZE_OBJECT_SIZE and
	or it into SANITIZE_UNDEFINED.
	* gimple-fold.c (gimple_fold_call): Optimize IFN_UBSAN_OBJECT_SIZE.
	* internal-fn.c (expand_UBSAN_OBJECT_SIZE): New function.
	* internal-fn.def (UBSAN_OBJECT_SIZE): Define.
	* opts.c (common_handle_option): Handle -fsanitize=object-size.
	* ubsan.c: Include "tree-object-size.h".
	(ubsan_type_descriptor): Call tree_to_uhwi instead of tree_to_shwi. 
	(ubsan_expand_bounds_ifn): Use false instead of 0.
	(ubsan_expand_objsize_ifn): New function.
	(instrument_object_size): New function.
	(pass_ubsan::execute): Add object size instrumentation.
	* ubsan.h (ubsan_expand_objsize_ifn): Declare.
testsuite/
	* c-c++-common/ubsan/object-size-1.c: New test.
	* c-c++-common/ubsan/object-size-2.c: New test.
	* c-c++-common/ubsan/object-size-3.c: New test.
	* c-c++-common/ubsan/object-size-4.c: New test.
	* c-c++-common/ubsan/object-size-5.c: New test.
	* c-c++-common/ubsan/object-size-6.c: New test.
	* c-c++-common/ubsan/object-size-7.c: New test.
	* c-c++-common/ubsan/object-size-8.c: New test.
	* c-c++-common/ubsan/object-size-9.c: New test.
	* g++.dg/ubsan/object-size-1.C: New test.
	* gcc.dg/ubsan/object-size-9.c: New test.

diff --git gcc/asan.c gcc/asan.c
index fca4ee6..6ea3efe 100644
--- gcc/asan.c
+++ gcc/asan.c
@@ -2879,6 +2879,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_UBSAN_BOUNDS:
 		  no_next = ubsan_expand_bounds_ifn (&gsi);
 		  break;
+		case IFN_UBSAN_OBJECT_SIZE:
+		  no_next = ubsan_expand_objsize_ifn (&gsi);
+		  break;
 		case IFN_ASAN_CHECK:
 		  {
 		    no_next = asan_expand_check_ifn (&gsi, use_calls);
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index 8f3eb16..ba9f98b 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -5577,6 +5577,12 @@ 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=object-size
+@opindex fsanitize=object-size
+This option enables instrumentation of memory references using the
+@code{__builtin_object_size} function.  Various out of bounds pointer
+accesses are detected.
+
 @item -fsanitize=float-divide-by-zero
 @opindex fsanitize=float-divide-by-zero
 Detect floating-point division by zero.  Unlike other similar options,
diff --git gcc/flag-types.h gcc/flag-types.h
index d0818e5..3d01c49 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -236,12 +236,14 @@ enum sanitize_code {
   SANITIZE_ALIGNMENT = 1 << 17,
   SANITIZE_NONNULL_ATTRIBUTE = 1 << 18,
   SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1 << 19,
+  SANITIZE_OBJECT_SIZE = 1 << 20,
   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_NONNULL_ATTRIBUTE
-		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
+		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
+		       | SANITIZE_OBJECT_SIZE,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
diff --git gcc/gimple-fold.c gcc/gimple-fold.c
index 8ac2211..e8d101a 100644
--- gcc/gimple-fold.c
+++ gcc/gimple-fold.c
@@ -2662,6 +2662,19 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 					gimple_call_arg (stmt, 1),
 					gimple_call_arg (stmt, 2));
 	  break;
+        case IFN_UBSAN_OBJECT_SIZE:
+	  if (integer_all_onesp (gimple_call_arg (stmt, 2))
+	      || (TREE_CODE (gimple_call_arg (stmt, 1)) == INTEGER_CST
+		  && TREE_CODE (gimple_call_arg (stmt, 2)) == INTEGER_CST
+		  && tree_int_cst_le (gimple_call_arg (stmt, 1),
+				      gimple_call_arg (stmt, 2))))
+	    {
+	      gsi_replace (gsi, gimple_build_nop (), true);
+	      unlink_stmt_vdef (stmt);
+	      release_defs (stmt);
+	      return true;
+	    }
+	  break;
 	case IFN_UBSAN_CHECK_ADD:
 	  subcode = PLUS_EXPR;
 	  break;
diff --git gcc/internal-fn.c gcc/internal-fn.c
index c2a44b6..c71259d 100644
--- gcc/internal-fn.c
+++ gcc/internal-fn.c
@@ -184,6 +184,14 @@ expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED)
 /* This should get expanded in the sanopt pass.  */
 
 static void
+expand_UBSAN_OBJECT_SIZE (gimple stmt ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
 expand_ASAN_CHECK (gimple stmt ATTRIBUTE_UNUSED)
 {
   gcc_unreachable ();
diff --git gcc/internal-fn.def gcc/internal-fn.def
index 54ade9f..b8e457c 100644
--- gcc/internal-fn.def
+++ gcc/internal-fn.def
@@ -53,6 +53,7 @@ DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW, NULL)
 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_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)
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".W...")
diff --git gcc/opts.c gcc/opts.c
index 5cb5a39..3d9b6a7 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1513,6 +1513,8 @@ common_handle_option (struct gcc_options *opts,
 	      { "returns-nonnull-attribute",
 		SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
 		sizeof "returns-nonnull-attribute" - 1 },
+	      { "object-size", SANITIZE_OBJECT_SIZE,
+		sizeof "object-size" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-1.c gcc/testsuite/c-c++-common/ubsan/object-size-1.c
index e69de29..7a3c87a 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-1.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-1.c
@@ -0,0 +1,125 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+/* Sanity-test -fsanitize=object-size.  We use -fsanitize=undefined option
+   to check that this feature doesn't clash with -fsanitize=bounds et al.  */
+
+#define N 20
+
+__attribute__((noinline, noclone)) void
+f1 (int i)
+{
+  volatile int j;
+  char *p, *orig;
+  orig = p = (char *) __builtin_calloc (N, 1);
+  j = *(p + i);
+  j = p[i];
+  p++;
+  j = p[i - 1];
+  j = *(p + i - 1);
+  __builtin_free (orig);
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f2 (int i)
+{
+  volatile int j;
+  char a[N];
+  __builtin_memset (a, 0, N);
+  j = *(a + i);
+  char *p = a;
+  j = *(p + i);
+  j = p[i];
+  p += 10;
+  j = *(p + i - 10);
+  j = p[i - 10];
+}
+
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f3 (int i)
+{
+  volatile int j;
+  int *p = (int *) __builtin_calloc (N, sizeof (*p));
+  int *o = &p[i];
+  j = *o;
+  j = o[0];
+  __builtin_free (p);
+}
+
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f4 (void)
+{
+  /* The second argument to __builtin_calloc is intentional.  */
+  int *p = (int *) __builtin_calloc (3, 1);
+  *p = 42;
+  __builtin_free (p);
+}
+
+/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f5 (int *p)
+{
+  /* This is not instrumented.  But don't ICE, etc.  */
+  volatile int i = p[N];
+}
+
+int
+main ()
+{
+  f1 (N);
+  f2 (N);
+  f3 (N);
+  f4 ();
+  int *p = (int *) __builtin_calloc (N, sizeof (*p));
+  f5 (p);
+  __builtin_free (p);
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-2.c gcc/testsuite/c-c++-common/ubsan/object-size-2.c
index e69de29..dba1243 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-2.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-2.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+void
+foo (unsigned long ul)
+{
+  unsigned int u;
+  u = *(unsigned long *) ul;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-3.c gcc/testsuite/c-c++-common/ubsan/object-size-3.c
index e69de29..62dc76f 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-3.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-3.c
@@ -0,0 +1,56 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size -fno-sanitize-recover" } */
+
+/* Test valid uses.  */
+
+#define N 20
+
+__attribute__((noinline, noclone)) void
+f1 (int i)
+{
+  volatile int j;
+  char *p, *orig;
+  orig = p = (char *) __builtin_calloc (N, 1);
+  j = *(p + i);
+  j = p[i];
+  p++;
+  j = p[i - 1];
+  j = *(p + i - 1);
+  __builtin_free (orig);
+}
+
+__attribute__((noinline, noclone)) void
+f2 (int i)
+{
+  volatile int j;
+  char a[N];
+  __builtin_memset (a, 0, N);
+  j = *(a + i);
+  char *p = a;
+  j = *(p + i);
+  j = p[i];
+  p += 10;
+  j = *(p + i - 10);
+  j = p[i - 10];
+}
+
+__attribute__((noinline, noclone)) void
+f3 (int i)
+{
+  volatile int j;
+  int *p = (int *) __builtin_calloc (N, sizeof (*p));
+  int *o = &p[i];
+  j = *o;
+  j = o[0];
+  __builtin_free (p);
+}
+
+int
+main ()
+{
+  f1 (N - 1);
+  f2 (N - 1);
+  f3 (N - 1);
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-4.c gcc/testsuite/c-c++-common/ubsan/object-size-4.c
index e69de29..8b95ec9 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-4.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-4.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+/* Test that we instrument flexible array members.  */
+
+struct T { int l; int a[]; };
+struct U { int l; int a[0]; };
+
+int
+main (void)
+{
+  volatile int i;
+  struct T *t = (struct T *) __builtin_calloc (sizeof (struct T)
+					       + sizeof (int), 1);
+  i = t->a[1];
+
+  struct U *u = (struct U *) __builtin_calloc (sizeof (struct U)
+					       + sizeof (int), 1);
+  i = u->a[1];
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-5.c gcc/testsuite/c-c++-common/ubsan/object-size-5.c
index e69de29..3dada10 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-5.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-5.c
@@ -0,0 +1,38 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+/* Test structures with -fsanitize=object-size.  */
+
+#define N 20
+
+struct S { char *p; int i; };
+struct T { struct S *s; };
+
+__attribute__((noinline, noclone)) void
+f1 (int i)
+{
+  volatile int j;
+  struct S s;
+  s.p = (char *) __builtin_calloc (N, 1);
+  j = s.p[i];
+  j = *(s.p + i);
+  __builtin_free (s.p);
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+int
+main ()
+{
+  f1 (N);
+  f1 (N - 1);
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-6.c gcc/testsuite/c-c++-common/ubsan/object-size-6.c
index e69de29..0e6035d 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-6.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-6.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+char
+foo (void *v)
+{
+  return *(char *) v;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-7.c gcc/testsuite/c-c++-common/ubsan/object-size-7.c
index e69de29..f5b26e5 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-7.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-7.c
@@ -0,0 +1,29 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+#define N 20
+
+struct S { int a; };
+
+__attribute__((noinline, noclone)) struct S
+f1 (int i)
+{
+  struct S a[N];
+  struct S *p = a;
+  struct S s;
+  s = p[i];
+  return s;
+}
+
+int
+main ()
+{
+  f1 (N);
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-8.c gcc/testsuite/c-c++-common/ubsan/object-size-8.c
index e69de29..ee0945b 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-8.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-8.c
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+struct S { int a; int b; };
+
+static inline __attribute__((always_inline)) int
+foo (struct S *p)
+{
+  volatile int a;
+  a = p->a; /* OK */
+  return p->b;
+}
+
+int
+bar (void)
+{
+  struct S *p = (struct S *) __builtin_calloc (sizeof (int) + sizeof (int) / 2, 1);
+  return foo (p);
+}
+
+int
+main (void)
+{
+  bar ();
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-9.c gcc/testsuite/c-c++-common/ubsan/object-size-9.c
index e69de29..829c822 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-9.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-9.c
@@ -0,0 +1,97 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+/* Test PARM_DECLs and RESULT_DECLs.  */
+
+struct T { char d[8]; int e; };
+struct T t = { "abcdefg", 1 };
+#ifdef __cplusplus
+struct C { C () : d("abcdefg"), e(1) {} C (const C &x) { __builtin_memcpy (d, x.d, 8); e = x.e; } ~C () {} char d[8]; int e; };
+#endif
+struct U { int a : 5; int b : 19; int c : 8; };
+struct S { struct U d[10]; };
+struct S s;
+
+int
+f1 (struct T x, int i)
+{
+  char *p = x.d;
+  p += i;
+  return *p;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+#ifdef __cplusplus
+struct C
+f2 (int i)
+{
+  struct C x;
+  x.d[i] = 'z';
+  return x;
+}
+
+/* { dg-output "\[^\n\r]*index 12 out of bounds for type 'char \\\[8\\\]'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+
+struct C
+f3 (int i)
+{
+  struct C x;
+  char *p = x.d;
+  p += i;
+  *p = 'z';
+  return x;
+}
+
+/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+
+#endif
+
+int
+f4 (int i)
+{
+  return s.d[i].b;
+}
+
+/* { dg-output "\[^\n\r]*index 12 out of bounds for type 'U \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+int
+f5 (int i)
+{
+  struct U *u = s.d;
+  u += i;
+  return u->b;
+}
+
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+int
+main (void)
+{
+  f1 (t, 12);
+#ifdef __cplusplus
+  f2 (12);
+  f3 (12);
+#endif
+  f4 (12);
+  f5 (12);
+  return 0;
+}
diff --git gcc/testsuite/g++.dg/ubsan/object-size-1.C gcc/testsuite/g++.dg/ubsan/object-size-1.C
index e69de29..e2aad46 100644
--- gcc/testsuite/g++.dg/ubsan/object-size-1.C
+++ gcc/testsuite/g++.dg/ubsan/object-size-1.C
@@ -0,0 +1,18 @@
+// { dg-do compile }
+// { dg-options "-fsanitize=undefined -fpermissive" }
+
+struct T { int c; char d[]; };
+
+struct T t = { 1, "a" }; // { dg-warning "initializer-string for array of chars is too long" }
+
+int
+baz (int i)
+{
+  return t.d[i];
+}
+
+int
+main (void)
+{
+  baz (3);
+}
diff --git gcc/testsuite/gcc.dg/ubsan/object-size-9.c gcc/testsuite/gcc.dg/ubsan/object-size-9.c
index e69de29..bb9aa1b 100644
--- gcc/testsuite/gcc.dg/ubsan/object-size-9.c
+++ gcc/testsuite/gcc.dg/ubsan/object-size-9.c
@@ -0,0 +1,24 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+struct T { int c; char d[]; };
+struct T t = { 1, "a" };
+
+int
+baz (int i)
+{
+  return t.d[i];
+}
+
+int
+main (void)
+{
+  baz (2);
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/ubsan.c gcc/ubsan.c
index c9a72ad..61140d2 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "realmpfr.h"
 #include "dfp.h"
 #include "builtins.h"
+#include "tree-object-size.h"
 
 /* Map from a tree to a VAR_DECL tree.  */
 
@@ -391,7 +392,7 @@ ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 	  tree dom = TYPE_DOMAIN (t);
 	  if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST)
 	    pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC,
-			    tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1);
+			    tree_to_uhwi (TYPE_MAX_VALUE (dom)) + 1);
 	  else
 	    /* ??? We can't determine the variable name; print VLA unspec.  */
 	    pretty_name[pos++] = '*';
@@ -614,12 +615,12 @@ ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
   /* Create condition "if (index > bound)".  */
   basic_block then_bb, fallthru_bb;
   gimple_stmt_iterator cond_insert_point
-    = create_cond_insert_point (gsi, 0/*before_p*/, false, true,
+    = create_cond_insert_point (gsi, false, false, true,
 				&then_bb, &fallthru_bb);
   index = fold_convert (TREE_TYPE (bound), index);
   index = force_gimple_operand_gsi (&cond_insert_point, index,
-				    true/*simple_p*/, NULL_TREE,
-				    false/*before*/, GSI_NEW_STMT);
+				    true, NULL_TREE,
+				    false, GSI_NEW_STMT);
   gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE);
   gimple_set_location (g, loc);
   gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
@@ -830,6 +831,76 @@ ubsan_expand_null_ifn (gimple_stmt_iterator *gsip)
   return false;
 }
 
+/* Expand UBSAN_OBJECT_SIZE internal call.  */
+
+bool
+ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  gcc_assert (gimple_call_num_args (stmt) == 4);
+
+  tree ptr = gimple_call_arg (stmt, 0);
+  tree offset = gimple_call_arg (stmt, 1);
+  tree size = gimple_call_arg (stmt, 2);
+  tree ckind = gimple_call_arg (stmt, 3);
+  gimple_stmt_iterator gsi_orig = *gsi;
+  gimple g;
+
+  gcc_assert (TREE_CODE (size) == INTEGER_CST);
+  /* See if we can discard the check.  */
+  if (integer_all_onesp (size))
+    /* Yes, __builtin_object_size couldn't determine the
+       object size.  */;
+  else
+    {
+      /* if (offset > objsize) */
+      basic_block then_bb, fallthru_bb;
+      gimple_stmt_iterator cond_insert_point
+	= create_cond_insert_point (gsi, false, false, true,
+				    &then_bb, &fallthru_bb);
+      g = gimple_build_cond (GT_EXPR, offset, size, NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+      gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+
+      /* Generate __ubsan_handle_type_mismatch call.  */
+      *gsi = gsi_after_labels (then_bb);
+      if (flag_sanitize_undefined_trap_on_error)
+	g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+      else
+	{
+	  tree data
+	    = ubsan_create_data ("__ubsan_objsz_data", 1, &loc,
+				 ubsan_type_descriptor (TREE_TYPE (ptr),
+							UBSAN_PRINT_POINTER),
+				 NULL_TREE,
+				 build_zero_cst (pointer_sized_int_node),
+				 ckind,
+				 NULL_TREE);
+	  data = build_fold_addr_expr_loc (loc, data);
+	  enum built_in_function bcode
+	    = flag_sanitize_recover
+	      ? BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH
+	      : BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT;
+	  tree p = make_ssa_name (pointer_sized_int_node, NULL);
+	  g = gimple_build_assign_with_ops (NOP_EXPR, p, ptr, NULL_TREE);
+	  gimple_set_location (g, loc);
+	  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+	  g = gimple_build_call (builtin_decl_explicit (bcode), 2, data, p);
+	}
+      gimple_set_location (g, loc);
+      gsi_insert_before (gsi, g, GSI_SAME_STMT);
+
+      /* Point GSI to next logical statement.  */
+      *gsi = gsi_start_bb (fallthru_bb);
+    }
+
+  /* Get rid of the UBSAN_OBJECT_SIZE call from the IR.  */
+  unlink_stmt_vdef (stmt);
+  gsi_remove (&gsi_orig, true);
+  return gsi_end_p (*gsi);
+}
+
 /* 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.  */
 
@@ -1339,6 +1410,128 @@ instrument_nonnull_return (gimple_stmt_iterator *gsi)
   flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
 }
 
+/* Instrument memory references.  Here we check whether the pointer
+   points to an out-of-bounds location.  */
+
+static void
+instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt);
+  tree type;
+  HOST_WIDE_INT size_in_bytes;
+
+  type = TREE_TYPE (t);
+  if (VOID_TYPE_P (type))
+    return;
+
+  switch (TREE_CODE (t))
+    {
+    case COMPONENT_REF:
+      if (TREE_CODE (t) == COMPONENT_REF
+	  && DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1)) != NULL_TREE)
+	{
+	  tree repr = DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1));
+	  t = build3 (COMPONENT_REF, TREE_TYPE (repr), TREE_OPERAND (t, 0),
+		      repr, NULL_TREE);
+	}
+      break;
+    case ARRAY_REF:
+    case INDIRECT_REF:
+    case MEM_REF:
+    case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+      break;
+    default:
+      return;
+    }
+
+  size_in_bytes = int_size_in_bytes (type);
+  if (size_in_bytes <= 0)
+    return;
+
+  HOST_WIDE_INT bitsize, bitpos;
+  tree offset;
+  enum machine_mode mode;
+  int volatilep = 0, unsignedp = 0;
+  tree inner = get_inner_reference (t, &bitsize, &bitpos, &offset, &mode,
+				    &unsignedp, &volatilep, false);
+
+  if (bitpos % BITS_PER_UNIT != 0
+      || bitsize != size_in_bytes * BITS_PER_UNIT)
+    return;
+
+  bool decl_p = DECL_P (inner);
+  tree base = decl_p ? inner : TREE_OPERAND (inner, 0);
+  tree ptr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (t)), t);
+
+  while (TREE_CODE (base) == SSA_NAME)
+    {
+      gimple def_stmt = SSA_NAME_DEF_STMT (base);
+      if (gimple_assign_ssa_name_copy_p (def_stmt)
+	  || (gimple_assign_cast_p (def_stmt)
+	      && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (def_stmt))))
+	  || (is_gimple_assign (def_stmt)
+	      && gimple_assign_rhs_code (def_stmt) == POINTER_PLUS_EXPR))
+	base = gimple_assign_rhs1 (def_stmt);
+      else
+	break;
+    }
+
+  if (!POINTER_TYPE_P (TREE_TYPE (base)) && !DECL_P (base))
+    return;
+
+  tree sizet;
+  tree base_addr = base;
+  if (decl_p)
+    base_addr = build1 (ADDR_EXPR,
+			build_pointer_type (TREE_TYPE (base)), base);
+  unsigned HOST_WIDE_INT size = compute_builtin_object_size (base_addr, 0);
+  if (size != (unsigned HOST_WIDE_INT) -1)
+    sizet = build_int_cst (sizetype, size);
+  else if (optimize)
+    {
+      if (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION)
+	loc = input_location;
+      /* Generate __builtin_object_size call.  */
+      sizet = builtin_decl_explicit (BUILT_IN_OBJECT_SIZE);
+      sizet = build_call_expr_loc (loc, sizet, 2, base_addr,
+				   integer_zero_node);
+      sizet = force_gimple_operand_gsi (gsi, sizet, false, NULL_TREE, true,
+					GSI_SAME_STMT);
+    }
+  else
+    return;
+
+  /* Generate UBSAN_OBJECT_SIZE (ptr, ptr+sizeof(*ptr)-base, objsize, ckind)
+     call.  */
+  /* ptr + sizeof (*ptr) - base */
+  t = fold_build2 (MINUS_EXPR, sizetype,
+		   fold_convert (pointer_sized_int_node, ptr),
+		   fold_convert (pointer_sized_int_node, base_addr));
+  t = fold_build2 (PLUS_EXPR, sizetype, t, TYPE_SIZE_UNIT (type));
+
+  /* Perhaps we can omit the check.  */
+  if (TREE_CODE (t) == INTEGER_CST
+      && TREE_CODE (sizet) == INTEGER_CST
+      && tree_int_cst_le (t, sizet))
+    return;
+
+  /* Nope.  Emit the check.  */
+  t = force_gimple_operand_gsi (gsi, t, true, NULL_TREE, true,
+				GSI_SAME_STMT);
+  ptr = force_gimple_operand_gsi (gsi, ptr, true, NULL_TREE, true,
+				  GSI_SAME_STMT);
+  tree ckind = build_int_cst (unsigned_char_type_node,
+			      is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF);
+  gimple g = gimple_build_call_internal (IFN_UBSAN_OBJECT_SIZE, 4,
+					 ptr, t, sizet, ckind);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+}
+
 namespace {
 
 const pass_data pass_data_ubsan =
@@ -1368,7 +1561,8 @@ public:
 			      | SANITIZE_BOOL | SANITIZE_ENUM
 			      | SANITIZE_ALIGNMENT
 			      | SANITIZE_NONNULL_ATTRIBUTE
-			      | SANITIZE_RETURNS_NONNULL_ATTRIBUTE)
+			      | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
+			      | SANITIZE_OBJECT_SIZE)
 	     && current_function_decl != NULL_TREE
 	     && !lookup_attribute ("no_sanitize_undefined",
 				   DECL_ATTRIBUTES (current_function_decl));
@@ -1431,6 +1625,14 @@ pass_ubsan::execute (function *fun)
 	      bb = gimple_bb (stmt);
 	    }
 
+	  if (flag_sanitize & SANITIZE_OBJECT_SIZE)
+	    {
+	      if (gimple_store_p (stmt))
+		instrument_object_size (&gsi, true);
+	      if (gimple_assign_load_p (stmt))
+		instrument_object_size (&gsi, false);
+	    }
+
 	  gsi_next (&gsi);
 	}
     }
diff --git gcc/ubsan.h gcc/ubsan.h
index d0b404f..27c18eb 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -40,6 +40,7 @@ enum ubsan_print_style {
 
 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 tree ubsan_instrument_unreachable (location_t);
 extern tree ubsan_create_data (const char *, int, const location_t *, ...);
 extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);

	Marek

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Implement -fsanitize=object-size
  2014-10-10 10:26       ` Marek Polacek
@ 2014-10-10 10:27         ` Jakub Jelinek
  2014-10-10 16:37           ` Marek Polacek
  2014-10-14 16:01           ` Jakub Jelinek
  0 siblings, 2 replies; 9+ messages in thread
From: Jakub Jelinek @ 2014-10-10 10:27 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches

On Fri, Oct 10, 2014 at 12:04:08PM +0200, Marek Polacek wrote:
> I couldn't test bootstrap-ubsan, because of error:
> /home/polacek/x/trunk/prev-x86_64-unknown-linux-gnu/libsanitizer/ubsan/.libs/libubsan.a(ubsan_init.o):
> .preinit_array section is not allowed in DSO
> but I remember that the previous version of the patch passed fine.

We build (intentionally) both libubsan.so.* objects and libubsan.a
objects with -fPIC, but don't build the latter with -DPIC.  I guess
we need now, with -static-libubsan libubsan.a is linked into shared
libraries statically and we definitely can't use .preinit_array
in that case.

So, I think (untested) something like:

2014-10-10  Jakub Jelinek  <jakub@redhat.com>

	* ubsan/Makefile.am (DEFS): Add -DPIC.
	* ubsan/Makefile.in: Regenerated.

--- libsanitizer/ubsan/Makefile.am	2014-09-24 11:08:04.183026156 +0200
+++ libsanitizer/ubsan/Makefile.am	2014-10-10 12:15:19.124247283 +0200
@@ -3,7 +3,7 @@ AM_CPPFLAGS = -I $(top_srcdir) -I $(top_
 # May be used by toolexeclibdir.
 gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
 
-DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS 
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC
 AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
 AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
 ACLOCAL_AMFLAGS = -I m4

should fix this.

> 2014-10-09  Marek Polacek  <polacek@redhat.com>

Check the date ;)

> 	* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE.
> 	* doc/invoke.texi: Document -fsanitize=object-size.
> 	* flag-types.h (enum sanitize_code): Add SANITIZE_OBJECT_SIZE and
> 	or it into SANITIZE_UNDEFINED.
> 	* gimple-fold.c (gimple_fold_call): Optimize IFN_UBSAN_OBJECT_SIZE.
> 	* internal-fn.c (expand_UBSAN_OBJECT_SIZE): New function.
> 	* internal-fn.def (UBSAN_OBJECT_SIZE): Define.
> 	* opts.c (common_handle_option): Handle -fsanitize=object-size.
> 	* ubsan.c: Include "tree-object-size.h".

I'd avoid the ""s.

> --- gcc/gimple-fold.c
> +++ gcc/gimple-fold.c
> @@ -2662,6 +2662,19 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
>  					gimple_call_arg (stmt, 1),
>  					gimple_call_arg (stmt, 2));
>  	  break;
> +        case IFN_UBSAN_OBJECT_SIZE:
> +	  if (integer_all_onesp (gimple_call_arg (stmt, 2))

Formatting on the case line, there should be tab.

> +
> +  gcc_assert (TREE_CODE (size) == INTEGER_CST);
> +  /* See if we can discard the check.  */
> +  if (integer_all_onesp (size))
> +    /* Yes, __builtin_object_size couldn't determine the
> +       object size.  */;

I'd just treat TREE_CODE (size) != INTEGER_CST
the same as integer_all_onesp.  It is very likely you'll get
an INTEGER_CST there, but I'd be afraid if somebody disables ccp, forwprop
and similar optimizations that if you are unlucky you might actually have
an SSA_NAME there instead.

Ok with those changes.

After commit, please update gcc-5/changes.html.  Thanks.

	Jakub

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Implement -fsanitize=object-size
  2014-10-10 10:27         ` Jakub Jelinek
@ 2014-10-10 16:37           ` Marek Polacek
  2014-10-14 16:01           ` Jakub Jelinek
  1 sibling, 0 replies; 9+ messages in thread
From: Marek Polacek @ 2014-10-10 16:37 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC Patches

On Fri, Oct 10, 2014 at 12:26:44PM +0200, Jakub Jelinek wrote:
> On Fri, Oct 10, 2014 at 12:04:08PM +0200, Marek Polacek wrote:
> > I couldn't test bootstrap-ubsan, because of error:
> > /home/polacek/x/trunk/prev-x86_64-unknown-linux-gnu/libsanitizer/ubsan/.libs/libubsan.a(ubsan_init.o):
> > .preinit_array section is not allowed in DSO
> > but I remember that the previous version of the patch passed fine.
> 
> We build (intentionally) both libubsan.so.* objects and libubsan.a
> objects with -fPIC, but don't build the latter with -DPIC.  I guess
> we need now, with -static-libubsan libubsan.a is linked into shared
> libraries statically and we definitely can't use .preinit_array
> in that case.
> 
> So, I think (untested) something like:
> 
> 2014-10-10  Jakub Jelinek  <jakub@redhat.com>
> 
> 	* ubsan/Makefile.am (DEFS): Add -DPIC.
> 	* ubsan/Makefile.in: Regenerated.
> 
> --- libsanitizer/ubsan/Makefile.am	2014-09-24 11:08:04.183026156 +0200
> +++ libsanitizer/ubsan/Makefile.am	2014-10-10 12:15:19.124247283 +0200
> @@ -3,7 +3,7 @@ AM_CPPFLAGS = -I $(top_srcdir) -I $(top_
>  # May be used by toolexeclibdir.
>  gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
>  
> -DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS 
> +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC
>  AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
>  AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
>  ACLOCAL_AMFLAGS = -I m4
> 
> should fix this.
> 
> > 2014-10-09  Marek Polacek  <polacek@redhat.com>
> 
> Check the date ;)

Adjusted.
 
> > 	* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE.
> > 	* doc/invoke.texi: Document -fsanitize=object-size.
> > 	* flag-types.h (enum sanitize_code): Add SANITIZE_OBJECT_SIZE and
> > 	or it into SANITIZE_UNDEFINED.
> > 	* gimple-fold.c (gimple_fold_call): Optimize IFN_UBSAN_OBJECT_SIZE.
> > 	* internal-fn.c (expand_UBSAN_OBJECT_SIZE): New function.
> > 	* internal-fn.def (UBSAN_OBJECT_SIZE): Define.
> > 	* opts.c (common_handle_option): Handle -fsanitize=object-size.
> > 	* ubsan.c: Include "tree-object-size.h".
> 
> I'd avoid the ""s.

Adjusted.
 
> > --- gcc/gimple-fold.c
> > +++ gcc/gimple-fold.c
> > @@ -2662,6 +2662,19 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
> >  					gimple_call_arg (stmt, 1),
> >  					gimple_call_arg (stmt, 2));
> >  	  break;
> > +        case IFN_UBSAN_OBJECT_SIZE:
> > +	  if (integer_all_onesp (gimple_call_arg (stmt, 2))
> 
> Formatting on the case line, there should be tab.

Fixed.
 
> > +
> > +  gcc_assert (TREE_CODE (size) == INTEGER_CST);
> > +  /* See if we can discard the check.  */
> > +  if (integer_all_onesp (size))
> > +    /* Yes, __builtin_object_size couldn't determine the
> > +       object size.  */;
> 
> I'd just treat TREE_CODE (size) != INTEGER_CST
> the same as integer_all_onesp.  It is very likely you'll get
> an INTEGER_CST there, but I'd be afraid if somebody disables ccp, forwprop
> and similar optimizations that if you are unlucky you might actually have
> an SSA_NAME there instead.

Fixed.
 
> Ok with those changes.

Thanks for reviewing!
 
> After commit, please update gcc-5/changes.html.  Thanks.

Sure.

I'm applying the following.

2014-10-10  Marek Polacek  <polacek@redhat.com>

	* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE.
	* doc/invoke.texi: Document -fsanitize=object-size.
	* flag-types.h (enum sanitize_code): Add SANITIZE_OBJECT_SIZE and
	or it into SANITIZE_UNDEFINED.
	* gimple-fold.c (gimple_fold_call): Optimize IFN_UBSAN_OBJECT_SIZE.
	* internal-fn.c (expand_UBSAN_OBJECT_SIZE): New function.
	* internal-fn.def (UBSAN_OBJECT_SIZE): Define.
	* opts.c (common_handle_option): Handle -fsanitize=object-size.
	* ubsan.c: Include tree-object-size.h.
	(ubsan_type_descriptor): Call tree_to_uhwi instead of tree_to_shwi. 
	(ubsan_expand_bounds_ifn): Use false instead of 0.
	(ubsan_expand_objsize_ifn): New function.
	(instrument_object_size): New function.
	(pass_ubsan::execute): Add object size instrumentation.
	* ubsan.h (ubsan_expand_objsize_ifn): Declare.
testsuite/
	* c-c++-common/ubsan/object-size-1.c: New test.
	* c-c++-common/ubsan/object-size-2.c: New test.
	* c-c++-common/ubsan/object-size-3.c: New test.
	* c-c++-common/ubsan/object-size-4.c: New test.
	* c-c++-common/ubsan/object-size-5.c: New test.
	* c-c++-common/ubsan/object-size-6.c: New test.
	* c-c++-common/ubsan/object-size-7.c: New test.
	* c-c++-common/ubsan/object-size-8.c: New test.
	* c-c++-common/ubsan/object-size-9.c: New test.
	* g++.dg/ubsan/object-size-1.C: New test.
	* gcc.dg/ubsan/object-size-9.c: New test.

diff --git gcc/asan.c gcc/asan.c
index fca4ee6..6ea3efe 100644
--- gcc/asan.c
+++ gcc/asan.c
@@ -2879,6 +2879,9 @@ pass_sanopt::execute (function *fun)
 		case IFN_UBSAN_BOUNDS:
 		  no_next = ubsan_expand_bounds_ifn (&gsi);
 		  break;
+		case IFN_UBSAN_OBJECT_SIZE:
+		  no_next = ubsan_expand_objsize_ifn (&gsi);
+		  break;
 		case IFN_ASAN_CHECK:
 		  {
 		    no_next = asan_expand_check_ifn (&gsi, use_calls);
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index f1ba77b..2b62a73 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -5578,6 +5578,12 @@ 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=object-size
+@opindex fsanitize=object-size
+This option enables instrumentation of memory references using the
+@code{__builtin_object_size} function.  Various out of bounds pointer
+accesses are detected.
+
 @item -fsanitize=float-divide-by-zero
 @opindex fsanitize=float-divide-by-zero
 Detect floating-point division by zero.  Unlike other similar options,
diff --git gcc/flag-types.h gcc/flag-types.h
index d0818e5..3d01c49 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -236,12 +236,14 @@ enum sanitize_code {
   SANITIZE_ALIGNMENT = 1 << 17,
   SANITIZE_NONNULL_ATTRIBUTE = 1 << 18,
   SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1 << 19,
+  SANITIZE_OBJECT_SIZE = 1 << 20,
   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_NONNULL_ATTRIBUTE
-		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
+		       | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
+		       | SANITIZE_OBJECT_SIZE,
   SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
diff --git gcc/gimple-fold.c gcc/gimple-fold.c
index 8ac2211..76441c7 100644
--- gcc/gimple-fold.c
+++ gcc/gimple-fold.c
@@ -2662,6 +2662,19 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
 					gimple_call_arg (stmt, 1),
 					gimple_call_arg (stmt, 2));
 	  break;
+	case IFN_UBSAN_OBJECT_SIZE:
+	  if (integer_all_onesp (gimple_call_arg (stmt, 2))
+	      || (TREE_CODE (gimple_call_arg (stmt, 1)) == INTEGER_CST
+		  && TREE_CODE (gimple_call_arg (stmt, 2)) == INTEGER_CST
+		  && tree_int_cst_le (gimple_call_arg (stmt, 1),
+				      gimple_call_arg (stmt, 2))))
+	    {
+	      gsi_replace (gsi, gimple_build_nop (), true);
+	      unlink_stmt_vdef (stmt);
+	      release_defs (stmt);
+	      return true;
+	    }
+	  break;
 	case IFN_UBSAN_CHECK_ADD:
 	  subcode = PLUS_EXPR;
 	  break;
diff --git gcc/internal-fn.c gcc/internal-fn.c
index c2a44b6..c71259d 100644
--- gcc/internal-fn.c
+++ gcc/internal-fn.c
@@ -184,6 +184,14 @@ expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED)
 /* This should get expanded in the sanopt pass.  */
 
 static void
+expand_UBSAN_OBJECT_SIZE (gimple stmt ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
+/* This should get expanded in the sanopt pass.  */
+
+static void
 expand_ASAN_CHECK (gimple stmt ATTRIBUTE_UNUSED)
 {
   gcc_unreachable ();
diff --git gcc/internal-fn.def gcc/internal-fn.def
index 54ade9f..b8e457c 100644
--- gcc/internal-fn.def
+++ gcc/internal-fn.def
@@ -53,6 +53,7 @@ DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW, NULL)
 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_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)
 DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".W...")
diff --git gcc/opts.c gcc/opts.c
index 5cb5a39..3d9b6a7 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1513,6 +1513,8 @@ common_handle_option (struct gcc_options *opts,
 	      { "returns-nonnull-attribute",
 		SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
 		sizeof "returns-nonnull-attribute" - 1 },
+	      { "object-size", SANITIZE_OBJECT_SIZE,
+		sizeof "object-size" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-1.c gcc/testsuite/c-c++-common/ubsan/object-size-1.c
index e69de29..7a3c87a 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-1.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-1.c
@@ -0,0 +1,125 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+/* Sanity-test -fsanitize=object-size.  We use -fsanitize=undefined option
+   to check that this feature doesn't clash with -fsanitize=bounds et al.  */
+
+#define N 20
+
+__attribute__((noinline, noclone)) void
+f1 (int i)
+{
+  volatile int j;
+  char *p, *orig;
+  orig = p = (char *) __builtin_calloc (N, 1);
+  j = *(p + i);
+  j = p[i];
+  p++;
+  j = p[i - 1];
+  j = *(p + i - 1);
+  __builtin_free (orig);
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f2 (int i)
+{
+  volatile int j;
+  char a[N];
+  __builtin_memset (a, 0, N);
+  j = *(a + i);
+  char *p = a;
+  j = *(p + i);
+  j = p[i];
+  p += 10;
+  j = *(p + i - 10);
+  j = p[i - 10];
+}
+
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f3 (int i)
+{
+  volatile int j;
+  int *p = (int *) __builtin_calloc (N, sizeof (*p));
+  int *o = &p[i];
+  j = *o;
+  j = o[0];
+  __builtin_free (p);
+}
+
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f4 (void)
+{
+  /* The second argument to __builtin_calloc is intentional.  */
+  int *p = (int *) __builtin_calloc (3, 1);
+  *p = 42;
+  __builtin_free (p);
+}
+
+/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+__attribute__((noinline, noclone)) void
+f5 (int *p)
+{
+  /* This is not instrumented.  But don't ICE, etc.  */
+  volatile int i = p[N];
+}
+
+int
+main ()
+{
+  f1 (N);
+  f2 (N);
+  f3 (N);
+  f4 ();
+  int *p = (int *) __builtin_calloc (N, sizeof (*p));
+  f5 (p);
+  __builtin_free (p);
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-2.c gcc/testsuite/c-c++-common/ubsan/object-size-2.c
index e69de29..dba1243 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-2.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-2.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+void
+foo (unsigned long ul)
+{
+  unsigned int u;
+  u = *(unsigned long *) ul;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-3.c gcc/testsuite/c-c++-common/ubsan/object-size-3.c
index e69de29..62dc76f 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-3.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-3.c
@@ -0,0 +1,56 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size -fno-sanitize-recover" } */
+
+/* Test valid uses.  */
+
+#define N 20
+
+__attribute__((noinline, noclone)) void
+f1 (int i)
+{
+  volatile int j;
+  char *p, *orig;
+  orig = p = (char *) __builtin_calloc (N, 1);
+  j = *(p + i);
+  j = p[i];
+  p++;
+  j = p[i - 1];
+  j = *(p + i - 1);
+  __builtin_free (orig);
+}
+
+__attribute__((noinline, noclone)) void
+f2 (int i)
+{
+  volatile int j;
+  char a[N];
+  __builtin_memset (a, 0, N);
+  j = *(a + i);
+  char *p = a;
+  j = *(p + i);
+  j = p[i];
+  p += 10;
+  j = *(p + i - 10);
+  j = p[i - 10];
+}
+
+__attribute__((noinline, noclone)) void
+f3 (int i)
+{
+  volatile int j;
+  int *p = (int *) __builtin_calloc (N, sizeof (*p));
+  int *o = &p[i];
+  j = *o;
+  j = o[0];
+  __builtin_free (p);
+}
+
+int
+main ()
+{
+  f1 (N - 1);
+  f2 (N - 1);
+  f3 (N - 1);
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-4.c gcc/testsuite/c-c++-common/ubsan/object-size-4.c
index e69de29..8b95ec9 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-4.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-4.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+/* Test that we instrument flexible array members.  */
+
+struct T { int l; int a[]; };
+struct U { int l; int a[0]; };
+
+int
+main (void)
+{
+  volatile int i;
+  struct T *t = (struct T *) __builtin_calloc (sizeof (struct T)
+					       + sizeof (int), 1);
+  i = t->a[1];
+
+  struct U *u = (struct U *) __builtin_calloc (sizeof (struct U)
+					       + sizeof (int), 1);
+  i = u->a[1];
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-5.c gcc/testsuite/c-c++-common/ubsan/object-size-5.c
index e69de29..3dada10 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-5.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-5.c
@@ -0,0 +1,38 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+/* Test structures with -fsanitize=object-size.  */
+
+#define N 20
+
+struct S { char *p; int i; };
+struct T { struct S *s; };
+
+__attribute__((noinline, noclone)) void
+f1 (int i)
+{
+  volatile int j;
+  struct S s;
+  s.p = (char *) __builtin_calloc (N, 1);
+  j = s.p[i];
+  j = *(s.p + i);
+  __builtin_free (s.p);
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+int
+main ()
+{
+  f1 (N);
+  f1 (N - 1);
+  return 0;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-6.c gcc/testsuite/c-c++-common/ubsan/object-size-6.c
index e69de29..0e6035d 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-6.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-6.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+char
+foo (void *v)
+{
+  return *(char *) v;
+}
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-7.c gcc/testsuite/c-c++-common/ubsan/object-size-7.c
index e69de29..f5b26e5 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-7.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-7.c
@@ -0,0 +1,29 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+#define N 20
+
+struct S { int a; };
+
+__attribute__((noinline, noclone)) struct S
+f1 (int i)
+{
+  struct S a[N];
+  struct S *p = a;
+  struct S s;
+  s = p[i];
+  return s;
+}
+
+int
+main ()
+{
+  f1 (N);
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-8.c gcc/testsuite/c-c++-common/ubsan/object-size-8.c
index e69de29..ee0945b 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-8.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-8.c
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+struct S { int a; int b; };
+
+static inline __attribute__((always_inline)) int
+foo (struct S *p)
+{
+  volatile int a;
+  a = p->a; /* OK */
+  return p->b;
+}
+
+int
+bar (void)
+{
+  struct S *p = (struct S *) __builtin_calloc (sizeof (int) + sizeof (int) / 2, 1);
+  return foo (p);
+}
+
+int
+main (void)
+{
+  bar ();
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/object-size-9.c gcc/testsuite/c-c++-common/ubsan/object-size-9.c
index e69de29..829c822 100644
--- gcc/testsuite/c-c++-common/ubsan/object-size-9.c
+++ gcc/testsuite/c-c++-common/ubsan/object-size-9.c
@@ -0,0 +1,97 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+/* Test PARM_DECLs and RESULT_DECLs.  */
+
+struct T { char d[8]; int e; };
+struct T t = { "abcdefg", 1 };
+#ifdef __cplusplus
+struct C { C () : d("abcdefg"), e(1) {} C (const C &x) { __builtin_memcpy (d, x.d, 8); e = x.e; } ~C () {} char d[8]; int e; };
+#endif
+struct U { int a : 5; int b : 19; int c : 8; };
+struct S { struct U d[10]; };
+struct S s;
+
+int
+f1 (struct T x, int i)
+{
+  char *p = x.d;
+  p += i;
+  return *p;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+#ifdef __cplusplus
+struct C
+f2 (int i)
+{
+  struct C x;
+  x.d[i] = 'z';
+  return x;
+}
+
+/* { dg-output "\[^\n\r]*index 12 out of bounds for type 'char \\\[8\\\]'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+
+struct C
+f3 (int i)
+{
+  struct C x;
+  char *p = x.d;
+  p += i;
+  *p = 'z';
+  return x;
+}
+
+/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+
+#endif
+
+int
+f4 (int i)
+{
+  return s.d[i].b;
+}
+
+/* { dg-output "\[^\n\r]*index 12 out of bounds for type 'U \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+int
+f5 (int i)
+{
+  struct U *u = s.d;
+  u += i;
+  return u->b;
+}
+
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
+
+int
+main (void)
+{
+  f1 (t, 12);
+#ifdef __cplusplus
+  f2 (12);
+  f3 (12);
+#endif
+  f4 (12);
+  f5 (12);
+  return 0;
+}
diff --git gcc/testsuite/g++.dg/ubsan/object-size-1.C gcc/testsuite/g++.dg/ubsan/object-size-1.C
index e69de29..e2aad46 100644
--- gcc/testsuite/g++.dg/ubsan/object-size-1.C
+++ gcc/testsuite/g++.dg/ubsan/object-size-1.C
@@ -0,0 +1,18 @@
+// { dg-do compile }
+// { dg-options "-fsanitize=undefined -fpermissive" }
+
+struct T { int c; char d[]; };
+
+struct T t = { 1, "a" }; // { dg-warning "initializer-string for array of chars is too long" }
+
+int
+baz (int i)
+{
+  return t.d[i];
+}
+
+int
+main (void)
+{
+  baz (3);
+}
diff --git gcc/testsuite/gcc.dg/ubsan/object-size-9.c gcc/testsuite/gcc.dg/ubsan/object-size-9.c
index e69de29..bb9aa1b 100644
--- gcc/testsuite/gcc.dg/ubsan/object-size-9.c
+++ gcc/testsuite/gcc.dg/ubsan/object-size-9.c
@@ -0,0 +1,24 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+struct T { int c; char d[]; };
+struct T t = { 1, "a" };
+
+int
+baz (int i)
+{
+  return t.d[i];
+}
+
+int
+main (void)
+{
+  baz (2);
+  return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/ubsan.c gcc/ubsan.c
index c9a72ad..dde0418 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "realmpfr.h"
 #include "dfp.h"
 #include "builtins.h"
+#include "tree-object-size.h"
 
 /* Map from a tree to a VAR_DECL tree.  */
 
@@ -391,7 +392,7 @@ ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
 	  tree dom = TYPE_DOMAIN (t);
 	  if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST)
 	    pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC,
-			    tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1);
+			    tree_to_uhwi (TYPE_MAX_VALUE (dom)) + 1);
 	  else
 	    /* ??? We can't determine the variable name; print VLA unspec.  */
 	    pretty_name[pos++] = '*';
@@ -614,12 +615,12 @@ ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
   /* Create condition "if (index > bound)".  */
   basic_block then_bb, fallthru_bb;
   gimple_stmt_iterator cond_insert_point
-    = create_cond_insert_point (gsi, 0/*before_p*/, false, true,
+    = create_cond_insert_point (gsi, false, false, true,
 				&then_bb, &fallthru_bb);
   index = fold_convert (TREE_TYPE (bound), index);
   index = force_gimple_operand_gsi (&cond_insert_point, index,
-				    true/*simple_p*/, NULL_TREE,
-				    false/*before*/, GSI_NEW_STMT);
+				    true, NULL_TREE,
+				    false, GSI_NEW_STMT);
   gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE);
   gimple_set_location (g, loc);
   gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
@@ -830,6 +831,76 @@ ubsan_expand_null_ifn (gimple_stmt_iterator *gsip)
   return false;
 }
 
+/* Expand UBSAN_OBJECT_SIZE internal call.  */
+
+bool
+ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  gcc_assert (gimple_call_num_args (stmt) == 4);
+
+  tree ptr = gimple_call_arg (stmt, 0);
+  tree offset = gimple_call_arg (stmt, 1);
+  tree size = gimple_call_arg (stmt, 2);
+  tree ckind = gimple_call_arg (stmt, 3);
+  gimple_stmt_iterator gsi_orig = *gsi;
+  gimple g;
+
+  /* See if we can discard the check.  */
+  if (TREE_CODE (size) != INTEGER_CST
+      || integer_all_onesp (size))
+    /* Yes, __builtin_object_size couldn't determine the
+       object size.  */;
+  else
+    {
+      /* if (offset > objsize) */
+      basic_block then_bb, fallthru_bb;
+      gimple_stmt_iterator cond_insert_point
+	= create_cond_insert_point (gsi, false, false, true,
+				    &then_bb, &fallthru_bb);
+      g = gimple_build_cond (GT_EXPR, offset, size, NULL_TREE, NULL_TREE);
+      gimple_set_location (g, loc);
+      gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+
+      /* Generate __ubsan_handle_type_mismatch call.  */
+      *gsi = gsi_after_labels (then_bb);
+      if (flag_sanitize_undefined_trap_on_error)
+	g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+      else
+	{
+	  tree data
+	    = ubsan_create_data ("__ubsan_objsz_data", 1, &loc,
+				 ubsan_type_descriptor (TREE_TYPE (ptr),
+							UBSAN_PRINT_POINTER),
+				 NULL_TREE,
+				 build_zero_cst (pointer_sized_int_node),
+				 ckind,
+				 NULL_TREE);
+	  data = build_fold_addr_expr_loc (loc, data);
+	  enum built_in_function bcode
+	    = flag_sanitize_recover
+	      ? BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH
+	      : BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT;
+	  tree p = make_ssa_name (pointer_sized_int_node, NULL);
+	  g = gimple_build_assign_with_ops (NOP_EXPR, p, ptr, NULL_TREE);
+	  gimple_set_location (g, loc);
+	  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+	  g = gimple_build_call (builtin_decl_explicit (bcode), 2, data, p);
+	}
+      gimple_set_location (g, loc);
+      gsi_insert_before (gsi, g, GSI_SAME_STMT);
+
+      /* Point GSI to next logical statement.  */
+      *gsi = gsi_start_bb (fallthru_bb);
+    }
+
+  /* Get rid of the UBSAN_OBJECT_SIZE call from the IR.  */
+  unlink_stmt_vdef (stmt);
+  gsi_remove (&gsi_orig, true);
+  return gsi_end_p (*gsi);
+}
+
 /* 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.  */
 
@@ -1339,6 +1410,128 @@ instrument_nonnull_return (gimple_stmt_iterator *gsi)
   flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
 }
 
+/* Instrument memory references.  Here we check whether the pointer
+   points to an out-of-bounds location.  */
+
+static void
+instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
+  tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt);
+  tree type;
+  HOST_WIDE_INT size_in_bytes;
+
+  type = TREE_TYPE (t);
+  if (VOID_TYPE_P (type))
+    return;
+
+  switch (TREE_CODE (t))
+    {
+    case COMPONENT_REF:
+      if (TREE_CODE (t) == COMPONENT_REF
+	  && DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1)) != NULL_TREE)
+	{
+	  tree repr = DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1));
+	  t = build3 (COMPONENT_REF, TREE_TYPE (repr), TREE_OPERAND (t, 0),
+		      repr, NULL_TREE);
+	}
+      break;
+    case ARRAY_REF:
+    case INDIRECT_REF:
+    case MEM_REF:
+    case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+      break;
+    default:
+      return;
+    }
+
+  size_in_bytes = int_size_in_bytes (type);
+  if (size_in_bytes <= 0)
+    return;
+
+  HOST_WIDE_INT bitsize, bitpos;
+  tree offset;
+  enum machine_mode mode;
+  int volatilep = 0, unsignedp = 0;
+  tree inner = get_inner_reference (t, &bitsize, &bitpos, &offset, &mode,
+				    &unsignedp, &volatilep, false);
+
+  if (bitpos % BITS_PER_UNIT != 0
+      || bitsize != size_in_bytes * BITS_PER_UNIT)
+    return;
+
+  bool decl_p = DECL_P (inner);
+  tree base = decl_p ? inner : TREE_OPERAND (inner, 0);
+  tree ptr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (t)), t);
+
+  while (TREE_CODE (base) == SSA_NAME)
+    {
+      gimple def_stmt = SSA_NAME_DEF_STMT (base);
+      if (gimple_assign_ssa_name_copy_p (def_stmt)
+	  || (gimple_assign_cast_p (def_stmt)
+	      && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (def_stmt))))
+	  || (is_gimple_assign (def_stmt)
+	      && gimple_assign_rhs_code (def_stmt) == POINTER_PLUS_EXPR))
+	base = gimple_assign_rhs1 (def_stmt);
+      else
+	break;
+    }
+
+  if (!POINTER_TYPE_P (TREE_TYPE (base)) && !DECL_P (base))
+    return;
+
+  tree sizet;
+  tree base_addr = base;
+  if (decl_p)
+    base_addr = build1 (ADDR_EXPR,
+			build_pointer_type (TREE_TYPE (base)), base);
+  unsigned HOST_WIDE_INT size = compute_builtin_object_size (base_addr, 0);
+  if (size != (unsigned HOST_WIDE_INT) -1)
+    sizet = build_int_cst (sizetype, size);
+  else if (optimize)
+    {
+      if (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION)
+	loc = input_location;
+      /* Generate __builtin_object_size call.  */
+      sizet = builtin_decl_explicit (BUILT_IN_OBJECT_SIZE);
+      sizet = build_call_expr_loc (loc, sizet, 2, base_addr,
+				   integer_zero_node);
+      sizet = force_gimple_operand_gsi (gsi, sizet, false, NULL_TREE, true,
+					GSI_SAME_STMT);
+    }
+  else
+    return;
+
+  /* Generate UBSAN_OBJECT_SIZE (ptr, ptr+sizeof(*ptr)-base, objsize, ckind)
+     call.  */
+  /* ptr + sizeof (*ptr) - base */
+  t = fold_build2 (MINUS_EXPR, sizetype,
+		   fold_convert (pointer_sized_int_node, ptr),
+		   fold_convert (pointer_sized_int_node, base_addr));
+  t = fold_build2 (PLUS_EXPR, sizetype, t, TYPE_SIZE_UNIT (type));
+
+  /* Perhaps we can omit the check.  */
+  if (TREE_CODE (t) == INTEGER_CST
+      && TREE_CODE (sizet) == INTEGER_CST
+      && tree_int_cst_le (t, sizet))
+    return;
+
+  /* Nope.  Emit the check.  */
+  t = force_gimple_operand_gsi (gsi, t, true, NULL_TREE, true,
+				GSI_SAME_STMT);
+  ptr = force_gimple_operand_gsi (gsi, ptr, true, NULL_TREE, true,
+				  GSI_SAME_STMT);
+  tree ckind = build_int_cst (unsigned_char_type_node,
+			      is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF);
+  gimple g = gimple_build_call_internal (IFN_UBSAN_OBJECT_SIZE, 4,
+					 ptr, t, sizet, ckind);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+}
+
 namespace {
 
 const pass_data pass_data_ubsan =
@@ -1368,7 +1561,8 @@ public:
 			      | SANITIZE_BOOL | SANITIZE_ENUM
 			      | SANITIZE_ALIGNMENT
 			      | SANITIZE_NONNULL_ATTRIBUTE
-			      | SANITIZE_RETURNS_NONNULL_ATTRIBUTE)
+			      | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
+			      | SANITIZE_OBJECT_SIZE)
 	     && current_function_decl != NULL_TREE
 	     && !lookup_attribute ("no_sanitize_undefined",
 				   DECL_ATTRIBUTES (current_function_decl));
@@ -1431,6 +1625,14 @@ pass_ubsan::execute (function *fun)
 	      bb = gimple_bb (stmt);
 	    }
 
+	  if (flag_sanitize & SANITIZE_OBJECT_SIZE)
+	    {
+	      if (gimple_store_p (stmt))
+		instrument_object_size (&gsi, true);
+	      if (gimple_assign_load_p (stmt))
+		instrument_object_size (&gsi, false);
+	    }
+
 	  gsi_next (&gsi);
 	}
     }
diff --git gcc/ubsan.h gcc/ubsan.h
index d0b404f..27c18eb 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -40,6 +40,7 @@ enum ubsan_print_style {
 
 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 tree ubsan_instrument_unreachable (location_t);
 extern tree ubsan_create_data (const char *, int, const location_t *, ...);
 extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);

	Marek

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] Implement -fsanitize=object-size
  2014-10-10 10:27         ` Jakub Jelinek
  2014-10-10 16:37           ` Marek Polacek
@ 2014-10-14 16:01           ` Jakub Jelinek
  1 sibling, 0 replies; 9+ messages in thread
From: Jakub Jelinek @ 2014-10-14 16:01 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches

On Fri, Oct 10, 2014 at 12:26:44PM +0200, Jakub Jelinek wrote:
> 2014-10-10  Jakub Jelinek  <jakub@redhat.com>
> 
> 	* ubsan/Makefile.am (DEFS): Add -DPIC.
> 	* ubsan/Makefile.in: Regenerated.

I've now bootstrapped/regtested this on x86_64-linux and i686-linux
and committed as obvious.

2014-10-14  Jakub Jelinek  <jakub@redhat.com>

	* ubsan/Makefile.am (DEFS): Add -DPIC.
	* ubsan/Makefile.in: Regenerated.

--- libsanitizer/ubsan/Makefile.am	2014-09-24 11:08:04.183026156 +0200
+++ libsanitizer/ubsan/Makefile.am	2014-10-10 12:15:19.124247283 +0200
@@ -3,7 +3,7 @@ AM_CPPFLAGS = -I $(top_srcdir) -I $(top_
 # May be used by toolexeclibdir.
 gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
 
-DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS 
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC
 AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long  -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
 AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
 ACLOCAL_AMFLAGS = -I m4

--- libsanitizer/ubsan/Makefile.in	2014-09-25 15:01:25.448109866 +0200
+++ libsanitizer/ubsan/Makefile.in	2014-10-14 11:26:17.772201307 +0200
@@ -132,7 +132,7 @@ CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
-DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS 
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC
 DEPDIR = @DEPDIR@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@


	Jakub

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2014-10-14 15:58 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-07-13 17:55 [PATCH] Implement -fsanitize=object-size Marek Polacek
2014-07-13 21:10 ` Gerald Pfeifer
2014-07-14 11:54 ` Jakub Jelinek
2014-09-11 17:48   ` Marek Polacek
2014-10-02 12:04     ` Jakub Jelinek
2014-10-10 10:26       ` Marek Polacek
2014-10-10 10:27         ` Jakub Jelinek
2014-10-10 16:37           ` Marek Polacek
2014-10-14 16:01           ` Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).