@@ -10536,12 +10536,31 @@ c_common_to_target_charset (HOST_WIDE_INT c)
/* Fold an offsetof-like expression. EXPR is a nested sequence of component
references with an INDIRECT_REF of a constant at the bottom; much like the
- traditional rendering of offsetof as a macro. Return the folded result. */
+ traditional rendering of offsetof as a macro. Return the folded result.
+ PCTX, which is initially null, is set by the first recursive call of
+ the function to refer to a local object describing the potentially out-
+ of-bounds index of the array member whose offset is being computed, and
+ to indicate whether all indices to the same array object have the highest
+ valid value. The function issues a warning for out-of-bounds array indices
+ that either refer to elements past the one just past end of the array object
+ or that exceed any of the major bounds. */
+
+struct offsetof_ctx_t
+{
+ tree inx; /* The invalid array index or NULL_TREE. */
+ int maxinx; /* All indices to the array have the highest valid value. */
+};
tree
-fold_offsetof_1 (tree expr)
+fold_offsetof_1 (tree expr, offsetof_ctx_t *pctx /* = 0 */)
{
tree base, off, t;
+ offsetof_ctx_t ctx = { NULL_TREE, -1 };
+
+ /* Set the context pointer to point to the local context object
+ to use by subsequent recursive calls. */
+ if (!pctx)
+ pctx = &ctx;
switch (TREE_CODE (expr))
{
@@ -10567,10 +10586,19 @@ fold_offsetof_1 (tree expr)
return TREE_OPERAND (expr, 0);
case COMPONENT_REF:
- base = fold_offsetof_1 (TREE_OPERAND (expr, 0));
+ base = fold_offsetof_1 (TREE_OPERAND (expr, 0), pctx);
if (base == error_mark_node)
return base;
+ if (ctx.inx != NULL_TREE) {
+ warning (OPT_Warray_bounds,
+ "index %E denotes an offset "
+ "greater than size of %qT",
+ ctx.inx, TREE_TYPE (TREE_OPERAND (expr, 0)));
+ /* Reset to avoid diagnosing the same expression multiple times. */
+ pctx->inx = NULL_TREE;
+ }
+
t = TREE_OPERAND (expr, 1);
if (DECL_C_BIT_FIELD (t))
{
@@ -10581,10 +10609,11 @@ fold_offsetof_1 (tree expr)
off = size_binop_loc (input_location, PLUS_EXPR, DECL_FIELD_OFFSET (t),
size_int (tree_to_uhwi (DECL_FIELD_BIT_OFFSET (t))
/ BITS_PER_UNIT));
+ pctx->maxinx = -1;
break;
case ARRAY_REF:
- base = fold_offsetof_1 (TREE_OPERAND (expr, 0));
+ base = fold_offsetof_1 (TREE_OPERAND (expr, 0), pctx);
if (base == error_mark_node)
return base;
@@ -10601,8 +10630,10 @@ fold_offsetof_1 (tree expr)
{
upbound = size_binop (PLUS_EXPR, upbound,
build_int_cst (TREE_TYPE (upbound), 1));
- if (tree_int_cst_lt (upbound, t))
- {
+
+ if (tree_int_cst_lt (upbound, t)) {
+ pctx->inx = t;
+
tree v;
for (v = TREE_OPERAND (expr, 0);
@@ -10622,25 +10653,61 @@ fold_offsetof_1 (tree expr)
/* Don't warn if the array might be considered a poor
man's flexible array member with a very permissive
definition thereof. */
- if (TREE_CODE (v) == ARRAY_REF
- || TREE_CODE (v) == COMPONENT_REF)
+ if (TREE_CODE (v) != ARRAY_REF
+ && TREE_CODE (v) != COMPONENT_REF)
+ pctx = NULL;
+ }
+ else if (pctx->inx == NULL_TREE && tree_int_cst_equal (upbound, t))
+ {
+ /* Index is considered valid when it's either less than
+ the upper bound or equal to it and refers to the lowest
+ rank. Since in the latter case it may not at this point
+ in the recursive call to the function be known whether
+ the element at the index is used to do more than to
+ compute its offset (e.g., it can be used to designate
+ a member of the type to which the just past-the-end
+ element refers), point the INX variable at the index
+ and leave it up to the caller to decide whether or not
+ to diagnose it. Special handling is required for minor
+ index values referring to the element just past the end
+ of the array object. */
+ pctx->inx = t;
+ tree_code lastcode = TREE_CODE (TREE_OPERAND (expr, 0));
+ if ((lastcode != ARRAY_REF && pctx != &ctx)
+ || (pctx == &ctx && pctx->maxinx))
+ pctx = NULL;
+ }
+ else
+ {
+ HOST_WIDE_INT ubi = tree_to_shwi (upbound);
+ HOST_WIDE_INT inx = tree_to_shwi (t);
+
+ if (pctx->maxinx)
+ pctx->maxinx = inx + 1 == ubi;
+ }
+ }
+ }
+
+ if (pctx && pctx->inx)
+ {
warning (OPT_Warray_bounds,
"index %E denotes an offset "
"greater than size of %qT",
- t, TREE_TYPE (TREE_OPERAND (expr, 0)));
- }
- }
+ pctx->inx, TREE_TYPE (TREE_OPERAND (expr, 0)));
+
+ pctx->inx = NULL_TREE;
}
t = convert (sizetype, t);
off = size_binop (MULT_EXPR, TYPE_SIZE_UNIT (TREE_TYPE (expr)), t);
+
break;
case COMPOUND_EXPR:
/* Handle static members of volatile structs. */
t = TREE_OPERAND (expr, 1);
gcc_assert (VAR_P (t));
- return fold_offsetof_1 (t);
+ return fold_offsetof_1 (t, pctx);
default:
gcc_unreachable ();
@@ -1015,7 +1015,8 @@ extern bool c_dump_tree (void *, tree);
extern void verify_sequence_points (tree);
-extern tree fold_offsetof_1 (tree);
+struct offsetof_ctx_t;
+extern tree fold_offsetof_1 (tree, offsetof_ctx_t* = NULL);
extern tree fold_offsetof (tree);
/* Places where an lvalue, or modifiable lvalue, may be required.
new file mode 100644
@@ -0,0 +1,157 @@
+// { dg-options "-Warray-bounds" }
+// { dg-do compile }
+
+// Test case exercising pr c/67882 - surprising offsetof result
+// on an invalid array member without diagnostic.
+
+typedef struct A {
+ char a1_1[1][1];
+ char a[];
+} A;
+
+typedef struct A2 {
+ char a1_1[1][1];
+ char c;
+} A2;
+
+typedef struct B {
+ A2 a2_3[2][3];
+ char a1_1[3][5];
+ char a[];
+} B;
+
+// Structures with members that contain flexible array members are
+// an extension accepted by GCC.
+typedef struct C {
+ A a5_7 [5][7];
+ int a;
+} C;
+
+// Structs with a "fake" flexible array member (a GCC extension).
+typedef struct FA0 {
+ int i;
+ char a0 [0];
+} FA0;
+
+typedef struct FA1 {
+ int i;
+ char a1 [1];
+} FA1;
+
+typedef struct FA3 {
+ int i;
+ char a3 [3];
+} FA3;
+
+// A "fake" multidimensional flexible array member deserves special
+// treatment since the offsetof exression yields the same result for
+// references to apparently distinct members (e.g., a5_7[0][7] is
+// at the same offset as a5_7[1][0] which may be surprising).
+typedef struct FA5_7 {
+ int i;
+ char a5_7 [5][7];
+} FA5_7;
+
+static void test (void)
+{
+ // Verify that offsetof references to array elements past the end of
+ // the array member are diagnosed. As an extension, permit references
+ // to the element just past-the-end of the array.
+
+ int a[] = {
+ __builtin_offsetof (A, a), // valid
+ __builtin_offsetof (A, a [0]), // valid
+ __builtin_offsetof (A, a [1]), // valid
+ __builtin_offsetof (A, a [99]), // valid
+
+ __builtin_offsetof (A, a1_1 [0][0]), // valid
+
+ // The following expression is silently accepted as an extension
+ // because it simply forms the equivalent of a just-past-the-end
+ // address.
+ __builtin_offsetof (A, a1_1 [0][1]), // extension
+
+ __builtin_offsetof (A, a1_1 [1][0]), // { dg-warning "index" }
+ __builtin_offsetof (A, a1_1 [1][1]), // { dg-warning "index" }
+
+ __builtin_offsetof (B, a2_3 [0][0].c), // valid
+ __builtin_offsetof (B, a2_3 [0][0].a1_1 [0][0]), // valid
+
+ // The following expressions are silently accepted as an extension
+ // because they simply form the equivalent of a just-past-the-end
+ // address.
+ __builtin_offsetof (B, a2_3 [1][3]), // extension
+ __builtin_offsetof (B, a2_3 [0][0].a1_1 [0][1]), // extension
+
+ __builtin_offsetof (B, a2_3 [0][0].a1_1 [1][0]), // { dg-warning "index" }
+ __builtin_offsetof (B, a2_3 [0][0].a1_1 [1][1]), // { dg-warning "index" }
+
+ __builtin_offsetof (B, a2_3 [1][2].a1_1 [0][0]), // valid
+
+ // Forming an offset to the just-past-end element is accepted.
+ __builtin_offsetof (B, a2_3 [1][2].a1_1 [0][1]), // extension
+ __builtin_offsetof (B, a2_3 [1][2].a1_1 [1][0]), // { dg-warning "index" }
+ __builtin_offsetof (B, a2_3 [1][2].a1_1 [1][1]), // { dg-warning "index" }
+
+ // Forming an offset to the just-past-end element is accepted.
+ __builtin_offsetof (B, a2_3 [1][3]), // exension
+ // ...but these are diagnosed because they dereference a just-path-the-end
+ // element.
+ __builtin_offsetof (B, a2_3 [1][3].a1_1 [0][0]), // { dg-warning "index" }
+ __builtin_offsetof (B, a2_3 [1][3].a1_1 [0][0]), // { dg-warning "index" }
+ __builtin_offsetof (B, a2_3 [1][3].a1_1 [0][1]), // { dg-warning "index" }
+ __builtin_offsetof (B, a2_3 [1][3].a1_1 [1][0]), // { dg-warning "index" }
+ __builtin_offsetof (B, a2_3 [1][3].a1_1 [1][1]), // { dg-warning "index" }
+
+ // Analogous to the case above, these are both diagnosed because they
+ // dereference just-path-the-end elements of the a2_3 array.
+ __builtin_offsetof (B, a2_3 [1][3].c), // { dg-warning "index" }
+ __builtin_offsetof (B, a2_3 [1][3].c), // { dg-warning "index" }
+
+ // The following are all invalid because of the reference to a2_3[2].
+ __builtin_offsetof (B, a2_3 [2][0].a1_1 [0][0]), // { dg-warning "index" }
+ __builtin_offsetof (B, a2_3 [2][0].a1_1 [0][1]), // { dg-warning "index" }
+ __builtin_offsetof (B, a2_3 [2][0].a1_1 [1][0]), // { dg-warning "index" }
+ __builtin_offsetof (B, a2_3 [2][0].a1_1 [1][1]), // { dg-warning "index" }
+ __builtin_offsetof (B, a2_3 [2][0].c), // { dg-warning "index" }
+
+ __builtin_offsetof (C, a5_7 [4][6]),
+ __builtin_offsetof (C, a5_7 [4][6].a),
+ __builtin_offsetof (C, a5_7 [4][6].a [0]),
+ __builtin_offsetof (C, a5_7 [4][6].a [99]),
+
+ __builtin_offsetof (C, a5_7 [4][7]), // extension
+ // Diagnose the following even though the object whose offset is
+ // computed is a flexible array member.
+ __builtin_offsetof (C, a5_7 [4][7].a), // { dg-warning "index" }
+ __builtin_offsetof (C, a5_7 [4][7].a [0]), // { dg-warning "index" }
+ __builtin_offsetof (C, a5_7 [4][7].a [99]), // { dg-warning "index" }
+
+ // Verify that no diagnostic is issued for offsetof expressions
+ // involving structs where the array has a rank of 1 and is the last
+ // member (e.g., those are treated as flexible array members).
+ __builtin_offsetof (FA0, a0 [0]),
+ __builtin_offsetof (FA0, a0 [1]),
+ __builtin_offsetof (FA0, a0 [99]),
+
+ __builtin_offsetof (FA1, a1 [0]),
+ __builtin_offsetof (FA1, a1 [1]),
+ __builtin_offsetof (FA1, a1 [99]),
+
+ __builtin_offsetof (FA3, a3 [0]),
+ __builtin_offsetof (FA3, a3 [3]),
+ __builtin_offsetof (FA3, a3 [99]),
+
+ __builtin_offsetof (FA5_7, a5_7 [0][0]),
+
+ // Unlike one-dimensional arrays, verify that out-of-bounds references
+ // to arrays with rank of 2 and greater are diagnosed.
+ __builtin_offsetof (FA5_7, a5_7 [0][7]), // { dg-warning "index" }
+ __builtin_offsetof (FA5_7, a5_7 [1][7]), // { dg-warning "index" }
+ __builtin_offsetof (FA5_7, a5_7 [5][0]), // { dg-warning "index" }
+ __builtin_offsetof (FA5_7, a5_7 [5][7]), // { dg-warning "index" }
+ __builtin_offsetof (FA5_7, a5_7 [99][99]) // { dg-warning "index" }
+ };
+
+ (void)&a;
+}