From 1ff4dbddcf74203a1e16316b18e12f9e1b5085f0 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Tue, 26 Oct 2021 14:40:33 -0600 Subject: Improve/correct detection of overlapping aggregates [PR102238, PR102919]. Resolves: PR tree-optimization/102238 - alias_offset in gimple-ssa-sprintf.c is broken PR tree-optimization/102919 - spurious -Wrestrict warning for sprintf into the same member array as argument plus offset gcc/ChangeLog: PR tree-optimization/102238 PR tree-optimization/102919 * gimple-ssa-sprintf.c (get_string_length): Add an argument. (array_elt_at_offset): Move to pointer-query. (set_aggregate_size_and_offset): New function. (field_at_offset): Move to pointer-query. (get_origin_and_offset): Rename... (get_origin_and_offset_r): this. Add an argument. Make aggregate handling more robust. (get_origin_and_offset): New. (alias_offset): Add an argument. (format_string): Use subobject size determined by get_origin_and_offset. * pointer-query.cc (field_at_offset): Move from gimple-ssa-sprintf.c. Improve/correct handling of aggregates. (array_elt_at_offset): Same. * pointer-query.h (field_at_offset): Declare. (array_elt_at_offset): Declare. gcc/testsuite/ChangeLog: PR tree-optimization/102238 PR tree-optimization/102919 * gcc.dg/tree-ssa/builtin-sprintf-warn-23.c: Remove warnings. * gcc.dg/Wrestrict-23.c: New test. --- gcc/gimple-ssa-sprintf.c | 250 +++++++++++---------- gcc/pointer-query.cc | 164 ++++++++++++++ gcc/pointer-query.h | 9 + gcc/testsuite/gcc.dg/Wrestrict-23.c | 146 ++++++++++++ .../gcc.dg/tree-ssa/builtin-sprintf-warn-23.c | 24 +- 5 files changed, 468 insertions(+), 125 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Wrestrict-23.c (limited to 'gcc') diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 8f42cb5..ec78090 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -2024,8 +2024,8 @@ format_floating (const directive &dir, tree arg, range_query *) Used by the format_string function below. */ static fmtresult -get_string_length (tree str, gimple *stmt, unsigned eltsize, - range_query *query) +get_string_length (tree str, gimple *stmt, unsigned HOST_WIDE_INT max_size, + unsigned eltsize, range_query *query) { if (!str) return fmtresult (); @@ -2065,6 +2065,20 @@ get_string_length (tree str, gimple *stmt, unsigned eltsize, && (!lendata.maxbound || lenmax <= tree_to_uhwi (lendata.maxbound)) && lenmax <= tree_to_uhwi (lendata.maxlen)) { + if (max_size > 0 && max_size < HOST_WIDE_INT_MAX) + { + /* Adjust the conservative unknown/unbounded result if MAX_SIZE + is valid. Set UNLIKELY to maximum in case MAX_SIZE refers + to a subobject. + TODO: This is overly conservative. Set UNLIKELY to the size + of the outermost enclosing declared object. */ + fmtresult res (0, max_size - 1); + res.nonstr = lendata.decl; + res.range.likely = res.range.max; + res.range.unlikely = HOST_WIDE_INT_MAX; + return res; + } + fmtresult res; res.nonstr = lendata.decl; return res; @@ -2203,110 +2217,80 @@ format_character (const directive &dir, tree arg, range_query *query) return res.adjust_for_width_or_precision (dir.width); } -/* Determine the offset *INDEX of the first byte of an array element of - TYPE (possibly recursively) into which the byte offset OFF points. - On success set *INDEX to the offset of the first byte and return type. - Otherwise, if no such element can be found, return null. */ +/* If TYPE is an array or struct or union, increment *FLDOFF by the starting + offset of the member that *OFF point into and set *FLDSIZE to its size + in bytes and decrement *OFF by the same. Otherwise do nothing. */ -static tree -array_elt_at_offset (tree type, HOST_WIDE_INT off, HOST_WIDE_INT *index) +static void +set_aggregate_size_and_offset (tree type, HOST_WIDE_INT *fldoff, + HOST_WIDE_INT *fldsize, HOST_WIDE_INT *off) { - gcc_assert (TREE_CODE (type) == ARRAY_TYPE); - - tree eltype = type; - while (TREE_CODE (TREE_TYPE (eltype)) == ARRAY_TYPE) - eltype = TREE_TYPE (eltype); - - if (TYPE_MODE (TREE_TYPE (eltype)) != TYPE_MODE (char_type_node)) - eltype = TREE_TYPE (eltype); - - if (eltype == type) - { - *index = 0; - return type; - } - - HOST_WIDE_INT typsz = int_size_in_bytes (type); - HOST_WIDE_INT eltsz = int_size_in_bytes (eltype); - if (off < typsz * eltsz) + /* The byte offset of the most basic struct member the byte + offset *OFF corresponds to, or for a (multidimensional) + array member, the byte offset of the array element. */ + if (TREE_CODE (type) == ARRAY_TYPE + && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE) { - *index = (off / eltsz) * eltsz; - return TREE_CODE (eltype) == ARRAY_TYPE ? TREE_TYPE (eltype) : eltype; + HOST_WIDE_INT index = 0, arrsize = 0; + if (array_elt_at_offset (type, *off, &index, &arrsize)) + { + *fldoff += index; + *off -= index; + *fldsize = arrsize; + } } - - return NULL_TREE; -} - -/* Determine the offset *INDEX of the first byte of a struct member of TYPE - (possibly recursively) into which the byte offset OFF points. On success - set *INDEX to the offset of the first byte and return true. Otherwise, - if no such member can be found, return false. */ - -static bool -field_at_offset (tree type, HOST_WIDE_INT off, HOST_WIDE_INT *index) -{ - gcc_assert (RECORD_OR_UNION_TYPE_P (type)); - - for (tree fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld)) + else if (RECORD_OR_UNION_TYPE_P (type)) { - if (TREE_CODE (fld) != FIELD_DECL || DECL_ARTIFICIAL (fld)) - continue; - - tree fldtype = TREE_TYPE (fld); - HOST_WIDE_INT fldoff = int_byte_position (fld); - - /* If the size is not available the field is a flexible array - member. Treat this case as success. */ - tree typesize = TYPE_SIZE_UNIT (fldtype); - HOST_WIDE_INT fldsize = (tree_fits_uhwi_p (typesize) - ? tree_to_uhwi (typesize) - : off); - - if (fldoff + fldsize < off) - continue; - - if (TREE_CODE (fldtype) == ARRAY_TYPE) + HOST_WIDE_INT index = 0; + tree sub = field_at_offset (type, NULL_TREE, *off, &index); + if (sub) { - HOST_WIDE_INT idx = 0; - if (tree ft = array_elt_at_offset (fldtype, off, &idx)) - fldtype = ft; + tree subsize = DECL_SIZE_UNIT (sub); + if (*fldsize < HOST_WIDE_INT_MAX + && subsize + && tree_fits_uhwi_p (subsize)) + *fldsize = tree_to_uhwi (subsize); else - break; - - *index += idx; - fldoff -= idx; - off -= idx; - } - - if (RECORD_OR_UNION_TYPE_P (fldtype)) - { - *index += fldoff; - return field_at_offset (fldtype, off - fldoff, index); + *fldsize = HOST_WIDE_INT_MAX; + *fldoff += index; + *off -= index; } - - *index += fldoff; - return true; } - - return false; } /* For an expression X of pointer type, recursively try to find the same - origin (object or pointer) as Y it references and return such an X. - When X refers to a struct member, set *FLDOFF to the offset of the - member from the beginning of the "most derived" object. */ + origin (object or pointer) as Y it references and return such a Y. + When X refers to an array element or struct member, set *FLDOFF to + the offset of the element or member from the beginning of the "most + derived" object and *FLDSIZE to its size. When nonnull, set *OFF to + the overall offset from the beginning of the object so that + *FLDOFF <= *OFF. */ static tree -get_origin_and_offset (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *off) +get_origin_and_offset_r (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *fldsize, + HOST_WIDE_INT *off) { if (!x) return NULL_TREE; + HOST_WIDE_INT sizebuf = -1; + if (!fldsize) + fldsize = &sizebuf; + + if (DECL_P (x)) + { + /* Set the size if it hasn't been set yet. */ + if (tree size = DECL_SIZE_UNIT (x)) + if (*fldsize < 0 && tree_fits_shwi_p (size)) + *fldsize = tree_to_shwi (size); + return x; + } + switch (TREE_CODE (x)) { case ADDR_EXPR: x = TREE_OPERAND (x, 0); - return get_origin_and_offset (x, fldoff, off); + return get_origin_and_offset_r (x, fldoff, fldsize, off); case ARRAY_REF: { @@ -2326,7 +2310,7 @@ get_origin_and_offset (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *off) *fldoff = idx; x = TREE_OPERAND (x, 0); - return get_origin_and_offset (x, fldoff, NULL); + return get_origin_and_offset_r (x, fldoff, fldsize, nullptr); } case MEM_REF: @@ -2345,32 +2329,19 @@ get_origin_and_offset (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *off) = (TREE_CODE (x) == ADDR_EXPR ? TREE_TYPE (TREE_OPERAND (x, 0)) : TREE_TYPE (TREE_TYPE (x))); - /* The byte offset of the most basic struct member the byte - offset *OFF corresponds to, or for a (multidimensional) - array member, the byte offset of the array element. */ - HOST_WIDE_INT index = 0; - - if ((RECORD_OR_UNION_TYPE_P (xtype) - && field_at_offset (xtype, *off, &index)) - || (TREE_CODE (xtype) == ARRAY_TYPE - && TREE_CODE (TREE_TYPE (xtype)) == ARRAY_TYPE - && array_elt_at_offset (xtype, *off, &index))) - { - *fldoff += index; - *off -= index; - } + set_aggregate_size_and_offset (xtype, fldoff, fldsize, off); } - return get_origin_and_offset (x, fldoff, NULL); + return get_origin_and_offset_r (x, fldoff, fldsize, nullptr); case COMPONENT_REF: { tree fld = TREE_OPERAND (x, 1); *fldoff += int_byte_position (fld); - get_origin_and_offset (fld, fldoff, off); + get_origin_and_offset_r (fld, fldoff, fldsize, off); x = TREE_OPERAND (x, 0); - return get_origin_and_offset (x, fldoff, off); + return get_origin_and_offset_r (x, fldoff, nullptr, off); } case SSA_NAME: @@ -2382,27 +2353,41 @@ get_origin_and_offset (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *off) if (code == ADDR_EXPR) { x = gimple_assign_rhs1 (def); - return get_origin_and_offset (x, fldoff, off); + return get_origin_and_offset_r (x, fldoff, fldsize, off); } if (code == POINTER_PLUS_EXPR) { tree offset = gimple_assign_rhs2 (def); - if (off) - *off = (tree_fits_uhwi_p (offset) - ? tree_to_uhwi (offset) : HOST_WIDE_INT_MAX); + if (off && tree_fits_uhwi_p (offset)) + *off = tree_to_uhwi (offset); x = gimple_assign_rhs1 (def); - return get_origin_and_offset (x, fldoff, NULL); + x = get_origin_and_offset_r (x, fldoff, fldsize, off); + if (off && !tree_fits_uhwi_p (offset)) + *off = HOST_WIDE_INT_MAX; + if (off) + { + tree xtype = TREE_TYPE (x); + set_aggregate_size_and_offset (xtype, fldoff, fldsize, off); + } + return x; } else if (code == VAR_DECL) { x = gimple_assign_rhs1 (def); - return get_origin_and_offset (x, fldoff, off); + return get_origin_and_offset_r (x, fldoff, fldsize, off); } } else if (gimple_nop_p (def) && SSA_NAME_VAR (x)) x = SSA_NAME_VAR (x); + + tree xtype = TREE_TYPE (x); + if (POINTER_TYPE_P (xtype)) + xtype = TREE_TYPE (xtype); + + if (off) + set_aggregate_size_and_offset (xtype, fldoff, fldsize, off); } default: @@ -2412,13 +2397,41 @@ get_origin_and_offset (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *off) return x; } +/* Nonrecursive version of the above. */ + +static tree +get_origin_and_offset (tree x, HOST_WIDE_INT *fldoff, HOST_WIDE_INT *off, + HOST_WIDE_INT *fldsize = nullptr) +{ + HOST_WIDE_INT sizebuf; + if (!fldsize) + fldsize = &sizebuf; + + *fldsize = -1; + + *fldoff = *off = *fldsize = 0; + tree orig = get_origin_and_offset_r (x, fldoff, fldsize, off); + if (!orig) + return NULL_TREE; + + if (!*fldoff && *off == *fldsize) + { + *fldoff = *off; + *off = 0; + } + + return orig; +} + /* If ARG refers to the same (sub)object or array element as described by DST and DST_FLD, return the byte offset into the struct member or - array element referenced by ARG. Otherwise return HOST_WIDE_INT_MIN - to indicate that ARG and DST do not refer to the same object. */ + array element referenced by ARG and set *ARG_SIZE to the size of + the (sub)object. Otherwise return HOST_WIDE_INT_MIN to indicate + that ARG and DST do not refer to the same object. */ static HOST_WIDE_INT -alias_offset (tree arg, tree dst, HOST_WIDE_INT dst_fld) +alias_offset (tree arg, HOST_WIDE_INT *arg_size, + tree dst, HOST_WIDE_INT dst_fld) { /* See if the argument refers to the same base object as the destination of the formatted function call, and if so, try to determine if they @@ -2430,7 +2443,7 @@ alias_offset (tree arg, tree dst, HOST_WIDE_INT dst_fld) to a struct member, see if the members are one and the same. */ HOST_WIDE_INT arg_off = 0, arg_fld = 0; - tree arg_orig = get_origin_and_offset (arg, &arg_fld, &arg_off); + tree arg_orig = get_origin_and_offset (arg, &arg_fld, &arg_off, arg_size); if (arg_orig == dst && arg_fld == dst_fld) return arg_off; @@ -2448,6 +2461,10 @@ format_string (const directive &dir, tree arg, range_query *query) { fmtresult res; + /* The size of the (sub)object ARG refers to. Used to adjust + the conservative get_string_length() result. */ + HOST_WIDE_INT arg_size = 0; + if (warn_restrict) { /* See if ARG might alias the destination of the call with @@ -2455,8 +2472,12 @@ format_string (const directive &dir, tree arg, range_query *query) so that the overlap can be determined for certain later, when the amount of output of the call (including subsequent directives) has been computed. Otherwise, store HWI_MIN. */ - res.dst_offset = alias_offset (arg, dir.info->dst_origin, + res.dst_offset = alias_offset (arg, &arg_size, dir.info->dst_origin, dir.info->dst_field); + if (res.dst_offset >= 0 && res.dst_offset <= arg_size) + arg_size -= res.dst_offset; + else + arg_size = 0; } /* Compute the range the argument's length can be in. */ @@ -2473,7 +2494,8 @@ format_string (const directive &dir, tree arg, range_query *query) gcc_checking_assert (count_by == 2 || count_by == 4); } - fmtresult slen = get_string_length (arg, dir.info->callstmt, count_by, query); + fmtresult slen = + get_string_length (arg, dir.info->callstmt, arg_size, count_by, query); if (slen.range.min == slen.range.max && slen.range.min < HOST_WIDE_INT_MAX) { diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc index 3b9f970..a0e4543 100644 --- a/gcc/pointer-query.cc +++ b/gcc/pointer-query.cc @@ -2194,3 +2194,167 @@ compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */, return size; } + +/* Determine the offset *FLDOFF of the first byte of a struct member + of TYPE (possibly recursively) into which the byte offset OFF points, + starting after the field START_AFTER if it's non-null. On success, + if nonnull, set *FLDOFF to the offset of the first byte, and return + the field decl. If nonnull, set *NEXTOFF to the offset of the next + field (which reflects any padding between the returned field and + the next). Otherwise, if no such member can be found, return null. */ + +tree +field_at_offset (tree type, tree start_after, HOST_WIDE_INT off, + HOST_WIDE_INT *fldoff /* = nullptr */, + HOST_WIDE_INT *nextoff /* = nullptr */) +{ + tree first_fld = TYPE_FIELDS (type); + + HOST_WIDE_INT offbuf = 0, nextbuf = 0; + if (!fldoff) + fldoff = &offbuf; + if (!nextoff) + nextoff = &nextbuf; + + *nextoff = 0; + + /* The field to return. */ + tree last_fld = NULL_TREE; + /* The next field to advance to. */ + tree next_fld = NULL_TREE; + + /* NEXT_FLD's cached offset. */ + HOST_WIDE_INT next_pos = -1; + + for (tree fld = first_fld; fld; fld = next_fld) + { + next_fld = fld; + do + /* Advance to the next relevant data member. */ + next_fld = TREE_CHAIN (next_fld); + while (next_fld + && (TREE_CODE (next_fld) != FIELD_DECL + || DECL_ARTIFICIAL (next_fld))); + + if (TREE_CODE (fld) != FIELD_DECL || DECL_ARTIFICIAL (fld)) + continue; + + if (fld == start_after) + continue; + + tree fldtype = TREE_TYPE (fld); + /* The offset of FLD within its immediately enclosing structure. */ + HOST_WIDE_INT fldpos = next_pos < 0 ? int_byte_position (fld) : next_pos; + + /* If the size is not available the field is a flexible array + member. Treat this case as success. */ + tree typesize = TYPE_SIZE_UNIT (fldtype); + HOST_WIDE_INT fldsize = (tree_fits_uhwi_p (typesize) + ? tree_to_uhwi (typesize) + : off); + + /* If OFF is beyond the end of the current field continue. */ + HOST_WIDE_INT fldend = fldpos + fldsize; + if (fldend < off) + continue; + + if (next_fld) + { + /* If OFF is equal to the offset of the next field continue + to it and skip the array/struct business below. */ + next_pos = int_byte_position (next_fld); + *nextoff = *fldoff + next_pos; + if (*nextoff == off && TREE_CODE (type) != UNION_TYPE) + continue; + } + else + *nextoff = HOST_WIDE_INT_MAX; + + /* OFF refers somewhere into the current field or just past its end, + which could mean it refers to the next field. */ + if (TREE_CODE (fldtype) == ARRAY_TYPE) + { + /* Will be set to the offset of the first byte of the array + element (which may be an array) of FLDTYPE into which + OFF - FLDPOS points (which may be past ELTOFF). */ + HOST_WIDE_INT eltoff = 0; + if (tree ft = array_elt_at_offset (fldtype, off - fldpos, &eltoff)) + fldtype = ft; + else + continue; + + /* Advance the position to include the array element above. + If OFF - FLPOS refers to a member of FLDTYPE, the member + will be determined below. */ + fldpos += eltoff; + } + + *fldoff += fldpos; + + if (TREE_CODE (fldtype) == RECORD_TYPE) + /* Drill down into the current field if it's a struct. */ + fld = field_at_offset (fldtype, start_after, off - fldpos, + fldoff, nextoff); + + last_fld = fld; + + /* Unless the offset is just past the end of the field return it. + Otherwise save it and return it only if the offset of the next + next field is greater (i.e., there is padding between the two) + or if there is no next field. */ + if (off < fldend) + break; + } + + if (*nextoff == HOST_WIDE_INT_MAX && next_fld) + *nextoff = next_pos; + + return last_fld; +} + +/* Determine the offset *ELTOFF of the first byte of the array element + of array ARTYPE into which the byte offset OFF points. On success + set *ELTOFF to the offset of the first byte and return type. + Otherwise, if no such element can be found, return null. */ + +tree +array_elt_at_offset (tree artype, HOST_WIDE_INT off, + HOST_WIDE_INT *eltoff /* = nullptr */, + HOST_WIDE_INT *subar_size /* = nullptr */) +{ + gcc_assert (TREE_CODE (artype) == ARRAY_TYPE); + + HOST_WIDE_INT dummy; + if (!eltoff) + eltoff = &dummy; + if (!subar_size) + subar_size = &dummy; + + tree eltype = artype; + while (TREE_CODE (TREE_TYPE (eltype)) == ARRAY_TYPE) + eltype = TREE_TYPE (eltype); + + tree subartype = eltype; + if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (eltype)) + || TYPE_MODE (TREE_TYPE (eltype)) != TYPE_MODE (char_type_node)) + eltype = TREE_TYPE (eltype); + + *subar_size = int_size_in_bytes (subartype); + + if (eltype == artype) + { + *eltoff = 0; + return artype; + } + + HOST_WIDE_INT artype_size = int_size_in_bytes (artype); + HOST_WIDE_INT eltype_size = int_size_in_bytes (eltype); + + if (off < artype_size)// * eltype_size) + { + *eltoff = (off / eltype_size) * eltype_size; + return TREE_CODE (eltype) == ARRAY_TYPE ? TREE_TYPE (eltype) : eltype; + } + + return NULL_TREE; +} diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h index 96c5001..c8215b6 100644 --- a/gcc/pointer-query.h +++ b/gcc/pointer-query.h @@ -266,4 +266,13 @@ inline tree compute_objsize (tree ptr, int ostype, access_ref *pref) extern tree compute_objsize (tree, int, tree * = nullptr, tree * = nullptr, range_query * = nullptr); +/* Return the field at the constant offset. */ +extern tree field_at_offset (tree, tree, HOST_WIDE_INT, + HOST_WIDE_INT * = nullptr, + HOST_WIDE_INT * = nullptr); +/* Return the array at the constant offset. */ +extern tree array_elt_at_offset (tree, HOST_WIDE_INT, + HOST_WIDE_INT * = nullptr, + HOST_WIDE_INT * = nullptr); + #endif // GCC_POINTER_QUERY_H diff --git a/gcc/testsuite/gcc.dg/Wrestrict-23.c b/gcc/testsuite/gcc.dg/Wrestrict-23.c new file mode 100644 index 0000000..c7a828b --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wrestrict-23.c @@ -0,0 +1,146 @@ +/* PR tree-optimization/102238 - missing -Wrestrict on sprintf formatting + a struct member into enclosing object + { dg-do compile } + { dg-options "-O2 -Wall -Wno-format-overflow" } */ + +extern int sprintf (char*, const char*, ...); + +extern void sink (void*, ...); + +struct A +{ + char a[4]; +}; + +struct B +{ + struct A a1, a2; +}; + +extern struct B eb; + +enum { B_a2_a_off = __builtin_offsetof (struct B, a2.a) }; + + +void test_warn_src_decl_plus (void) +{ + { + char *s = (char*)&eb + B_a2_a_off; + char *d = eb.a2.a; + sprintf (d, "%s", s); // { dg-warning "overlaps" } + } + + { + // If strlen (s) > 0 there is overlap with a[1]. + char *s = (char*)&eb + B_a2_a_off + 1; + char *d = eb.a2.a; + sprintf (d, "%s", s); // { dg-warning "may overlap" } + } + + { + // strlen (s) must be at most 1 so there can be no overlap with a. + char *s = (char*)&eb + B_a2_a_off + 2; + char *d = eb.a2.a; + sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" } + } + + { + // strlen (s) must be at most 0 so there can be no overlap with a. + char *s = (char*)&eb + B_a2_a_off + 3; + char *d = eb.a2.a; + sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" } + } +} + + +void test_warn_src_ptr_plus (struct B *p) +{ + { + char *s = (char*)p + B_a2_a_off; + char *d = p->a2.a; + sprintf (d, "%s", s); // { dg-warning "overlaps" } + } + + { + // If strlen (s) > 0 there is overlap with a[1]. + char *s = (char*)p + B_a2_a_off + 1; + char *d = p->a2.a; + sprintf (d, "%s", s); // { dg-warning "may overlap" } + } + + { + // strlen (s) must be at most 1 so there can be no overlap with a. + char *s = (char*)p + B_a2_a_off + 2; + char *d = p->a2.a; + sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" } + } + + { + // strlen (s) must be at most 0 so there can be no overlap with a. + char *s = (char*)p + B_a2_a_off + 3; + char *d = p->a2.a; + sprintf (d, "%s", s); // { dg-bogus "-Wrestrict" } + } +} + + +void test_warn_dst_decl_plus (void) +{ + { + char *s = eb.a2.a; + char *d = (char*)&eb + B_a2_a_off; + sprintf (d, "%s", s); // { dg-warning "overlaps" } + } + + { + // If strlen (a) > 0 there is overlap with a[1]. + char *s = eb.a2.a; + char *d = (char*)&eb + B_a2_a_off + 1; + sprintf (d, "%s", s); // { dg-warning "may overlap" } + } + + { + // If strlen (a) > 1 there is overlap with a[2]. + char *s = eb.a2.a; + char *d = (char*)&eb + B_a2_a_off + 2; + sprintf (d, "%s", s); // { dg-warning "may overlap" } + } + + { + // If strlen (a) > 2 there is overlap with a[3]. + char *s = eb.a2.a; + char *d = (char*)&eb + B_a2_a_off + 3; + sprintf (d, "%s", s); // { dg-warning "may overlap" } + } +} + + +void test_warn_dst_ptr_plus (struct B *p) +{ + { + char *s = p->a2.a; + char *d = (char*)p + B_a2_a_off; + sprintf (d, "%s", s); // { dg-warning "overlaps" } + } + + { + // If strlen (a) > 0 there is overlap with a[1]. + char *s = p->a2.a; + char *d = (char*)p + B_a2_a_off + 1; + sprintf (d, "%s", s); // { dg-warning "may overlap" } + } + + { + // If strlen (a) > 1 there is overlap with a[2]. + char *s = p->a2.a; + char *d = (char*)p + B_a2_a_off + 2; + sprintf (d, "%s", s); // { dg-warning "may overlap" } + } + + { + // If strlen (a) > 2 there is overlap with a[3]. + char *s = p->a2.a; + char *d = (char*)p + B_a2_a_off + 3; + sprintf (d, "%s", s); // { dg-warning "may overlap" } + } +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-23.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-23.c index 7fb9651..112b08a 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-23.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-23.c @@ -214,12 +214,14 @@ void test_struct_member_array (struct S3 *s3, int i) T (d, "%s", d); /* { dg-warning "overlaps" } */ T (d, "%s", d + 0); /* { dg-warning "overlaps" } */ T (d, "%s", d + 1); /* { dg-warning "may overlap" } */ - T (d, "%s", d + 2); /* { dg-warning "may overlap" } */ + /* Since d below points to char[4], strlen(d + 2) must be at most 1 + and so the call cannot overlap. */ + T (d, "%s", d + 2); T (d, "%s", d + i); /* { dg-warning "may overlap" } */ T (d, "%s", &d[0]); /* { dg-warning "overlaps" } */ T (d, "%s", &d[1]); /* { dg-warning "may overlap" } */ - T (d, "%s", &d[2]); /* { dg-warning "may overlap" } */ + T (d, "%s", &d[2]); T (d, "%s", &d[i]); /* { dg-warning "may overlap" } */ T (d + 0, "%s", d); /* { dg-warning "overlaps" } */ @@ -236,7 +238,7 @@ void test_struct_member_array (struct S3 *s3, int i) T (d, "%s", s); /* { dg-warning "overlaps" } */ T (d, "%s", s + 1); /* { dg-warning "may overlap" } */ - T (d, "%s", s + 2); /* { dg-warning "may overlap" } */ + T (d, "%s", s + 2); T (d, "%s", s + i); /* { dg-warning "may overlap" } */ s = s3->s2_1.s_1.b; @@ -324,7 +326,7 @@ void test_struct_member_array (struct S3 *s3, int i) T (d, "%s", s); /* { dg-warning "overlaps" } */ T (d, "%s", s + 1); /* { dg-warning "may overlap" } */ - T (d, "%s", s + 2); /* { dg-warning "may overlap" } */ + T (d, "%s", s + 2); T (d, "%s", s + i); /* { dg-warning "may overlap" } */ s = s3->s2_2.s_2.a; @@ -368,7 +370,7 @@ void test_struct_member_array (struct S3 *s3, int i) T (d, "%s", s); /* { dg-warning "overlaps" } */ T (d, "%s", s + 1); /* { dg-warning "may overlap" } */ - T (d, "%s", s + 2); /* { dg-warning "may overlap" } */ + T (d, "%s", s + 2); T (d, "%s", s + i); /* { dg-warning "may overlap" } */ s = s3->s2_2.s_2.a; @@ -394,12 +396,12 @@ void test_struct_member_array_array (struct S3 *s3, int i) T (d, "%s", s); /* { dg-warning "overlaps" } */ T (d, "%s", s + 0); /* { dg-warning "overlaps" } */ T (d, "%s", s + 1); /* { dg-warning "may overlap" } */ - T (d, "%s", s + 2); /* { dg-warning "may overlap" } */ + T (d, "%s", s + 2); T (d, "%s", s + i); /* { dg-warning "may overlap" } */ T (d, "%s", &s[0]); /* { dg-warning "overlaps" } */ T (d, "%s", &s[1]); /* { dg-warning "may overlap" } */ - T (d, "%s", &s[2]); /* { dg-warning "may overlap" } */ + T (d, "%s", &s[2]); T (d, "%s", &s[i]); /* { dg-warning "may overlap" } */ T (d + 0, "%s", s); /* { dg-warning "overlaps" } */ @@ -566,12 +568,12 @@ void test_union_member_array (union U *un, int i) T (d, "%s", d); /* { dg-warning "overlaps" } */ T (d, "%s", d + 0); /* { dg-warning "overlaps" } */ T (d, "%s", d + 1); /* { dg-warning "may overlap" } */ - T (d, "%s", d + 2); /* { dg-warning "may overlap" } */ + T (d, "%s", d + 2); T (d, "%s", d + i); /* { dg-warning "may overlap" } */ T (d, "%s", &d[0]); /* { dg-warning "overlaps" } */ T (d, "%s", &d[1]); /* { dg-warning "may overlap" } */ - T (d, "%s", &d[2]); /* { dg-warning "may overlap" } */ + T (d, "%s", &d[2]); T (d, "%s", &d[i]); /* { dg-warning "may overlap" } */ T (d + 0, "%s", d); /* { dg-warning "overlaps" } */ @@ -588,7 +590,7 @@ void test_union_member_array (union U *un, int i) T (d, "%s", s); /* { dg-warning "overlaps" } */ T (d, "%s", s + 1); /* { dg-warning "may overlap" } */ - T (d, "%s", s + 2); /* { dg-warning "may overlap" } */ + T (d, "%s", s + 2); T (d, "%s", s + i); /* { dg-warning "may overlap" } */ s = un->s2_1.s_1.b; @@ -616,7 +618,7 @@ void test_union_member_array (union U *un, int i) T (d, "%s", s); /* { dg-warning "overlaps" } */ T (d, "%s", s + 1); /* { dg-warning "may overlap" } */ - T (d, "%s", s + 2); /* { dg-warning "may overlap" } */ + T (d, "%s", s + 2); T (d, "%s", s + i); /* { dg-warning "may overlap" } */ s = un->s2_2.s_1.b; -- cgit v1.1