On 08/20/2016 01:26 AM, Jakub Jelinek wrote:
On Fri, Aug 19, 2016 at 04:30:47PM -0600, Martin Sebor wrote:
The patch looks bigger than it actually is because:
1) It modifies the return type of the function to bool rather than
unsigned HOST_WIDE_INT representing the object size (this was
necessary to avoid having its callers misinterpret zero as
unknown when it means zero bytes).
Can you explain why do you need this? I don't understand why do you need to
differentiate between unknown and maximum (or minimum for modes 2 and 3 that
nobody actually uses in real-world), the builtin after all returns the same
value for both. If you want to know if the compiler knows the size
precisely, you can request both mode 0 (or 1) and 2 (or 3) and compare, if
the values are the same, it is the exact size, if there is a range, then you
have minimum and maximum (and, if minimum is 0, but maximum non-zero, you
really don't know minimum, if maximum is -1, then you really don't know the
maximum (no object should be better that big). For the return value, I
don't see how you could reliably differentiate between the two even if it
made for whatever strange reason sense - for SSA_NAMEs etc. you have just
recorded the sizes, not also a flag whether it is unknown or known.
The change makes it possible to fold into constants even at -O0 type
2 and 3 calls to the built-in with zero-sized objects.
fold_builtin_object_size repeatedly calls compute_builtin_object_size
to compute the same result (zero) only to interpret it as a failure
and try over and over, never succeeding. The built-in call is
ultimately expanded into a zero but that's too late to eliminate code
that depends on it. For example, the following emits the call to abort
that's never executed.
char a[2];
void f (void)
{
if (__builtin_object_size (a + 2, 2) != 0)
__builtin_abort ();
}
With the change, compute_builtin_object_size is called just once and
the call to abort is not emitted.
The initial version of the test verified this folding but adding code
to work around the built-ins various idiosyncrasies inadvertently wound
up removing it. The attached patch adds a new test to exercise this
folding.
2) As a result of a small change to the conditional that controls
the main algorithm of the compute_builtin_object_size function
it changes the depth of its indentation (without actually
changing any of the code there).
If you've done lots of redindentation, then additionally diff -upb would be
appreciated.
Sure. The attached diff was created without regard to whitespace.
Martin
PR tree-optimization/71831 - __builtin_object_size poor results with no
optimization
gcc/testsuite/ChangeLog:
2016-08-21 Martin Sebor <mse...@redhat.com>
PR tree-optimization/71831
* gcc.dg/builtin-object-size-16.c: New test.
* gcc.dg/builtin-object-size-17.c: New test.
gcc/ChangeLog:
2016-08-21 Martin Sebor <mse...@redhat.com>
PR tree-optimization/71831
* tree-object-size.h: Return bool instead of the size and add
argument for the size.
* tree-object-size.c (compute_object_offset): Update signature.
(addr_object_size): Same.
(compute_builtin_object_size): Return bool instead of the size
and add argument for the size. Handle POINTER_PLUS_EXPR when
optimization is disabled.
(expr_object_size): Adjust.
(plus_stmt_object_size): Adjust.
(pass_object_sizes::execute): Adjust.
* builtins.c (fold_builtin_object_size): Adjust.
* doc/extend.texi (Object Size Checking): Update.
* ubsan.c (instrument_object_size): Adjust.
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 03a0dc8..5d0c1af 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -9610,7 +9610,7 @@ fold_builtin_object_size (tree ptr, tree ost)
if (TREE_CODE (ptr) == ADDR_EXPR)
{
- bytes = compute_builtin_object_size (ptr, object_size_type);
+ compute_builtin_object_size (ptr, object_size_type, &bytes);
if (wi::fits_to_tree_p (bytes, size_type_node))
return build_int_cstu (size_type_node, bytes);
}
@@ -9619,8 +9619,7 @@ fold_builtin_object_size (tree ptr, tree ost)
/* If object size is not known yet, delay folding until
later. Maybe subsequent passes will help determining
it. */
- bytes = compute_builtin_object_size (ptr, object_size_type);
- if (bytes != (unsigned HOST_WIDE_INT) (object_size_type < 2 ? -1 : 0)
+ if (compute_builtin_object_size (ptr, object_size_type, &bytes)
&& wi::fits_to_tree_p (bytes, size_type_node))
return build_int_cstu (size_type_node, bytes);
}
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 5285e00..d9dc137 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -10009,8 +10009,15 @@ __atomic_store_n(&lockvar, 0, __ATOMIC_RELEASE|__ATOMIC_HLE_RELEASE);
@findex __builtin___fprintf_chk
@findex __builtin___vfprintf_chk
-GCC implements a limited buffer overflow protection mechanism
-that can prevent some buffer overflow attacks.
+GCC implements a limited buffer overflow protection mechanism that can
+prevent some buffer overflow attacks by determining the sizes of objects
+into which data is about to be written and preventing the writes when
+the size isn't sufficient. The built-in functions described below yield
+the best results when used together and when optimization is enabled.
+For example, to detect object sizes across function boundaries or to
+follow pointer assignments through non-trivial control flow they rely
+on various optimization passes enabled with @option{-O2}. However, to
+a limited extent, they can be used without optimization as well.
@deftypefn {Built-in Function} {size_t} __builtin_object_size (void * @var{ptr}, int @var{type})
is a built-in construct that returns a constant number of bytes from
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-16.c b/gcc/testsuite/gcc.dg/builtin-object-size-16.c
new file mode 100644
index 0000000..15721e5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-16.c
@@ -0,0 +1,201 @@
+/* PR 71831 - __builtin_object_size poor results with no optimization
+ Verify that even without optimization __builtin_object_size returns
+ a meaningful result for a subset of simple expressins. In cases
+ where the result could not easily be made to match the one obtained
+ with optimization the built-in was made to fail instead. */
+/* { dg-do run } */
+/* { dg-options "-O0" } */
+
+static int nfails;
+
+#define TEST_FAILURE(line, obj, type, expect, result) \
+ __builtin_printf ("FAIL: line %i: __builtin_object_size(" \
+ #obj ", %i) == %zu, got %zu\n", \
+ line, type, expect, result), ++nfails
+
+#define bos(obj, type) __builtin_object_size (obj, type)
+#define size(obj, n) ((size_t)n == X ? sizeof *obj : (size_t)n)
+
+#define test(expect, type, obj) \
+ do { \
+ if (bos (obj, type) != size (obj, expect)) \
+ TEST_FAILURE (__LINE__, obj, type, size (obj, expect), bos (obj, type)); \
+ } while (0)
+
+#define T(r0, r1, r2, r3, obj) \
+ do { \
+ test (r0, 0, obj); \
+ test (r1, 1, obj); \
+ test (r2, 2, obj); \
+ test (r3, 3, obj); \
+ } while (0)
+
+/* For convenience. Substitute for 'sizeof object' in test cases where
+ the size can vary from target to target. */
+#define X (size_t)0xdeadbeef
+
+/* __builtin_object_size checking results are inconsistent for equivalent
+ expressions (see bug 71831). To avoid having duplicate the inconsistency
+ at -O0 the built-in simply fails. The results hardcoded in this test
+ are those obtained with optimization (for easy comparison) but without
+ optimization the macros below turn them into expected failures . */
+#if __OPTIMIZE__
+# define F0(n) n
+# define F1(n) n
+# define F2(n) n
+# define F3(n) n
+#else
+# define F0(n) -1
+# define F1(n) -1
+# define F2(n) 0
+# define F3(n) 0
+#endif
+
+typedef __SIZE_TYPE__ size_t;
+
+extern char ax[];
+char ax2[]; /* { dg-warning "assumed to have one element" } */
+
+extern char a0[0];
+static char a1[1];
+static char a2[2];
+static char a9[9];
+
+#if __SIZEOF_SHORT__ == 4
+extern short ia0[0];
+static short ia1[1];
+static short ia9[9];
+#elif __SIZEOF_INT__ == 4
+extern int ia0[0];
+static int ia1[1];
+static int ia9[9];
+#endif
+
+static char a2x2[2][2];
+static char a3x5[3][5];
+
+struct Sx { char n, a[]; } sx;
+struct S0 { char n, a[0]; } s0;
+struct S1 { char n, a[1]; } s1;
+struct S2 { char n, a[2]; } s2;
+struct S9 { char n, a[9]; } s9;
+
+struct S2x2 { char n, a[2][2]; } s2x2;
+struct S3x5 { char n, a[3][5]; } s3x5;
+
+static __attribute__ ((noclone, noinline)) void
+test_arrays ()
+{
+ T ( -1, -1, 0, 0, ax);
+
+ T ( 0, 0, 0, 0, a0);
+ T ( 1, 1, 1, 1, ax2);
+
+ T ( 1, 1, 1, 1, a1);
+ T ( 2, 2, 2, 2, a2);
+ T ( 9, 9, 9, 9, a9);
+
+ T ( 0, 0, 0, 0, a0);
+ T ( 1, 1, 1, 1, ax2);
+
+ T ( 0, 0, 0, 0, ia0);
+ T ( 4, 4, 4, 4, ia1);
+ T ( 36, 36, 36, 36, ia9);
+
+ /* Not all results for multidimensional arrays make sense (see
+ bug 77293). The expected results below simply reflect those
+ obtained at -O2 (modulo the known limitations at -O1). */
+ T ( 4, 4, 4, 4, a2x2);
+ T ( 4, 4, 4, 4, &a2x2[0]);
+ T ( 4, 2, 4, 2, &a2x2[0][0]);
+ T ( 0, F1 (0), 0, 0, &a2x2 + 1);
+ T ( 2, F1 ( 2), 2, F3 ( 2), &a2x2[0] + 1);
+ T ( 3, F1 ( 1), 3, F3 ( 3), &a2x2[0][0] + 1);
+
+ T ( 15, 15, 15, 15, a3x5);
+ T ( 15, 5, 15, 5, &a3x5[0][0] + 0);
+ T ( 14, F1 ( 4), 14, F3 (14), &a3x5[0][0] + 1);
+
+ T ( 1, 1, 1, 1, a1 + 0);
+ T ( 0, F1 (0), 0, 0, a1 + 1);
+ T ( 0, F1 ( 0), 0, 0, &a1 + 1);
+ /* In the following the offset is out of bounds which makes
+ the expression undefined. Still, verify that the returned
+ size is zero (and not some large number). */
+ T ( 0, F1 (0), 0, 0, a1 + 2);
+
+ T ( 2, 2, 2, 2, a2 + 0);
+ T ( 1, F1 ( 1), 1, F3 ( 1), a2 + 1);
+ T ( 0, F1 ( 0), 0, 0, a2 + 2);
+}
+
+static __attribute__ ((noclone, noinline)) void
+test_structs (struct Sx *psx, struct S0 *ps0, struct S1 *ps1, struct S9 *ps9)
+{
+ /* The expected size of a declared object with a flexible array member
+ is sizeof sx in all __builtin_object_size types. */
+ T ( X, X, X, X, &sx);
+
+ /* The expected size of an unknown object with a flexible array member
+ is unknown in all __builtin_object_size types. */
+ T ( -1, -1, 0, 0, psx);
+
+ /* The expected size of a flexible array member of a declared object
+ is zero. */
+ T ( 0, 0, 0, 0, sx.a);
+
+ /* The expected size of a flexible array member of an unknown object
+ is unknown. */
+ T ( -1, -1, 0, 0, psx->a);
+
+ /* The expected size of a declared object with a zero-length array member
+ is sizeof sx in all __builtin_object_size types. */
+ T ( X, X, X, X, &s0);
+
+ /* The expected size of an unknown object with a zero-length array member
+ is unknown in all __builtin_object_size types. */
+ T ( -1, -1, 0, 0, ps0);
+
+ /* The expected size of a zero-length array member of a declared object
+ is zero. */
+ T ( 0, 0, 0, 0, s0.a);
+
+ /* The expected size of a zero-length array member of an unknown object
+ is unknown. */
+ T ( -1, -1, 0, 0, ps0->a);
+
+ T ( X, X, X, X, &s1);
+ T ( 1, 1, 1, 1, s1.a);
+ T ( 0, F1 (0), 0, 0, s1.a + 1);
+
+ /* GCC treats arrays of all sizes that are the last member of a struct
+ as flexible array members. */
+ T ( -1, -1, 0, 0, ps1);
+ T ( -1, -1, 0, 0, ps1->a);
+ T ( -1, -1, 0, 0, ps1->a + 1);
+
+ T ( X, X, X, X, &s9);
+ T ( 9, 9, 9, 9, s9.a);
+ T ( 9, 9, 9, 9, s9.a + 0);
+ T ( 8, F1 ( 8), 8, F3 ( 8), s9.a + 1);
+ T ( 7, F1 ( 7), 7, F3 ( 7), s9.a + 2);
+ T ( 0, F1 ( 0), 0, F3 ( 0), s9.a + 9);
+
+ /* The following make little sense but see bug 77301. */
+ T ( -1, -1, 0, 0, ps9);
+ T ( -1, -1, 0, 0, ps9->a);
+ T ( -1, -1, 0, 0, ps9->a + 1);
+}
+
+int
+main()
+{
+ test_arrays ();
+
+ test_structs (&sx, &s0, &s1, &s9);
+
+ if (nfails)
+ __builtin_abort ();
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-17.c b/gcc/testsuite/gcc.dg/builtin-object-size-17.c
new file mode 100644
index 0000000..03664d5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-17.c
@@ -0,0 +1,158 @@
+/* PR 71831 - __builtin_object_size poor results with no optimization
+ Verify that even without optimization __builtin_object_size result
+ is folded into a constant and dead code that depends on it is
+ eliminated. */
+/* { dg-do compile } */
+/* { dg-options "-O0 -fdump-tree-ssa" } */
+
+#define concat(a, b) a ## b
+#define CAT(a, b) concat (a, b)
+
+/* Create a symbol name unique to each tes and object size type. */
+#define SYM(type) CAT (CAT (CAT (failure_on_line_, __LINE__), _type_), type)
+
+/* References to the following undefined symbol which is unique for each
+ test case are expected to be eliminated. */
+#define TEST_FAILURE(type) \
+ do { \
+ extern void SYM (type)(void); \
+ SYM (type)(); \
+ } while (0)
+
+#define bos(obj, type) __builtin_object_size (obj, type)
+#define size(obj, n) ((size_t)n == X ? sizeof *obj : (size_t)n)
+
+#define test(expect, type, obj) \
+ do { \
+ if (bos (obj, type) != size (obj, expect)) \
+ TEST_FAILURE (type); \
+ } while (0)
+
+#define FOLD_ALL(r0, r1, r2, r3, obj) \
+ do { \
+ test (r0, 0, obj); \
+ test (r1, 1, obj); \
+ test (r2, 2, obj); \
+ test (r3, 3, obj); \
+ } while (0)
+
+#define FOLD_0_2(r0, r1, r2, r3, obj) \
+ do { \
+ test (r0, 0, obj); \
+ test (r2, 2, obj); \
+ } while (0)
+
+/* For convenience. Substitute for 'sizeof object' in test cases where
+ the size can vary from target to target. */
+#define X (size_t)0xdeadbeef
+
+typedef __SIZE_TYPE__ size_t;
+
+extern char ax[];
+char ax2[]; /* { dg-warning "assumed to have one element" } */
+
+extern char a0[0];
+static char a1[1];
+static char a2[2];
+static char a9[9];
+
+#if __SIZEOF_SHORT__ == 4
+extern short ia0[0];
+static short ia1[1];
+static short ia9[9];
+#elif __SIZEOF_INT__ == 4
+extern int ia0[0];
+static int ia1[1];
+static int ia9[9];
+#endif
+
+static char a2x2[2][2];
+static char a3x5[3][5];
+
+struct Sx { char n, a[]; } sx;
+struct S0 { char n, a[0]; } s0;
+struct S1 { char n, a[1]; } s1;
+struct S2 { char n, a[2]; } s2;
+struct S9 { char n, a[9]; } s9;
+
+struct S2x2 { char n, a[2][2]; } s2x2;
+struct S3x5 { char n, a[3][5]; } s3x5;
+
+static __attribute__ ((noclone, noinline)) void
+test_arrays ()
+{
+ FOLD_ALL ( 1, 1, 1, 1, ax2);
+
+ FOLD_ALL ( 1, 1, 1, 1, a1);
+ FOLD_ALL ( 2, 2, 2, 2, a2);
+ FOLD_ALL ( 9, 9, 9, 9, a9);
+
+ FOLD_ALL ( 0, 0, 0, 0, a0);
+ FOLD_ALL ( 1, 1, 1, 1, ax2);
+
+ FOLD_ALL ( 0, 0, 0, 0, ia0);
+ FOLD_ALL ( 4, 4, 4, 4, ia1);
+ FOLD_ALL ( 36, 36, 36, 36, ia9);
+
+ /* Not all results for multidimensional arrays make sense (see
+ bug 77293). The expected results below simply reflect those
+ obtained at -O2 (modulo the known limitations at -O1). */
+ FOLD_ALL ( 4, 4, 4, 4, a2x2);
+ FOLD_ALL ( 4, 4, 4, 4, &a2x2[0]);
+ FOLD_ALL ( 4, 2, 4, 2, &a2x2[0][0]);
+ FOLD_0_2 ( 0, F1 (0), 0, 0, &a2x2 + 1);
+ FOLD_0_2 ( 2, F1 ( 2), 2, F3 ( 2), &a2x2[0] + 1);
+ FOLD_0_2 ( 3, F1 ( 1), 3, F3 ( 3), &a2x2[0][0] + 1);
+
+ FOLD_ALL ( 15, 15, 15, 15, a3x5);
+ FOLD_ALL ( 15, 5, 15, 5, &a3x5[0][0] + 0);
+ FOLD_0_2 ( 14, F1 ( 4), 14, F3 (14), &a3x5[0][0] + 1);
+
+ FOLD_ALL ( 1, 1, 1, 1, a1 + 0);
+ FOLD_0_2 ( 0, F1 ( 0), 0, 0, &a1 + 1);
+ FOLD_ALL ( 2, 2, 2, 2, a2 + 0);
+ FOLD_0_2 ( 1, F1 ( 1), 1, F3 ( 1), a2 + 1);
+ FOLD_0_2 ( 0, F1 ( 0), 0, 0, a2 + 2);
+}
+
+static __attribute__ ((noclone, noinline)) void
+test_structs (void)
+{
+ /* The expected size of a declared object with a flexible array member
+ is sizeof sx in all __builtin_object_size types. */
+ FOLD_ALL ( X, X, X, X, &sx);
+
+ /* The expected size of a flexible array member of a declared object
+ is zero. */
+ FOLD_ALL ( 0, 0, 0, 0, sx.a);
+
+ /* The expected size of a declared object with a zero-length array member
+ is sizeof sx in all __builtin_object_size types. */
+ FOLD_ALL ( X, X, X, X, &s0);
+
+ /* The expected size of a zero-length array member of a declared object
+ is zero. */
+ FOLD_ALL ( 0, 0, 0, 0, s0.a);
+
+ FOLD_ALL ( X, X, X, X, &s1);
+ FOLD_ALL ( 1, 1, 1, 1, s1.a);
+ FOLD_0_2 ( 0, F1 (0), 0, 0, s1.a + 1);
+
+ FOLD_ALL ( X, X, X, X, &s9);
+ FOLD_ALL ( 9, 9, 9, 9, s9.a);
+ FOLD_ALL ( 9, 9, 9, 9, s9.a + 0);
+ FOLD_0_2 ( 8, F1 ( 8), 8, F3 ( 8), s9.a + 1);
+ FOLD_0_2 ( 7, F1 ( 7), 7, F3 ( 7), s9.a + 2);
+ FOLD_0_2 ( 0, F1 ( 0), 0, F3 ( 0), s9.a + 9);
+}
+
+int
+main()
+{
+ test_arrays ();
+ test_structs ();
+
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "failure_on_line" "ssa" } } */
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index c088451..1317ad7 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -51,8 +51,8 @@ static const unsigned HOST_WIDE_INT unknown[4] = {
};
static tree compute_object_offset (const_tree, const_tree);
-static unsigned HOST_WIDE_INT addr_object_size (struct object_size_info *,
- const_tree, int);
+static bool addr_object_size (struct object_size_info *,
+ const_tree, int, unsigned HOST_WIDE_INT *);
static unsigned HOST_WIDE_INT alloc_object_size (const gcall *, int);
static tree pass_through_call (const gcall *);
static void collect_object_sizes_for (struct object_size_info *, tree);
@@ -163,14 +163,18 @@ compute_object_offset (const_tree expr, const_tree var)
OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
If unknown, return unknown[object_size_type]. */
-static unsigned HOST_WIDE_INT
+static bool
addr_object_size (struct object_size_info *osi, const_tree ptr,
- int object_size_type)
+ int object_size_type, unsigned HOST_WIDE_INT *psize)
{
tree pt_var, pt_var_size = NULL_TREE, var_size, bytes;
gcc_assert (TREE_CODE (ptr) == ADDR_EXPR);
+ /* Set to unknown and overwrite just before returning if the size
+ could be determined. */
+ *psize = unknown[object_size_type];
+
pt_var = TREE_OPERAND (ptr, 0);
while (handled_component_p (pt_var))
pt_var = TREE_OPERAND (pt_var, 0);
@@ -183,8 +187,8 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
if (!osi || (object_size_type & 1) != 0
|| TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME)
{
- sz = compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
- object_size_type & ~1);
+ compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
+ object_size_type & ~1, &sz);
}
else
{
@@ -224,7 +228,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
< offset_limit)
pt_var_size = TYPE_SIZE_UNIT (TREE_TYPE (pt_var));
else
- return unknown[object_size_type];
+ return false;
if (pt_var != TREE_OPERAND (ptr, 0))
{
@@ -339,7 +343,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
if (var != pt_var)
var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
else if (!pt_var_size)
- return unknown[object_size_type];
+ return false;
else
var_size = pt_var_size;
bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
@@ -369,14 +373,17 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
}
}
else if (!pt_var_size)
- return unknown[object_size_type];
+ return false;
else
bytes = pt_var_size;
if (tree_fits_uhwi_p (bytes))
- return tree_to_uhwi (bytes);
+ {
+ *psize = tree_to_uhwi (bytes);
+ return true;
+ }
- return unknown[object_size_type];
+ return false;
}
@@ -484,24 +491,61 @@ pass_through_call (const gcall *call)
}
-/* Compute __builtin_object_size value for PTR. OBJECT_SIZE_TYPE is the
- second argument from __builtin_object_size. */
+/* Compute __builtin_object_size value for PTR and set *PSIZE to
+ the resulting value. OBJECT_SIZE_TYPE is the second argument
+ to __builtin_object_size. Return true on success and false
+ when the object size could not be determined. */
-unsigned HOST_WIDE_INT
-compute_builtin_object_size (tree ptr, int object_size_type)
+bool
+compute_builtin_object_size (tree ptr, int object_size_type,
+ unsigned HOST_WIDE_INT *psize)
{
gcc_assert (object_size_type >= 0 && object_size_type <= 3);
+ /* Set to unknown and overwrite just before returning if the size
+ could be determined. */
+ *psize = unknown[object_size_type];
+
if (! offset_limit)
init_offset_limit ();
if (TREE_CODE (ptr) == ADDR_EXPR)
- return addr_object_size (NULL, ptr, object_size_type);
+ return addr_object_size (NULL, ptr, object_size_type, psize);
+
+ if (TREE_CODE (ptr) != SSA_NAME
+ || !POINTER_TYPE_P (TREE_TYPE (ptr)))
+ return false;
- if (TREE_CODE (ptr) == SSA_NAME
- && POINTER_TYPE_P (TREE_TYPE (ptr))
- && computed[object_size_type] != NULL)
+ if (computed[object_size_type] == NULL)
{
+ if (optimize || object_size_type & 1)
+ return false;
+
+ /* When not optimizing, rather than failing, make a small effort
+ to determine the object size without the full benefit of
+ the (costly) computation below. */
+ gimple *def = SSA_NAME_DEF_STMT (ptr);
+ if (gimple_code (def) == GIMPLE_ASSIGN)
+ {
+ tree_code code = gimple_assign_rhs_code (def);
+ if (code == POINTER_PLUS_EXPR)
+ {
+ tree offset = gimple_assign_rhs2 (def);
+ ptr = gimple_assign_rhs1 (def);
+
+ if (cst_and_fits_in_hwi (offset)
+ && compute_builtin_object_size (ptr, object_size_type, psize))
+ {
+ /* Return zero when the offset is out of bounds. */
+ unsigned HOST_WIDE_INT off = tree_to_shwi (offset);
+ *psize = off < *psize ? *psize - off : 0;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
if (!bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr)))
{
struct object_size_info osi;
@@ -616,10 +660,8 @@ compute_builtin_object_size (tree ptr, int object_size_type)
BITMAP_FREE (osi.visited);
}
- return object_sizes[object_size_type][SSA_NAME_VERSION (ptr)];
- }
-
- return unknown[object_size_type];
+ *psize = object_sizes[object_size_type][SSA_NAME_VERSION (ptr)];
+ return *psize != unknown[object_size_type];
}
/* Compute object_sizes for PTR, defined to VALUE, which is not an SSA_NAME. */
@@ -643,7 +685,7 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
|| !POINTER_TYPE_P (TREE_TYPE (value)));
if (TREE_CODE (value) == ADDR_EXPR)
- bytes = addr_object_size (osi, value, object_size_type);
+ addr_object_size (osi, value, object_size_type, &bytes);
else
bytes = unknown[object_size_type];
@@ -809,7 +851,7 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
unsigned HOST_WIDE_INT off = tree_to_uhwi (op1);
/* op0 will be ADDR_EXPR here. */
- bytes = addr_object_size (osi, op0, object_size_type);
+ addr_object_size (osi, op0, object_size_type, &bytes);
if (bytes == unknown[object_size_type])
;
else if (off > offset_limit)
@@ -1282,10 +1324,9 @@ pass_object_sizes::execute (function *fun)
&& lhs)
{
tree type = TREE_TYPE (lhs);
- unsigned HOST_WIDE_INT bytes
- = compute_builtin_object_size (ptr, object_size_type);
- if (bytes != (unsigned HOST_WIDE_INT) (object_size_type == 1
- ? -1 : 0)
+ unsigned HOST_WIDE_INT bytes;
+ if (compute_builtin_object_size (ptr, object_size_type,
+ &bytes)
&& wi::fits_to_tree_p (bytes, type))
{
tree tem = make_ssa_name (type);
diff --git a/gcc/tree-object-size.h b/gcc/tree-object-size.h
index 836f6d3..38c3e07 100644
--- a/gcc/tree-object-size.h
+++ b/gcc/tree-object-size.h
@@ -21,6 +21,6 @@ along with GCC; see the file COPYING3. If not see
#define GCC_TREE_OBJECT_SIZE_H
extern void init_object_sizes (void);
-extern unsigned HOST_WIDE_INT compute_builtin_object_size (tree, int);
+extern bool compute_builtin_object_size (tree, int, unsigned HOST_WIDE_INT *);
#endif // GCC_TREE_OBJECT_SIZE_H
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index a118af2..5cbc98d 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -1826,8 +1826,8 @@ instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs)
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 != HOST_WIDE_INT_M1U)
+ unsigned HOST_WIDE_INT size;
+ if (compute_builtin_object_size (base_addr, 0, &size))
sizet = build_int_cst (sizetype, size);
else if (optimize)
{