MEM_REF cast of a subobject to its containing object has negative offsets, which objsz sees as an invalid access. Support this use case by peeking into the structure to validate that the containing object indeed contains a type of the subobject at that offset and if present, adjust the wholesize for the object to allow the negative offset.
gcc/ChangeLog: PR tree-optimization/120780 * tree-object-size.cc (inner_at_offset, get_wholesize_for_memref): New functions. (addr_object_size): Call GET_WHOLESIZE_FOR_MEMREF. gcc/testsuite/ChangeLog: PR tree-optimization/120780 * gcc.dg/builtin-dynamic-object-size-pr120780.c: New test case. Signed-off-by: Siddhesh Poyarekar <siddh...@gotplt.org> --- .../builtin-dynamic-object-size-pr120780.c | 233 ++++++++++++++++++ gcc/tree-object-size.cc | 87 ++++++- 2 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-pr120780.c diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-pr120780.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-pr120780.c new file mode 100644 index 00000000000..0d6593ec828 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-pr120780.c @@ -0,0 +1,233 @@ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#include "builtin-object-size-common.h" +typedef __SIZE_TYPE__ size_t; +#define NUM_MCAST_RATE 6 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +struct inner +{ + int dummy[4]; +}; + +struct container +{ + int mcast_rate[NUM_MCAST_RATE]; + struct inner mesh; +}; + +static void +test1_child (struct inner *ifmsh, size_t expected) +{ + struct container *sdata = + (struct container *) ((void *) ifmsh + - __builtin_offsetof (struct container, mesh)); + + if (__builtin_dynamic_object_size (sdata->mcast_rate, 1) + != sizeof (sdata->mcast_rate)) + FAIL (); + + if (__builtin_dynamic_object_size (&sdata->mesh, 1) != expected) + FAIL (); +} + +void +__attribute__((noinline)) +test1 (size_t sz) +{ + struct container *sdata = __builtin_malloc (sz); + struct inner *ifmsh = &sdata->mesh; + + test1_child (ifmsh, + (sz > sizeof (sdata->mcast_rate) + ? sz - sizeof (sdata->mcast_rate) : 0)); + + __builtin_free (sdata); +} + +struct container2 +{ + int mcast_rate[NUM_MCAST_RATE]; + union + { + int dummy; + double dbl; + struct inner mesh; + } u; +}; + +static void +test2_child (struct inner *ifmsh, size_t sz) +{ + struct container2 *sdata = + (struct container2 *) ((void *) ifmsh + - __builtin_offsetof (struct container2, u.mesh)); + + if (__builtin_dynamic_object_size (sdata->mcast_rate, 1) + != sizeof (sdata->mcast_rate)) + FAIL (); + + size_t diff = sizeof (*sdata) - sz; + size_t expected = MIN(sizeof (double), MAX (sizeof (sdata->u), diff) - diff); + + if (__builtin_dynamic_object_size (&sdata->u.dbl, 1) != expected) + FAIL (); + + expected = MAX (sizeof (sdata->u.mesh), diff) - diff; + if (__builtin_dynamic_object_size (&sdata->u.mesh, 1) != expected) + FAIL (); +} + +void +__attribute__((noinline)) +test2 (size_t sz) +{ + struct container2 *sdata = __builtin_malloc (sz); + struct inner *ifmsh = &sdata->u.mesh; + + test2_child (ifmsh, sz);; + + __builtin_free (sdata); +} + +struct container3 +{ + int mcast_rate[NUM_MCAST_RATE]; + char mesh[8]; +}; + +static void +test3_child (char ifmsh[], size_t expected) +{ + struct container3 *sdata = + (struct container3 *) ((void *) ifmsh + - __builtin_offsetof (struct container3, mesh)); + + if (__builtin_dynamic_object_size (sdata->mcast_rate, 1) + != sizeof (sdata->mcast_rate)) + FAIL (); + + if (__builtin_dynamic_object_size (sdata->mesh, 1) != expected) + FAIL (); +} + +void +__attribute__((noinline)) +test3 (size_t sz) +{ + struct container3 *sdata = __builtin_malloc (sz); + char *ifmsh = sdata->mesh; + size_t diff = sizeof (*sdata) - sz; + + test3_child (ifmsh, MAX(sizeof (sdata->mesh), diff) - diff); + + __builtin_free (sdata); +} + + +struct container4 +{ + int mcast_rate[NUM_MCAST_RATE]; + struct + { + int dummy; + struct inner mesh; + } s; +}; + +static void +test4_child (struct inner *ifmsh, size_t expected) +{ + struct container4 *sdata = + (struct container4 *) ((void *) ifmsh + - __builtin_offsetof (struct container4, s.mesh)); + + + if (__builtin_dynamic_object_size (sdata->mcast_rate, 1) + != sizeof (sdata->mcast_rate)) + FAIL (); + + if (__builtin_dynamic_object_size (&sdata->s.mesh, 1) != expected) + FAIL (); +} + +void +__attribute__((noinline)) +test4 (size_t sz) +{ + struct container4 *sdata = __builtin_malloc (sz); + struct inner *ifmsh = &sdata->s.mesh; + size_t diff = sizeof (*sdata) - sz; + + test4_child (ifmsh, MAX(sizeof (sdata->s.mesh), diff) - diff); + + __builtin_free (sdata); +} + +struct container5 +{ + int mcast_rate[NUM_MCAST_RATE]; + struct + { + int dummy; + struct inner *mesh; + } s; +}; + +static void +test5_child (struct inner **ifmsh, size_t expected) +{ + struct container5 *sdata = + (struct container5 *) ((void *) ifmsh + - __builtin_offsetof (struct container5, s.mesh)); + + + if (__builtin_dynamic_object_size (sdata->mcast_rate, 1) + != sizeof (sdata->mcast_rate)) + FAIL (); + + if (__builtin_dynamic_object_size (&sdata->s.mesh, 1) != expected) + FAIL (); +} + +void +__attribute__((noinline)) +test5 (size_t sz) +{ + struct container5 *sdata = __builtin_malloc (sz); + struct inner **ifmsh = &sdata->s.mesh; + size_t diff = sizeof (*sdata) - sz; + + test5_child (ifmsh, MAX(sizeof (sdata->s.mesh), diff) - diff); + + __builtin_free (sdata); +} + +int +main (size_t sz) +{ + test1 (sizeof (struct container)); + test1 (sizeof (struct container) - sizeof (int)); + test1 (sizeof (int) * NUM_MCAST_RATE - 1); + + test2 (sizeof (struct container2)); + test2 (sizeof (struct container2) - sizeof (int)); + test2 (sizeof (int) * NUM_MCAST_RATE - 1); + + test3 (sizeof (struct container3)); + test3 (sizeof (struct container3) - sizeof (int)); + test3 (sizeof (int) * NUM_MCAST_RATE - 1); + + test4 (sizeof (struct container4)); + test4 (sizeof (struct container4) - sizeof (int)); + test4 (sizeof (int) * NUM_MCAST_RATE - 1); + + test5 (sizeof (struct container5)); + test5 (sizeof (struct container5) - sizeof (int)); + test5 (sizeof (int) * NUM_MCAST_RATE - 1); + + DONE (); +} diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc index f348673ae75..16095a482ec 100644 --- a/gcc/tree-object-size.cc +++ b/gcc/tree-object-size.cc @@ -464,6 +464,90 @@ compute_object_offset (tree expr, const_tree var) return size_binop (code, base, off); } +/* Return true if CONTAINER has a field of type INNER at OFFSET. */ + +static bool +inner_at_offset (tree container, tree inner, tree offset) +{ + gcc_assert (RECORD_OR_UNION_TYPE_P (container)); + + for (tree t = TYPE_FIELDS (container); t; t = DECL_CHAIN (t)) + { + if (TREE_CODE (t) != FIELD_DECL) + continue; + + tree byte_offset = DECL_FIELD_OFFSET (t); + if (TREE_CODE (byte_offset) != INTEGER_CST + || tree_int_cst_lt (offset, byte_offset)) + return false; + + tree bit_offset = size_int (tree_to_uhwi (DECL_FIELD_BIT_OFFSET (t)) + / BITS_PER_UNIT); + byte_offset = size_binop (PLUS_EXPR, byte_offset, bit_offset); + + /* For an array, check the element type, otherwise the actual type. This + deliberately does not support the case of jumping from a pointer to + the middle of an array to its containing struct. */ + tree t_type = TREE_TYPE (t); + if (((TREE_CODE (t_type) == ARRAY_TYPE && TREE_TYPE (t_type) == inner) + || t_type == inner) + && tree_int_cst_equal (byte_offset, offset)) + return true; + + /* Nested structure or union, adjust the expected offset and dive in. */ + if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (t)) + && inner_at_offset (TREE_TYPE (t), inner, + fold_build2 (MINUS_EXPR, sizetype, offset, + byte_offset))) + return true; + } + + return false; +} + +/* For the input MEMREF of type MEMREF_TYPE, look for the presence of a field + of BASE_TYPE at OFFSET and return an adjusted WHOLESIZE if found. */ + +static tree +get_wholesize_for_memref (tree memref, tree wholesize) +{ + tree base = TREE_OPERAND (memref, 0); + tree offset = fold_convert (sizetype, TREE_OPERAND (memref, 1)); + tree memref_type = TREE_TYPE (memref); + tree base_type = TREE_TYPE (base); + + if (POINTER_TYPE_P (base_type)) + base_type = TREE_TYPE ((base_type)); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "wholesize_for_memref: "); + print_generic_expr (dump_file, wholesize, dump_flags); + fprintf (dump_file, ", offset: "); + print_generic_expr (dump_file, offset, dump_flags); + fprintf (dump_file, "\n"); + } + + if (TREE_CODE (offset) != INTEGER_CST + || compare_tree_int (offset, offset_limit) < 0 + || !RECORD_OR_UNION_TYPE_P (memref_type)) + return wholesize; + + offset = fold_build1 (NEGATE_EXPR, sizetype, offset); + + if (inner_at_offset (memref_type, base_type, offset)) + wholesize = size_binop (PLUS_EXPR, wholesize, offset); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " new wholesize: "); + print_generic_expr (dump_file, wholesize, dump_flags); + fprintf (dump_file, "\n"); + } + + return wholesize; +} + /* Returns the size of the object designated by DECL considering its initializer if it either has one or if it would not affect its size, otherwise the size of the object without the initializer when MIN @@ -536,7 +620,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr, { compute_builtin_object_size (TREE_OPERAND (pt_var, 0), object_size_type & ~OST_SUBOBJECT, &sz); - wholesize = sz; + wholesize = get_wholesize_for_memref (pt_var, sz); } else { @@ -548,6 +632,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr, { sz = object_sizes_get (osi, SSA_NAME_VERSION (var)); wholesize = object_sizes_get (osi, SSA_NAME_VERSION (var), true); + wholesize = get_wholesize_for_memref (pt_var, wholesize); } else sz = wholesize = size_unknown (object_size_type); -- 2.49.0