In addition to assuming that built-in functions are called with
the correct number of arguments (bug 83603), the restrict pass
also assumes that they are called with arguments of the expected
types.  When a built-in is declared with no prototype and called
with arguments of the wrong type (such as memcpy (3, dst, ""))
the pass crashes GCC with a SEGV.

The attached patch prevents invalid calls from being checked for
-Wrestrict violations.  It's meant to be applied in conjunction
with the patch for bug 83603:
  https://gcc.gnu.org/ml/gcc-patches/2018-01/msg00005.html

A separate fix is to have GCC issue a warning for declarations
of built-ins with no prototype.  I opened bug 83656 to track this
change request to the C front end (C++ already warns for this).

Another improvement is to also warn about calls with the wrong number
or types of arguments when a built-in is declared without a prototype
despite the first warning, similarly to what Clang does.  I opened
bug 83657 to add this warning.

Martin
PR tree-optimization/83655 - ICE on an invalid call to memcpy declared with no prototype

gcc/testsuite/ChangeLog:

	PR tree-optimization/83655
	* gcc.dg/Wrestrict-5.c: New test.
	* c-c++-common/builtins.c: New test.

gcc/ChangeLog:

	PR tree-optimization/83655
	* gimple-ssa-warn-restrict.c (wrestrict_dom_walker::check_call): 

diff --git a/gcc/gimple-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c
index ac545e4..47d4900 100644
--- a/gcc/gimple-ssa-warn-restrict.c
+++ b/gcc/gimple-ssa-warn-restrict.c
@@ -1688,7 +1688,17 @@ wrestrict_dom_walker::check_call (gcall *call)
   if (!dstwr && strfun)
     dstwr = size_one_node;
 
-  if (check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
+  /* DST and SRC can be null for a call with an insufficient number
+     of arguments to a built-in function declared without a protype.  */
+  if (!dst || !src)
+    return;
+
+  /* DST, SRC, or DSTWR can also have the wrong type in a call to
+     a function declared without a prototype.  Avoid checking such
+     invalid calls.  */
+  if (TREE_CODE (TREE_TYPE (dst)) != POINTER_TYPE
+      || TREE_CODE (TREE_TYPE (src)) != POINTER_TYPE
+      || (dstwr && !INTEGRAL_TYPE_P (TREE_TYPE (dstwr))))
     return;
 
   /* Avoid diagnosing the call again.  */
diff --git a/gcc/testsuite/gcc.dg/Wrestrict-5.c b/gcc/testsuite/gcc.dg/Wrestrict-5.c
new file mode 100644
index 0000000..4912ee5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wrestrict-5.c
@@ -0,0 +1,43 @@
+/* Test to verify that valid calls to common restrict-qualified built-in
+   functions declared with no prototype are checked for overlap, and that
+   invalid calls are ignored.
+  { dg-do compile }
+  { dg-options "-O2 -Wrestrict" }  */
+
+typedef __SIZE_TYPE__ size_t;
+
+#if __cplusplus
+extern "C" {
+
+#define NO_PROTO ...
+#else
+#define NO_PROTO /* empty */
+#endif
+
+void* memcpy ();
+char* strncpy ();
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+void test_memcpy_warn (char *d)
+{
+  memcpy (d, d + 2, 3);       /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
+}
+
+void test_memcpy_nowarn (char *d)
+{
+  memcpy (d, d + 2, "");
+}
+
+
+void test_strncpy_warn (char *d)
+{
+  strncpy (d + 1, d + 3, 5);  /* { dg-warning "accessing 5 bytes at offsets 1 and 3 overlaps 2 bytes at offset 3" } */
+}
+
+void test_strncpy_nowarn (char *d)
+{
+  strncpy (d + 1, d + 3, "");
+}
diff --git a/gcc/testsuite/c-c++-common/builtins.c b/gcc/testsuite/c-c++-common/builtins.c
new file mode 100644
index 0000000..673fcad
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtins.c
@@ -0,0 +1,193 @@
+/* Test to verify that calls to common built-in functions declared
+   with no prototype do not cause an ICE.
+  { dg-do compile }
+  { dg-options "-O2 -Wall -Wextra" }
+  { dg-prune-output "warning" }  */
+
+typedef __SIZE_TYPE__ size_t;
+
+#if __cplusplus
+extern "C" {
+
+#define NO_PROTO ...
+#else
+#define NO_PROTO /* empty */
+#endif
+
+  /* Character classification built-ins from <ctype.h>.  */
+  int isalpha (NO_PROTO);
+  int isalnum (NO_PROTO);
+  int isalpha (NO_PROTO);
+  int iscntrl (NO_PROTO);
+  int isdigit (NO_PROTO);
+  int isgraph (NO_PROTO);
+  int islower (NO_PROTO);
+  int isprint (NO_PROTO);
+  int ispunct (NO_PROTO);
+  int isspace (NO_PROTO);
+  int isupper (NO_PROTO);
+  int isxdigit (NO_PROTO);
+  int tolower (NO_PROTO);
+  int toupper (NO_PROTO);
+
+  /* Memory allocation built-ins from <stdlib.h>.  */
+  void* alloca (NO_PROTO);
+  void* aligned_alloc (NO_PROTO);
+  void* calloc (NO_PROTO);
+  void* malloc (NO_PROTO);
+  void* realloc (NO_PROTO);
+
+  /* Raw memory built-ins from <string.h>.  */
+  void* memcpy (NO_PROTO);
+  void* memchr (NO_PROTO);
+  void* memmove (NO_PROTO);
+  void* mempcpy (NO_PROTO);
+  void* memset (NO_PROTO);
+
+  /* String built-ins from <string.h>.  */
+  char* stpcpy (NO_PROTO);
+  char* stpncpy (NO_PROTO);
+
+  char* strcat (NO_PROTO);
+  char* strcpy (NO_PROTO);
+
+  char* strdup (NO_PROTO);
+  char* strndup (NO_PROTO);
+
+  char* strncat (NO_PROTO);
+  char* strncpy (NO_PROTO);
+
+  size_t strlen (NO_PROTO);
+  size_t strnlen (NO_PROTO);
+
+  char* strchr (NO_PROTO);
+  int strcmp (NO_PROTO);
+  int strncmp (NO_PROTO);
+
+  /* Input/output functions from <stdio.h>.  */
+  int puts (NO_PROTO);
+  int fputs (NO_PROTO);
+
+  int scanf (NO_PROTO);
+  int fscanf (NO_PROTO);
+  int sscanf (NO_PROTO);
+  int vfscanf (NO_PROTO);
+  int vsscanf (NO_PROTO);
+
+  int printf (NO_PROTO);
+  int fprintf (NO_PROTO);
+  int sprintf (NO_PROTO);
+
+  int snprintf (NO_PROTO);
+
+  int vprintf (NO_PROTO);
+  int vfprintf (NO_PROTO);
+  int vsprintf (NO_PROTO);
+
+  int vsnprintf (NO_PROTO);
+
+#if __cplusplus
+}
+#endif
+
+
+#define CONCAT(a, b) a ## b
+#define UNIQ_NAME(func, id) CONCAT (test_ ## func ## _, id)
+
+#define TEST_FUNC(func, arglist)		\
+  __typeof__ (func arglist)			\
+  UNIQ_NAME (func, __COUNTER__) (void) {	\
+    return func arglist;			\
+  }
+
+#define T1(func)				\
+  TEST_FUNC (func, ());				\
+  TEST_FUNC (func, (1));			\
+  TEST_FUNC (func, (""));			\
+  TEST_FUNC (func, ((void*)1));			\
+  TEST_FUNC (func, (iarr));			\
+  TEST_FUNC (func, (function))
+
+#define T2(func)				\
+  TEST_FUNC (func, (1, 1));			\
+  TEST_FUNC (func, (1, ""));			\
+  TEST_FUNC (func, (1, (void*)1));		\
+  TEST_FUNC (func, (1, iarr));			\
+  TEST_FUNC (func, (1, function))
+
+#define T3(func)				\
+  TEST_FUNC (func, (1, 1, 1));			\
+  TEST_FUNC (func, (1, 1, ""));			\
+  TEST_FUNC (func, (1, 1, (void*)1));		\
+  TEST_FUNC (func, (1, 1, iarr));		\
+  TEST_FUNC (func, (1, 1, function))
+
+extern int iarr[];
+extern void function (void);
+
+T1 (isalpha);
+T1 (isalnum);
+T1 (isalpha);
+T1 (iscntrl);
+T1 (isdigit);
+T1 (isgraph);
+T1 (islower);
+T1 (isprint);
+T1 (ispunct);
+T1 (isspace);
+T1 (isupper);
+T1 (isxdigit);
+T1 (tolower);
+T1 (toupper);
+
+T1 (alloca);
+T2 (aligned_alloc);
+T2 (malloc);
+T2 (calloc);
+T2 (realloc);
+
+T3 (memcpy);
+T3 (memmove);
+T3 (mempcpy);
+T3 (memset);
+T3 (memchr);
+
+T2 (stpcpy);
+T3 (stpncpy);
+
+T2 (strcat);
+T2 (strcpy);
+
+T1 (strdup);
+T2 (strndup);
+
+T3 (strncat);
+T3 (strncpy);
+
+T2 (strchr);
+T2 (strcmp);
+T3 (strncmp);
+
+T1 (strlen);
+T2 (strnlen);
+
+T1 (puts);
+T2 (fputs);
+
+T1 (scanf);
+T2 (fscanf);
+T2 (sscanf);
+T2 (vfscanf);
+T2 (vsscanf);
+
+T2 (printf);
+T3 (fprintf);
+T3 (sprintf);
+
+T3 (snprintf);
+
+T2 (vprintf);
+T2 (vfprintf);
+T2 (vsprintf);
+
+T3 (vsnprintf);

Reply via email to