diff mbox series

tree-optimization/pr103961: Never compute offset for -1 size

Message ID 20220111131044.1947091-1-siddhesh@gotplt.org
State New
Headers show
Series tree-optimization/pr103961: Never compute offset for -1 size | expand

Commit Message

Siddhesh Poyarekar Jan. 11, 2022, 1:10 p.m. UTC
Never try to compute size for offset when the object size is -1, which
is either unknown maximum or uninitialized minimum irrespective of the
osi->pass number.

gcc/ChangeLog:

	PR tree-optimization/pr103961
	* tree-object-size.c (plus_stmt_object_size): Always avoid
	computing offset for -1 size.

gcc/testsuite/ChangeLog:

	PR tree-optimization/pr103961
	* gcc.dg/pr103961.c: New test case.

Co-authored-by: Jakub Jelinek <jakub@redhat.com>
Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
Tested with i686 build+test, x86_64 bootstrap build+test and ubsan
bootstrap.

 gcc/testsuite/gcc.dg/pr103961.c | 30 ++++++++++++++++++++++++++++++
 gcc/tree-object-size.c          | 11 ++++-------
 2 files changed, 34 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr103961.c

Comments

Jakub Jelinek Jan. 11, 2022, 1:34 p.m. UTC | #1
On Tue, Jan 11, 2022 at 06:40:44PM +0530, Siddhesh Poyarekar wrote:
> Never try to compute size for offset when the object size is -1, which
> is either unknown maximum or uninitialized minimum irrespective of the
> osi->pass number.
> 
> gcc/ChangeLog:
> 
> 	PR tree-optimization/pr103961
> 	* tree-object-size.c (plus_stmt_object_size): Always avoid
> 	computing offset for -1 size.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR tree-optimization/pr103961
> 	* gcc.dg/pr103961.c: New test case.

Ok.  Martin's executable testcase would work too but only if
it would be limited to targets with glibc with __sprintf_chk in or
it would need to be linked against -lssp, so I think it is fine as is.

> +void
> +cap_to_text (int c)
> +{
> +  char buf[1572];
> +  char *p;
> +  int n, t;
> +  p = 20 + buf;
> +  for (t = 8; t--; )
> +    {
> +      for (n = 0; n < c; n++)
> +	p += sprintf (p, "a,");
> +      p--;
> +      if (__builtin_object_size (p, 1) == 0)
> +	abort ();
> +    }

Just curious, does your PR77608 patch change this such that __bos (p, 1)
is not ~(size_t)0 but 1572?
We don't know if c isn't 0, so can't count on p += sprintf (p, "a,");
actually incrementing the pointer (and unless we try to understand more
what exactly it is doing we'd need to also assume it could e.g. return -1,
and we aren't too smart about loops anyway, but if the PR77608 patch assumes
that variable bounds aren't out of bounds, then a pointer that started
as 20 + buf can be minimum buf + 0 and maximum buf + 1572.
Note, as __bos is trying to prevent exploiting UB, perhaps assuming the
pointer arithmetics or array refs aren't out of bounds isn't perfect, but
then I'd say a __bos (p, 1) == 1572 means bigger security than __bos (p, 1)
== -1 where we punt and don't check anything.

	Jakub
Siddhesh Poyarekar Jan. 11, 2022, 2:09 p.m. UTC | #2
On 11/01/2022 19:04, Jakub Jelinek wrote:
> On Tue, Jan 11, 2022 at 06:40:44PM +0530, Siddhesh Poyarekar wrote:
>> Never try to compute size for offset when the object size is -1, which
>> is either unknown maximum or uninitialized minimum irrespective of the
>> osi->pass number.
>>
>> gcc/ChangeLog:
>>
>> 	PR tree-optimization/pr103961
>> 	* tree-object-size.c (plus_stmt_object_size): Always avoid
>> 	computing offset for -1 size.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 	PR tree-optimization/pr103961
>> 	* gcc.dg/pr103961.c: New test case.
> 
> Ok.  Martin's executable testcase would work too but only if
> it would be limited to targets with glibc with __sprintf_chk in or
> it would need to be linked against -lssp, so I think it is fine as is.

Thanks I'll push this then.

>> +void
>> +cap_to_text (int c)
>> +{
>> +  char buf[1572];
>> +  char *p;
>> +  int n, t;
>> +  p = 20 + buf;
>> +  for (t = 8; t--; )
>> +    {
>> +      for (n = 0; n < c; n++)
>> +	p += sprintf (p, "a,");
>> +      p--;
>> +      if (__builtin_object_size (p, 1) == 0)
>> +	abort ();
>> +    }
> 
> Just curious, does your PR77608 patch change this such that __bos (p, 1)
> is not ~(size_t)0 but 1572?
> We don't know if c isn't 0, so can't count on p += sprintf (p, "a,");
> actually incrementing the pointer (and unless we try to understand more
> what exactly it is doing we'd need to also assume it could e.g. return -1,
> and we aren't too smart about loops anyway, but if the PR77608 patch assumes
> that variable bounds aren't out of bounds, then a pointer that started
> as 20 + buf can be minimum buf + 0 and maximum buf + 1572.
> Note, as __bos is trying to prevent exploiting UB, perhaps assuming the
> pointer arithmetics or array refs aren't out of bounds isn't perfect, but
> then I'd say a __bos (p, 1) == 1572 means bigger security than __bos (p, 1)
> == -1 where we punt and don't check anything.

That's a good catch; the 77608 patch actually breaks this again, but for 
a different reason that the patch itself introduces.  The "safe" 
assumption of wholesize for p_4 is not safe enough because p_17 (which 
is p_4 - 1) is assumed to have an underflow, resulting in a zero size again.

I need to rethink the 77608 fix; it won't always be as simple as 
returning the wholesize.

Thanks,
Siddhesh
diff mbox series

Patch

diff --git a/gcc/testsuite/gcc.dg/pr103961.c b/gcc/testsuite/gcc.dg/pr103961.c
new file mode 100644
index 00000000000..2cd52884e3b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr103961.c
@@ -0,0 +1,30 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void abort ();
+
+extern inline __attribute__ ((__gnu_inline__)) int
+sprintf (char *restrict s, const char *restrict fmt, ...)
+{
+  return __builtin___sprintf_chk (s, 1, __builtin_object_size (s, 1),
+				  fmt, __builtin_va_arg_pack ());
+}
+
+void
+cap_to_text (int c)
+{
+  char buf[1572];
+  char *p;
+  int n, t;
+  p = 20 + buf;
+  for (t = 8; t--; )
+    {
+      for (n = 0; n < c; n++)
+	p += sprintf (p, "a,");
+      p--;
+      if (__builtin_object_size (p, 1) == 0)
+	abort ();
+    }
+}
+
+/* { dg-final { scan-assembler-not "abort" } } */
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index fbaf57a20f8..f7cc323591c 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -990,13 +990,10 @@  plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
 	  addr_object_size (osi, op0, object_size_type, &bytes, &wholesize);
 	}
 
-      /* In the first pass, do not compute size for offset if either the
-	 maximum size is unknown or the minimum size is not initialized yet;
-	 the latter indicates a dependency loop and will be resolved in
-	 subsequent passes.  We attempt to compute offset for 0 minimum size
-	 too because a negative offset could be within bounds of WHOLESIZE,
-	 giving a non-zero result for VAR.  */
-      if (osi->pass != 0 || !size_unknown_p (bytes, 0))
+      /* size_for_offset doesn't make sense for -1 size, but it does for size 0
+	 since the wholesize could be non-zero and a negative offset could give
+	 a non-zero size.  */
+      if (!size_unknown_p (bytes, 0))
 	bytes = size_for_offset (bytes, op1, wholesize);
     }
   else