Avoid expecting nonzero size for access none void* arguments [PR101751]

Message ID 93d80da9-c2fa-9324-bf19-53b606984b3a@gmail.com
State New
Headers
Series Avoid expecting nonzero size for access none void* arguments [PR101751] |

Commit Message

Martin Sebor Nov. 25, 2021, 2:16 a.m. UTC
  When the optional size-index argument to attribute index is
omitted for a pointer, GCC expects the actual pointer argument
to point to an object at least as big as its size implies, or
at least one byte for void*.  This is done to make it possible
to detect past-the-end accesses in calls to functions that
only take a pointer (and not a size).

This logic has proved to be overly restrictive for the "none"
access mode applied to void* pointer arguments as a signal
that a function doesn't access the object.  The use case that
brought this to light is a function that only stores its pointer
argument somewhere for later use, without ever dereferencing it,
like pthread_setspecific() does.  pthread_setspecific() needs to
use attribute access because it takes a const void* argument,
and GCC assumes that functions with const-qualified pointer
arguments read from the memory they point to (as most do) and
issues -Wuninitialized when it detects the object such a pointer
points to is not initialized.

The attached tweak adjusts the logic to exempt void* arguments
with access none from the usual bounds checking by setting
the expected object size to zero.  This lets Glibc to continue
to annotate pthread_setspecific() with attribute access none
in its headers to avoid the -Wuninitialized in user code.

Tested on x86_64-linux.

Martin
  

Comments

Jeff Law Dec. 3, 2021, 3:24 a.m. UTC | #1
On 11/24/2021 7:16 PM, Martin Sebor via Gcc-patches wrote:
> When the optional size-index argument to attribute index is
> omitted for a pointer, GCC expects the actual pointer argument
> to point to an object at least as big as its size implies, or
> at least one byte for void*.  This is done to make it possible
> to detect past-the-end accesses in calls to functions that
> only take a pointer (and not a size).
>
> This logic has proved to be overly restrictive for the "none"
> access mode applied to void* pointer arguments as a signal
> that a function doesn't access the object.  The use case that
> brought this to light is a function that only stores its pointer
> argument somewhere for later use, without ever dereferencing it,
> like pthread_setspecific() does.  pthread_setspecific() needs to
> use attribute access because it takes a const void* argument,
> and GCC assumes that functions with const-qualified pointer
> arguments read from the memory they point to (as most do) and
> issues -Wuninitialized when it detects the object such a pointer
> points to is not initialized.
>
> The attached tweak adjusts the logic to exempt void* arguments
> with access none from the usual bounds checking by setting
> the expected object size to zero.  This lets Glibc to continue
> to annotate pthread_setspecific() with attribute access none
> in its headers to avoid the -Wuninitialized in user code.
>
> Tested on x86_64-linux.
>
> Martin
>
> gcc-101751.diff
>
> Avoid expecting nonzero size for access none void* arguments [PR101751].
>
> Resolves:
> PR middle-end/101751 - attribute access none with void pointer expects nonzero size
>
> gcc/ChangeLog:
>
> 	PR middle-end/101751
> 	* doc/invoke.texi (attribute access): Adjust.
> 	* gimple-ssa-warn-access.cc (pass_waccess::maybe_check_access_sizes):
>
> gcc/testsuite/ChangeLog:
>
> 	PR middle-end/101751
> 	* gcc.dg/Wstringop-overflow-86.c: New test.
OK
jeff
  

Patch

Avoid expecting nonzero size for access none void* arguments [PR101751].

Resolves:
PR middle-end/101751 - attribute access none with void pointer expects nonzero size

gcc/ChangeLog:

	PR middle-end/101751
	* doc/invoke.texi (attribute access): Adjust.
	* gimple-ssa-warn-access.cc (pass_waccess::maybe_check_access_sizes):

gcc/testsuite/ChangeLog:

	PR middle-end/101751
	* gcc.dg/Wstringop-overflow-86.c: New test.

diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 9a9f48685b9..11c99e5dfab 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -2999,6 +2999,10 @@  pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
 	  if (access.second.minsize
 	      && access.second.minsize != HOST_WIDE_INT_M1U)
 	    access_nelts = build_int_cstu (sizetype, access.second.minsize);
+	  else if (VOID_TYPE_P (argtype) && access.second.mode == access_none)
+	    /* Treat access mode none on a void* argument as expecting
+	       as little as zero bytes.  */
+	    access_nelts = size_zero_node;
 	  else
 	    access_nelts = size_one_node;
 	}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-86.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-86.c
new file mode 100644
index 00000000000..345abe4a274
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-86.c
@@ -0,0 +1,63 @@ 
+/* PR middle-end/101751 - attribute access none with void pointer expects
+   nonzero size
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+__attribute__ ((access (none, 1))) void
+fvp_m1 (const void*);
+
+void nowarn_m1 (void)
+{
+  /* Verify these don't trigger a warning for calls to a function
+     declared with attribute access none.  */
+  fvp_m1 ((void*)-1);         // { dg-bogus "-Wstringop-" }
+  fvp_m1 ((void*)1);          // { dg-bogus "-Wstringop-" }
+}
+
+
+__attribute__ ((access (none, 1))) void
+fvp_none (void*);
+
+void nowarn_c_cp1 (void)
+{
+  char c;
+  fvp_none (&c);
+  fvp_none (&c + 1);          // { dg-bogus "-Wstringop-" }
+}
+
+void nowarn_f_fp1 (void)
+{
+  fvp_none ((char*)&nowarn_f_fp1);
+  fvp_none ((char*)&nowarn_f_fp1 + 1);
+}
+
+void nowarn_sp1_sp_4 (void)
+{
+  fvp_none ("" + 1);          // { dg-bogus "-Wstringop-" }
+  fvp_none ("123" + 4);       // { dg-bogus "-Wstringop-" }
+}
+
+
+__attribute__ ((access (none, 1))) void
+wfvp_none (void*);            // { dg-message "in a call to function 'wfvp_none' declared with attribute 'access \\\(none, 1\\\)'" }
+
+void warn_cm1_p1 (void)
+{
+  char c;
+  /* With optimization both of the following are diagnosed by -Warray-bounds.
+     The second also without optimization by -Wstringop-overread.  They
+     should both be diagnosed by the same warning even without optimization. */
+  wfvp_none (&c - 1);         // { dg-warning "" "pr??????" { xfail *-*-* } }
+  wfvp_none (&c + 2);         // { dg-warning "" }
+}
+
+void warn_fp2 (void)
+{
+  void *p = (char*)&warn_fp2 + sizeof warn_fp2;
+  fvp_none (p);               // { dg-warning "" "pr??????" { xfail *-*-* } }
+}
+
+void warn_sp2 (void)
+{
+  wfvp_none ("" + 2);         // { dg-warning "" }
+}
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ef654d7b878..266ef76e5c3 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2524,7 +2524,6 @@  The following attributes are supported on most targets.
 @table @code
 @c Keep this table alphabetized by attribute name.  Treat _ as space.
 
-@item access
 @itemx access (@var{access-mode}, @var{ref-index})
 @itemx access (@var{access-mode}, @var{ref-index}, @var{size-index})
 
@@ -2598,7 +2597,9 @@  __attribute__ ((access (write_only, 1, 2), access (read_write, 3))) int fgets (c
 The access mode @code{none} specifies that the pointer to which it applies
 is not used to access the referenced object at all.  Unless the pointer is
 null the pointed-to object must exist and have at least the size as denoted
-by the @var{size-index} argument.  The object need not be initialized.
+by the @var{size-index} argument.  When the optional @var{size-index}
+argument is omitted for an argument of @code{void*} type the actual pointer
+agument is ignored.  The referenced object need not be initialized.
 The mode is intended to be used as a means to help validate the expected
 object size, for example in functions that call @code{__builtin_object_size}.
 @xref{Object Size Checking}.