diff mbox

c/71392 - SEGV calling integer overflow built-ins with a null pointer

Message ID 5750BFA2.5030508@gmail.com
State New
Headers show

Commit Message

Martin Sebor June 2, 2016, 11:22 p.m. UTC
In a discussion of a patch in a this area (c/68120 and c++/70507)
Jakub noticed that the integer arithmetic built-ins with overflow
checking that expect a pointer to an integer as the last argument
silently (i.e., without a warning) accept a null pointer.  As the
test case in the bug referenced in in subject shows, such calls
then crash at runtime.

The attached patch follows the same approach used by other built
ins that take a pointer to an object (such as __built_strlen) to
issue a -Wnonnull warning for such invalid calls.

Martin
diff mbox

Patch

PR c/71392 - SEGV calling integer overflow built-ins with a null pointer

gcc/testsuite/ChangeLog:
2016-06-02  Martin Sebor  <msebor@redhat.com>

	PR c/71392
	* c-c++-common/builtin-arith-overflow-1.c: Add test cases.

gcc/ChangeLog:
2016-06-02  Martin Sebor  <msebor@redhat.com>

	PR c/71392
	* builtin-attrs.def (ATTR_NOTHROW_NONNULL_LEAF_LIST): New macro.
	* builtins.def (BUILT_IN_SADD_OVERFLOW, BUILT_IN_SADDL_OVERFLOW): Use it.
	(BUILT_IN_SADDLL_OVERFLOW, BUILT_IN_SSUB_OVERFLOW): Same.
	(BUILT_IN_SSUBL_OVERFLOW, BUILT_IN_SSUBLL_OVERFLOW): Same.
	(BUILT_IN_SMUL_OVERFLOW, BUILT_IN_SMULL_OVERFLOW): Same.
	(BUILT_IN_SMULLL_OVERFLOW, BUILT_IN_UADD_OVERFLOW): Same.
	(BUILT_IN_UADDL_OVERFLOW, BUILT_IN_UADDLL_OVERFLOW): Same.
	(BUILT_IN_USUB_OVERFLOW, BUILT_IN_USUBL_OVERFLOW): Same.
	(BUILT_IN_USUBLL_OVERFLOW, BUILT_IN_UMUL_OVERFLOW): Same.
	(BUILT_IN_UMULL_OVERFLOW, BUILT_IN_UMULLL_OVERFLOW):

diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def
index 089817a..cefe121 100644
--- a/gcc/builtin-attrs.def
+++ b/gcc/builtin-attrs.def
@@ -165,6 +165,7 @@  DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL, ATTR_NONNULL, ATTR_NULL, \
 /* Nothrow leaf functions whose pointer parameter(s) are all nonnull.  */
 DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_LEAF, ATTR_NONNULL, ATTR_NULL, \
 			ATTR_NOTHROW_LEAF_LIST)
+DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_LEAF_LIST, ATTR_LEAF, ATTR_NULL, ATTR_NOTHROW_NONNULL_LEAF)
 /* Nothrow functions whose first parameter is a nonnull pointer.  */
 DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_1, ATTR_NONNULL, ATTR_LIST_1, \
 			ATTR_NOTHROW_LIST)
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 2fc7f65..4df15e4 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -711,24 +711,24 @@  DEF_GCC_BUILTIN        (BUILT_IN_ADD_OVERFLOW, "add_overflow", BT_FN_BOOL_VAR, A
 DEF_GCC_BUILTIN        (BUILT_IN_SUB_OVERFLOW, "sub_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
 DEF_GCC_BUILTIN        (BUILT_IN_MUL_OVERFLOW, "mul_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
 /* Clang compatibility.  */
-DEF_GCC_BUILTIN        (BUILT_IN_SADD_OVERFLOW, "sadd_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SADDL_OVERFLOW, "saddl_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SADDLL_OVERFLOW, "saddll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SSUB_OVERFLOW, "ssub_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SSUBL_OVERFLOW, "ssubl_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SSUBLL_OVERFLOW, "ssubll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SMUL_OVERFLOW, "smul_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SMULL_OVERFLOW, "smull_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SMULLL_OVERFLOW, "smulll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UADD_OVERFLOW, "uadd_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UADDL_OVERFLOW, "uaddl_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UADDLL_OVERFLOW, "uaddll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_USUB_OVERFLOW, "usub_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_USUBL_OVERFLOW, "usubl_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_USUBLL_OVERFLOW, "usubll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UMUL_OVERFLOW, "umul_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UMULL_OVERFLOW, "umull_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UMULLL_OVERFLOW, "umulll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SADD_OVERFLOW, "sadd_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SADDL_OVERFLOW, "saddl_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SADDLL_OVERFLOW, "saddll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SSUB_OVERFLOW, "ssub_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SSUBL_OVERFLOW, "ssubl_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SSUBLL_OVERFLOW, "ssubll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SMUL_OVERFLOW, "smul_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SMULL_OVERFLOW, "smull_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SMULLL_OVERFLOW, "smulll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UADD_OVERFLOW, "uadd_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UADDL_OVERFLOW, "uaddl_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UADDLL_OVERFLOW, "uaddll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_USUB_OVERFLOW, "usub_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_USUBL_OVERFLOW, "usubl_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_USUBLL_OVERFLOW, "usubll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UMUL_OVERFLOW, "umul_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UMULL_OVERFLOW, "umull_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UMULLL_OVERFLOW, "umulll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_NONNULL_LEAF_LIST)
 
 /* Category: miscellaneous builtins.  */
 DEF_LIB_BUILTIN        (BUILT_IN_ABORT, "abort", BT_FN_VOID, ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST)
diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c
index 69b5083..9ee787d 100644
--- a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c
+++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c
@@ -1,7 +1,14 @@ 
+/* Test exercising invalid calls to arithmetic overflow checking built-ins,
+   including PR c/71392 - SEGV calling integer overflow built-ins with a null
+   pointer, (issuing a warning for such invocations).  */
 /* { dg-do compile } */
+/* { dg-additional-options "-Wnonnull" }
+
+/* Verify that calls with fewer or more than 3 arguments to the generic
+   __builtin_op_overflow functions are rejected.  */
 
 int
-f1 (void)
+generic_0 (void)
 {
   int x = __builtin_add_overflow ();	/* { dg-error "not enough arguments to function" } */
   x += __builtin_sub_overflow ();	/* { dg-error "not enough arguments to function" } */
@@ -10,14 +17,178 @@  f1 (void)
 }
 
 int
-f2 (int a, int b, int *c, int d)
+generic_1 (int a)
+{
+  int x = __builtin_add_overflow (a);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow (a);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow (a);	/* { dg-error "not enough arguments to function" } */
+
+  /* Literal argument.  */
+  x += __builtin_add_overflow (1);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow (2);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow (3);	/* { dg-error "not enough arguments to function" } */
+  return x;
+}
+
+int
+generic_2 (int a, int b)
+{
+  int x = __builtin_add_overflow (a, b);/* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow (a, b);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow (a, b);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_add_overflow (a, 1);   /* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow (a, 2);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow (a, 3);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_add_overflow (4, b);   /* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow (5, b);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow (6, b);	/* { dg-error "not enough arguments to function" } */
+  return x;
+}
+
+/* Verify that calls with the correct number of arguments to the generic
+   __builtin_op_overflow functions are accepted.  */
+
+int
+generic_3 (int a, int b, int c)
+{
+  int x = __builtin_add_overflow (a, b, &c);
+  x += __builtin_sub_overflow (a, b, &c);
+  x += __builtin_mul_overflow (a, b, &c);
+  x += __builtin_add_overflow (a, 1, &c);
+  x += __builtin_sub_overflow (a, 2, &c);
+  x += __builtin_mul_overflow (a, 3, &c);
+  x += __builtin_add_overflow (4, b, &c);
+  x += __builtin_sub_overflow (5, b, &c);
+  x += __builtin_mul_overflow (6, b, &c);
+  x += __builtin_add_overflow (7, 8, &c);
+  x += __builtin_sub_overflow (9, 10, &c);
+  x += __builtin_mul_overflow (11, 12, &c);
+
+  /* Verify that a null pointer to an integer is accepted without
+     warning as the last argument.  */
+  x += __builtin_add_overflow (a, b, (int *)0);
+  x += __builtin_sub_overflow (a, b, (int *)0);
+  x += __builtin_mul_overflow (a, b, (int *)0);
+  x += __builtin_add_overflow (a, 1, (int *)0);
+  x += __builtin_sub_overflow (a, 2, (int *)0);
+  x += __builtin_mul_overflow (a, 3, (int *)0);
+  x += __builtin_add_overflow (4, b, (int *)0);
+  x += __builtin_sub_overflow (5, b, (int *)0);
+  x += __builtin_mul_overflow (6, b, (int *)0);
+  x += __builtin_add_overflow (7, 8, (int *)0);
+  x += __builtin_sub_overflow (9, 10, (int *)0);
+  x += __builtin_mul_overflow (11, 12, (int *)0);
+
+  return x;
+}
+
+int
+generic_4 (int a, int b, int *c, int d)
 {
   int x = __builtin_add_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
-  x += __builtin_sub_overflow (a, b, c, d, d, d);	/* { dg-error "too many arguments to function" } */
+  x += __builtin_sub_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
   x += __builtin_mul_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
   return x;
 }
 
+/* Verify that calls with fewer or more than 3 arguments to the type
+   specific forms of the __builtin_op_overflow functions are rejected.  */
+
+int
+generic_wrong_type (int a, int b)
+{
+  void *p = 0;
+  double d = 0;
+  int x = __builtin_add_overflow (a, b, p);   /* { dg-error "does not have pointer to integer type" } */
+  x += __builtin_sub_overflow (a, b, &p);     /* { dg-error "does not have pointer to integer type" } */
+  x += __builtin_mul_overflow (a, b, &d);     /* { dg-error "does not have pointer to integer type" } */
+
+  /* Also verify literal arguments.  */
+  x += __builtin_add_overflow (1, 1, p);   /* { dg-error "does not have pointer to integer type" } */
+  x += __builtin_sub_overflow (1, 1, &p);     /* { dg-error "does not have pointer to integer type" } */
+  x += __builtin_mul_overflow (1, 1, &d);     /* { dg-error "does not have pointer to integer type" } */
+  return x;
+}
+
+/* Verify that calls with fewer than 2 or more than 3 arguments to
+   the typed __builtin_op_overflow functions are rejected.  */
+int
+typed_0 (void)
+{
+  int x = __builtin_add_overflow ();	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow ();	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow ();	/* { dg-error "not enough arguments to function" } */
+  return x;
+}
+
+int
+typed_1 (int a)
+{
+  int x = __builtin_sadd_overflow (a);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_ssub_overflow (a);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_smul_overflow (a);	/* { dg-error "too few arguments to function" } */
+  return x;
+}
+
+int
+typed_2 (int a, int b)
+{
+  int x = __builtin_sadd_overflow (a, b);  /* { dg-error "too few arguments to function" } */
+  x += __builtin_ssub_overflow (a, b);	   /* { dg-error "too few arguments to function" } */
+  x += __builtin_smul_overflow (a, b);	   /* { dg-error "too few arguments to function" } */
+  return x;
+}
+
+/* Exercise PR c/71392 - SEGV calling integer overflow built-ins with
+   a null pointer.  Verify that calls with a null argument are diagnosed
+   with -Wnonnull.  */
+
+int
+typed_3_null (int a, int b)
+{
+  int x = 0;
+
+  x += __builtin_sadd_overflow (a, b, (int *)0); /* { dg-warning "null argument" } */
+  x += __builtin_uadd_overflow (a, b, (unsigned *)0); /* { dg-warning "null argument" } */
+
+  x += __builtin_saddl_overflow (a, b, (long *)0); /* { dg-warning "null argument" } */
+  x += __builtin_uaddl_overflow (a, b, (unsigned long *)0); /* { dg-warning "null argument" } */
+
+  x += __builtin_saddll_overflow (a, b, (long long *)0); /* { dg-warning "null argument" } */
+  x += __builtin_uaddll_overflow (a, b, (unsigned long long *)0); /* { dg-warning "null argument" } */
+
+
+  x += __builtin_ssub_overflow (a, b, (int *)0); /* { dg-warning "null argument" } */
+  x += __builtin_usub_overflow (a, b, (unsigned *)0); /* { dg-warning "null argument" } */
+
+  x += __builtin_ssubl_overflow (a, b, (long *)0); /* { dg-warning "null argument" } */
+  x += __builtin_usubl_overflow (a, b, (unsigned long *)0); /* { dg-warning "null argument" } */
+
+  x += __builtin_ssubll_overflow (a, b, (long long *)0); /* { dg-warning "null argument" } */
+  x += __builtin_usubll_overflow (a, b, (unsigned long long *)0); /* { dg-warning "null argument" } */
+
+
+  x += __builtin_smul_overflow (a, b, (int *)0); /* { dg-warning "null argument" } */
+  x += __builtin_umul_overflow (a, b, (unsigned *)0); /* { dg-warning "null argument" } */
+
+  x += __builtin_smull_overflow (a, b, (long *)0); /* { dg-warning "null argument" } */
+  x += __builtin_umull_overflow (a, b, (unsigned long *)0); /* { dg-warning "null argument" } */
+
+  x += __builtin_smulll_overflow (a, b, (long long *)0); /* { dg-warning "null argument" } */
+  x += __builtin_umulll_overflow (a, b, (unsigned long long *)0); /* { dg-warning "null argument" } */
+
+  return x;
+}
+
+int
+typed_4 (int a, int b, int *c, int d)
+{
+  int x = __builtin_sadd_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
+  x += __builtin_ssub_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
+  x += __builtin_smul_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
+  return x;
+}
+
 enum E { e0 = 0, e1 = 1 };
 
 #ifndef __cplusplus