From baad4c48a85a354d2bf1b17e5aff71203c08adea Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Sat, 19 Sep 2020 17:37:05 -0600 Subject: [PATCH] Extend -Wstringop-overflow to detect out-of-bounds accesses to array parameters. gcc/ChangeLog: PR c/50584 * builtins.c (warn_for_access): Add argument. Distinguish between reads and writes. (check_access): Add argument. Distinguish between reads and writes. (gimple_call_alloc_size): Set range even on failure. (gimple_parm_array_size): New function. (compute_objsize): Call it. (check_memop_access): Pass check_access an additional argument. (expand_builtin_memchr, expand_builtin_strcat): Same. (expand_builtin_strcpy, expand_builtin_stpcpy_1): Same. (expand_builtin_stpncpy, check_strncat_sizes): Same. (expand_builtin_strncat, expand_builtin_strncpy): Same. (expand_builtin_memcmp): Same. * builtins.h (compute_objsize): Declare a new overload. (gimple_parm_array_size): Declare. (check_access): Add argument. * calls.c (append_attrname): Simplify. (maybe_warn_rdwr_sizes): Handle internal attribute access. * tree-ssa-uninit.c (maybe_warn_pass_by_reference): Avoid adding quotes. gcc/testsuite/ChangeLog: PR c/50584 * c-c++-common/Wsizeof-pointer-memaccess1.c: Disable new expected warnings. * g++.dg/ext/attr-access.C: Update text of expected warnings. * gcc.dg/Wstringop-overflow-23.c: Same. * gcc.dg/Wstringop-overflow-24.c: Same. * gcc.dg/attr-access-none.c: Same. * gcc.dg/dfp/composite-type.c: Prune expected warnings. * gcc.dg/torture/pr57147-1.c: Add a member to an otherwise empty struct to avoid a warning. * gcc.dg/torture/pr57147-3.c: Same. * gcc.dg/Warray-bounds-30.c: Adjust. * gcc.dg/attr-access-none.c: Same. * gcc.dg/Wstringop-overflow-40.c: New test. * gcc.dg/attr-access-2.c: New test. --- gcc/builtins.c | 314 +++++++++++++++------ gcc/builtins.h | 19 +- gcc/calls.c | 214 ++++++++------ .../c-c++-common/Wsizeof-pointer-memaccess1.c | 4 +- gcc/testsuite/g++.dg/ext/attr-access.C | 12 +- gcc/testsuite/gcc.dg/Warray-bounds-30.c | 3 +- gcc/testsuite/gcc.dg/Wstringop-overflow-23.c | 4 +- gcc/testsuite/gcc.dg/Wstringop-overflow-24.c | 8 +- gcc/testsuite/gcc.dg/Wstringop-overflow-40.c | 120 ++++++++ gcc/testsuite/gcc.dg/attr-access-2.c | 116 ++++++++ gcc/testsuite/gcc.dg/attr-access-none.c | 2 +- gcc/testsuite/gcc.dg/dfp/composite-type.c | 3 + gcc/testsuite/gcc.dg/torture/pr57147-1.c | 4 +- gcc/testsuite/gcc.dg/torture/pr57147-3.c | 3 +- gcc/tree-ssa-uninit.c | 4 +- 15 files changed, 624 insertions(+), 206 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Wstringop-overflow-40.c create mode 100644 gcc/testsuite/gcc.dg/attr-access-2.c diff --git a/gcc/builtins.c b/gcc/builtins.c index 8b9a4a4..45efc1c 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -183,7 +183,6 @@ static void maybe_emit_chk_warning (tree, enum built_in_function); static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function); static void maybe_emit_free_warning (tree); static tree fold_builtin_object_size (tree, tree); -static tree compute_objsize (tree, int, access_ref *, const vr_values * = NULL); static bool get_range (tree, signop, offset_int[2], const vr_values * = NULL); static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1); @@ -3490,51 +3489,146 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func, } /* For an expression EXP issue an access warning controlled by option OPT - with access to a region SLEN bytes in size in the RANGE of sizes. */ + with access to a region SIZE bytes in size in the RANGE of sizes. + WRITE is true for a write access, READ for a read access, neither for + call that may or may not perform an access but for which the range + is expected to valid. + Returns true when a warning has been issued. */ static bool -warn_for_access (location_t loc, tree func, tree exp, tree range[2], - tree slen, bool access) +warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2], + tree size, bool write, bool read) { bool warned = false; - if (access) + if (write && read) + { + if (tree_int_cst_equal (range[0], range[1])) + warned = (func + ? warning_n (loc, opt, tree_to_uhwi (range[0]), + "%K%qD accessing %E byte in a region " + "of size %E", + "%K%qD accessing %E bytes in a region " + "of size %E", + exp, func, range[0], size) + : warning_n (loc, opt, tree_to_uhwi (range[0]), + "%Kaccessing %E byte in a region " + "of size %E", + "%Kaccessing %E bytes in a region " + "of size %E", + exp, range[0], size)); + else if (tree_int_cst_sign_bit (range[1])) + { + /* Avoid printing the upper bound if it's invalid. */ + warned = (func + ? warning_at (loc, opt, + "%K%qD accessing %E or more bytes in " + "a region of size %E", + exp, func, range[0], size) + : warning_at (loc, opt, + "%Kaccessing %E or more bytes in " + "a region of size %E", + exp, range[0], size)); + } + else + warned = (func + ? warning_at (loc, opt, + "%K%qD accessing between %E and %E bytes " + "in a region of size %E", + exp, func, range[0], range[1], + size) + : warning_at (loc, opt, + "%Kaccessing between %E and %E bytes " + "in a region of size %E", + exp, range[0], range[1], + size)); + return warned; + } + + if (write) + { + if (tree_int_cst_equal (range[0], range[1])) + warned = (func + ? warning_n (loc, opt, tree_to_uhwi (range[0]), + "%K%qD writing %E byte into a region " + "of size %E overflows the destination", + "%K%qD writing %E bytes into a region " + "of size %E overflows the destination", + exp, func, range[0], size) + : warning_n (loc, opt, tree_to_uhwi (range[0]), + "%Kwriting %E byte into a region " + "of size %E overflows the destination", + "%Kwriting %E bytes into a region " + "of size %E overflows the destination", + exp, range[0], size)); + else if (tree_int_cst_sign_bit (range[1])) + { + /* Avoid printing the upper bound if it's invalid. */ + warned = (func + ? warning_at (loc, opt, + "%K%qD writing %E or more bytes into " + "a region of size %E overflows " + "the destination", + exp, func, range[0], size) + : warning_at (loc, opt, + "%Kwriting %E or more bytes into " + "a region of size %E overflows " + "the destination", + exp, range[0], size)); + } + else + warned = (func + ? warning_at (loc, opt, + "%K%qD writing between %E and %E bytes " + "into a region of size %E overflows " + "the destination", + exp, func, range[0], range[1], + size) + : warning_at (loc, opt, + "%Kwriting between %E and %E bytes " + "into a region of size %E overflows " + "the destination", + exp, range[0], range[1], + size)); + return warned; + } + + if (read) { if (tree_int_cst_equal (range[0], range[1])) warned = (func ? warning_n (loc, OPT_Wstringop_overread, tree_to_uhwi (range[0]), "%K%qD reading %E byte from a region of size %E", - "%K%qD reading %E bytes from a region of size %E", - exp, func, range[0], slen) + "%K%qD reading %E bytes from a region of size %E", exp, func, range[0], size) : warning_n (loc, OPT_Wstringop_overread, tree_to_uhwi (range[0]), "%Kreading %E byte from a region of size %E", "%Kreading %E bytes from a region of size %E", - exp, range[0], slen)); + exp, range[0], size)); else if (tree_int_cst_sign_bit (range[1])) { /* Avoid printing the upper bound if it's invalid. */ warned = (func ? warning_at (loc, OPT_Wstringop_overread, - "%K%qD reading %E or more bytes from a region " - "of size %E", - exp, func, range[0], slen) + "%K%qD reading %E or more bytes from " + "a region of size %E", + exp, func, range[0], size) : warning_at (loc, OPT_Wstringop_overread, "%Kreading %E or more bytes from a region " "of size %E", - exp, range[0], slen)); + exp, range[0], size)); } else warned = (func ? warning_at (loc, OPT_Wstringop_overread, "%K%qD reading between %E and %E bytes from " "a region of size %E", - exp, func, range[0], range[1], slen) - : warning_at (loc, OPT_Wstringop_overread, - "%Kreading between %E and %E bytes from " + exp, func, range[0], range[1], size) + : warning_at (loc, opt, + "%K reading between %E and %E bytes from " "a region of size %E", - exp, range[0], range[1], slen)); + exp, range[0], range[1], size)); if (warned) TREE_NO_WARNING (exp) = true; @@ -3542,18 +3636,19 @@ warn_for_access (location_t loc, tree func, tree exp, tree range[2], return warned; } - if (tree_int_cst_equal (range[0], range[1])) + if (tree_int_cst_equal (range[0], range[1]) + || tree_int_cst_sign_bit (range[1])) warned = (func ? warning_n (loc, OPT_Wstringop_overread, tree_to_uhwi (range[0]), "%K%qD epecting %E byte in a region of size %E", "%K%qD expecting %E bytes in a region of size %E", - exp, func, range[0], slen) + exp, func, range[0], size) : warning_n (loc, OPT_Wstringop_overread, tree_to_uhwi (range[0]), "%Kexpecting %E byte in a region of size %E", "%Kexpecting %E bytes in a region of size %E", - exp, range[0], slen)); + exp, range[0], size)); else if (tree_int_cst_sign_bit (range[1])) { /* Avoid printing the upper bound if it's invalid. */ @@ -3561,22 +3656,22 @@ warn_for_access (location_t loc, tree func, tree exp, tree range[2], ? warning_at (loc, OPT_Wstringop_overread, "%K%qD expecting %E or more bytes in a region " "of size %E", - exp, func, range[0], slen) + exp, func, range[0], size) : warning_at (loc, OPT_Wstringop_overread, "%Kexpecting %E or more bytes in a region " "of size %E", - exp, range[0], slen)); + exp, range[0], size)); } else warned = (func ? warning_at (loc, OPT_Wstringop_overread, "%K%qD expecting between %E and %E bytes in " "a region of size %E", - exp, func, range[0], range[1], slen) + exp, func, range[0], range[1], size) : warning_at (loc, OPT_Wstringop_overread, "%Kexpectting between %E and %E bytes in " "a region of size %E", - exp, range[0], range[1], slen)); + exp, range[0], range[1], size)); if (warned) TREE_NO_WARNING (exp) = true; @@ -3759,8 +3854,9 @@ get_size_range (tree bound, tree range[2], const offset_int bndrng[2]) When DSTWRITE is null LEN is checked to verify that it doesn't exceed SIZE_MAX. - ACCESS is true for accesses, false for simple size checks in calls - to functions that neither read from nor write to the region. + WRITE is true for write accesses, READ is true for reads. Both are + false for simple size checks in calls to functions that neither read + from nor write to the region. When nonnull, PAD points to a more detailed description of the access. @@ -3857,6 +3953,11 @@ check_access (tree exp, tree dstwrite, get_size_range (dstwrite, range, pad ? pad->dst.bndrng : NULL); tree func = get_callee_fndecl (exp); + /* Read vs write access by built-ins can be determined from the const + qualifiers on the pointer argument. In the absence of attribute + access, non-const qualified pointer arguments to user-defined + functions are assumed to both read and write the objects. */ + const bool builtin = func ? fndecl_built_in_p (func) : false; /* First check the number of bytes to be written against the maximum object size. */ @@ -3913,51 +4014,18 @@ check_access (tree exp, tree dstwrite, "the destination", exp, range[0], dstsize)); } - else if (tree_int_cst_equal (range[0], range[1])) - warned = (func - ? warning_n (loc, OPT_Wstringop_overflow_, - tree_to_uhwi (range[0]), - "%K%qD writing %E byte into a region " - "of size %E overflows the destination", - "%K%qD writing %E bytes into a region " - "of size %E overflows the destination", - exp, func, range[0], dstsize) - : warning_n (loc, OPT_Wstringop_overflow_, - tree_to_uhwi (range[0]), - "%Kwriting %E byte into a region " - "of size %E overflows the destination", - "%Kwriting %E bytes into a region " - "of size %E overflows the destination", - exp, range[0], dstsize)); - else if (tree_int_cst_sign_bit (range[1])) + else { - /* Avoid printing the upper bound if it's invalid. */ - warned = (func - ? warning_at (loc, OPT_Wstringop_overflow_, - "%K%qD writing %E or more bytes into " - "a region of size %E overflows " - "the destination", - exp, func, range[0], dstsize) - : warning_at (loc, OPT_Wstringop_overflow_, - "%Kwriting %E or more bytes into " - "a region of size %E overflows " - "the destination", - exp, range[0], dstsize)); + const bool read + = mode == access_read_only || mode == access_read_write; + const bool write + = mode == access_write_only || mode == access_read_write; + warned = warn_for_access (loc, func, exp, + OPT_Wstringop_overflow_, + range, dstsize, + write, read && !builtin); } - else - warned = (func - ? warning_at (loc, OPT_Wstringop_overflow_, - "%K%qD writing between %E and %E bytes " - "into a region of size %E overflows " - "the destination", - exp, func, range[0], range[1], - dstsize) - : warning_at (loc, OPT_Wstringop_overflow_, - "%Kwriting between %E and %E bytes " - "into a region of size %E overflows " - "the destination", - exp, range[0], range[1], - dstsize)); + if (warned) { TREE_NO_WARNING (exp) = true; @@ -4037,10 +4105,15 @@ check_access (tree exp, tree dstwrite, location_t loc = tree_nonartificial_location (exp); loc = expansion_point_location_if_in_system_header (loc); - if (warn_for_access (loc, func, exp, range, slen, mode) - && pad) - inform_access (pad->src, access_read_only); - + const bool read + = mode == access_read_only || mode == access_read_write; + if (warn_for_access (loc, func, exp, OPT_Wstringop_overread, range, + slen, false, read)) + { + TREE_NO_WARNING (exp) = true; + if (pad) + inform_access (pad->src, access_read_only); + } return false; } @@ -4065,8 +4138,11 @@ check_read_access (tree exp, tree src, tree bound /* = NULL_TREE */, } /* If STMT is a call to an allocation function, returns the constant - size of the object allocated by the call represented as sizetype. - If nonnull, sets RNG1[] to the range of the size. */ + maximum size of the object allocated by the call represented as + sizetype. If nonnull, sets RNG1[] to the range of the size. + When nonnull, uses RVALS for range information, otherwise calls + get_range_info to get it. + Returns null when STMT is not a call to a valid allocation function. */ tree gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */, @@ -4122,8 +4198,14 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */, if (!rng1) rng1 = rng1_buf; + const int prec = ADDR_MAX_PRECISION; + const tree size_max = TYPE_MAX_VALUE (sizetype); if (!get_range (size, rng1, rvals)) - return NULL_TREE; + { + /* Use the full non-negative range on failure. */ + rng1[0] = wi::zero (prec); + rng1[1] = wi::to_wide (size_max, prec); + } if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST) return fold_convert (sizetype, size); @@ -4133,10 +4215,13 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */, tree n = argidx2 < nargs ? gimple_call_arg (stmt, argidx2) : integer_one_node; wide_int rng2[2]; if (!get_range (n, rng2, rvals)) - return NULL_TREE; + { + /* As above, use the full non-negative range on failure. */ + rng2[0] = wi::zero (prec); + rng2[1] = wi::to_wide (size_max, prec); + } /* Extend to the maximum precision to avoid overflow. */ - const int prec = ADDR_MAX_PRECISION; rng1[0] = wide_int::from (rng1[0], prec, UNSIGNED); rng1[1] = wide_int::from (rng1[1], prec, UNSIGNED); rng2[0] = wide_int::from (rng2[0], prec, UNSIGNED); @@ -4146,7 +4231,6 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */, of SIZE_MAX and the product of the upper bounds as a constant. */ rng1[0] = rng1[0] * rng2[0]; rng1[1] = rng1[1] * rng2[1]; - tree size_max = TYPE_MAX_VALUE (sizetype); if (wi::gtu_p (rng1[1], wi::to_wide (size_max, prec))) { rng1[1] = wi::to_wide (size_max); @@ -4156,6 +4240,61 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */, return wide_int_to_tree (sizetype, rng1[1]); } +/* For an access to an object referenced to by the function parameter PTR + of pointer type, and set RNG[] to the range of sizes of the object + obtainedfrom the attribute access specification for the current function. + Return the function parameter on success and null otherwise. */ + +tree +gimple_parm_array_size (tree ptr, wide_int rng[2], + const vr_values * /* = NULL */) +{ + /* For a function argument try to determine the byte size of the array + from the current function declaratation (e.g., attribute access or + related). */ + tree var = SSA_NAME_VAR (ptr); + if (TREE_CODE (var) != PARM_DECL) + return NULL_TREE; + + const unsigned prec = TYPE_PRECISION (sizetype); + + rdwr_map rdwr_idx; + attr_access *access = get_parm_access (rdwr_idx, var); + if (!access) + return NULL_TREE; + + if (access->sizarg != UINT_MAX) + { + /* TODO: Try to extract the range from the argument based on + those of subsequent assertions or based on known calls to + the current function. */ + return NULL_TREE; + } + + if (!access->minsize) + return NULL_TREE; + + /* Only consider ordinary array bound at level 2 (or above if it's + ever added). */ + if (warn_array_parameter < 2 && !access->static_p) + return NULL_TREE; + + rng[0] = wi::zero (prec); + rng[1] = wi::uhwi (access->minsize, prec); + /* If the PTR argument points to an array multiply MINSIZE by the size + of array element type. Otherwise, multiply it by the size of what + the pointer points to. */ + tree eltype = TREE_TYPE (TREE_TYPE (ptr)); + if (TREE_CODE (eltype) == ARRAY_TYPE) + eltype = TREE_TYPE (eltype); + tree size = TYPE_SIZE_UNIT (eltype); + if (!size || TREE_CODE (size) != INTEGER_CST) + return NULL_TREE; + + rng[1] *= wi::to_wide (size, prec); + return var; +} + /* Wrapper around the wide_int overload of get_range. Returns the same result but accepts offset_int instead. */ @@ -4348,6 +4487,21 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, return false; } + if (gimple_nop_p (stmt)) + { + /* For a function argument try to determine the byte size + of the array from the current function declaratation + (e.g., attribute access or related). */ + wide_int wr[2]; + tree ref = gimple_parm_array_size (ptr, wr, rvals); + if (!ref) + return NULL_TREE; + pref->ref = ref; + pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED); + pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED); + return true; + } + /* TODO: Handle PHI. */ if (!is_gimple_assign (stmt)) @@ -4400,7 +4554,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, /* A "public" wrapper around the above. Clients should use this overload instead. */ -static tree +tree compute_objsize (tree ptr, int ostype, access_ref *pref, const vr_values *rvals /* = NULL */) { @@ -4977,7 +5131,6 @@ expand_builtin_stpncpy (tree exp, rtx) /* The size of the destination object. */ tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst); check_access (exp, len, /*maxread=*/len, src, destsize, data.mode, &data); - return NULL_RTX; } @@ -5130,7 +5283,6 @@ expand_builtin_strncat (tree exp, rtx) check_access (exp, /*dstwrite=*/NULL_TREE, maxread, srclen, destsize, data.mode, &data); - return NULL_RTX; } diff --git a/gcc/builtins.h b/gcc/builtins.h index 94ff96b..8136b76 100644 --- a/gcc/builtins.h +++ b/gcc/builtins.h @@ -133,13 +133,6 @@ extern tree fold_call_stmt (gcall *, bool); extern void set_builtin_user_assembler_name (tree decl, const char *asmspec); extern bool is_simple_builtin (tree); extern bool is_inexpensive_builtin (tree); - -class vr_values; -tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL, - const vr_values * = NULL); -extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL, - const vr_values * = NULL); - extern bool readonly_data_expr (tree exp); extern bool init_target_chars (void); extern unsigned HOST_WIDE_INT target_newline; @@ -202,7 +195,15 @@ struct access_data access_mode mode; }; -extern bool check_access (tree, tree, tree, tree, tree, - access_mode, const access_data * = NULL); +class vr_values; +extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL, + const vr_values * = NULL); +extern tree gimple_parm_array_size (tree, wide_int[2], const vr_values * = NULL); +extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL, + const vr_values * = NULL); +extern tree compute_objsize (tree, int, access_ref *, const vr_values * = NULL); + +extern bool check_access (tree, tree, tree, tree, tree, access_mode, + const access_data * = NULL); #endif /* GCC_BUILTINS_H */ diff --git a/gcc/calls.c b/gcc/calls.c index b56069d..0e5c696 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -58,6 +58,8 @@ along with GCC; see the file COPYING3. If not see #include "builtins.h" #include "gimple-fold.h" +#include "tree-pretty-print.h" + /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */ #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT) @@ -1898,36 +1900,20 @@ fntype_argno_type (tree fntype, unsigned argno) return NULL_TREE; } -/* Helper to append the "rdwr" attribute specification described - by ACCESS to the array ATTRSTR with size STRSIZE. Used in +/* Helper to append the "human readable" attribute access specification + described by ACCESS to the array ATTRSTR with size STRSIZE. Used in diagnostics. */ static inline void append_attrname (const std::pair &access, char *attrstr, size_t strsize) { - /* Append the relevant attribute to the string. This (deliberately) - appends the attribute pointer operand even when none was specified. */ - size_t len = strlen (attrstr); - - const char* const atname - = (access.second.mode == access_read_only - ? "read_only" - : (access.second.mode == access_write_only - ? "write_only" - : (access.second.mode == access_read_write - ? "read_write" : "none"))); - - const char *sep = len ? ", " : ""; - - if (access.second.sizarg == UINT_MAX) - snprintf (attrstr + len, strsize - len, - "%s%s (%i)", sep, atname, - access.second.ptrarg + 1); - else - snprintf (attrstr + len, strsize - len, - "%s%s (%i, %i)", sep, atname, - access.second.ptrarg + 1, access.second.sizarg + 1); + if (access.second.internal_p) + return; + + tree str = access.second.to_external_string (); + gcc_assert (strsize >= (size_t) TREE_STRING_LENGTH (str)); + strcpy (attrstr, TREE_STRING_POINTER (str)); } /* Iterate over attribute access read-only, read-write, and write-only @@ -1938,6 +1924,7 @@ static void maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp) { auto_diagnostic_group adg; + bool warned = false; /* A string describing the attributes that the warnings issued by this function apply to. Used to print one informational note per function @@ -1966,35 +1953,40 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp) if (!access.second.ptr) continue; - tree argtype = fntype_argno_type (fntype, ptridx); - argtype = TREE_TYPE (argtype); + tree ptrtype = fntype_argno_type (fntype, ptridx); + tree argtype = TREE_TYPE (ptrtype); - tree size; + /* The size of the access by the call. */ + tree access_size; if (sizidx == -1) { - /* If only the pointer attribute operand was specified - and not size, set SIZE to the size of one element of - the pointed to type to detect smaller objects (null - pointers are diagnosed in this case only if - the pointer is also declared with attribute nonnull. */ - size = size_one_node; + /* If only the pointer attribute operand was specified and + not size, set SIZE to the greater of MINSIZE or size of + one element of the pointed to type to detect smaller + objects (null pointers are diagnosed in this case only + if the pointer is also declared with attribute nonnull. */ + if (access.second.minsize + && access.second.minsize != HOST_WIDE_INT_M1U) + access_size = build_int_cstu (sizetype, access.second.minsize); + else + access_size = size_one_node; } else - size = rwm->get (sizidx)->size; + access_size = rwm->get (sizidx)->size; + bool warned = false; + location_t loc = EXPR_LOCATION (exp); tree ptr = access.second.ptr; tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) }; - if (get_size_range (size, sizrng, true) + if (get_size_range (access_size, sizrng, true) && tree_int_cst_sgn (sizrng[0]) < 0 && tree_int_cst_sgn (sizrng[1]) < 0) { /* Warn about negative sizes. */ - bool warned = false; - location_t loc = EXPR_LOCATION (exp); if (tree_int_cst_equal (sizrng[0], sizrng[1])) warned = warning_at (loc, OPT_Wstringop_overflow_, "%Kargument %i value %E is negative", - exp, sizidx + 1, size); + exp, sizidx + 1, access_size); else warned = warning_at (loc, OPT_Wstringop_overflow_, "%Kargument %i range [%E, %E] is negative", @@ -2011,44 +2003,56 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp) { if (COMPLETE_TYPE_P (argtype)) { - /* Multiple SIZE by the size of the type the pointer - argument points to. If it's incomplete the size - is used as is. */ - size = NULL_TREE; + /* Multiply ACCESS_SIZE by the size of the type the pointer + argument points to. If it's incomplete the size is used + as is. */ + access_size = NULL_TREE; if (tree argsize = TYPE_SIZE_UNIT (argtype)) if (TREE_CODE (argsize) == INTEGER_CST) { const int prec = TYPE_PRECISION (sizetype); wide_int minsize = wi::to_wide (sizrng[0], prec); minsize *= wi::to_wide (argsize, prec); - size = wide_int_to_tree (sizetype, minsize); + access_size = wide_int_to_tree (sizetype, minsize); } } } else - size = NULL_TREE; + access_size = NULL_TREE; - if (sizidx >= 0 - && integer_zerop (ptr) - && tree_int_cst_sgn (sizrng[0]) > 0) + if (integer_zerop (ptr)) { - /* Warn about null pointers with positive sizes. This is - different from also declaring the pointer argument with - attribute nonnull when the function accepts null pointers - only when the corresponding size is zero. */ - bool warned = false; - const location_t loc = EXPR_LOC_OR_LOC (ptr, EXPR_LOCATION (exp)); - if (tree_int_cst_equal (sizrng[0], sizrng[1])) - warned = warning_at (loc, OPT_Wnonnull, - "%Kargument %i is null but the corresponding " - "size argument %i value is %E", - exp, ptridx + 1, sizidx + 1, size); - else - warned = warning_at (loc, OPT_Wnonnull, - "%Kargument %i is null but the corresponding " - "size argument %i range is [%E, %E]", - exp, ptridx + 1, sizidx + 1, - sizrng[0], sizrng[1]); + if (sizidx >= 0 && tree_int_cst_sgn (sizrng[0]) > 0) + { + /* Warn about null pointers with positive sizes. This is + different from also declaring the pointer argument with + attribute nonnull when the function accepts null pointers + only when the corresponding size is zero. */ + if (tree_int_cst_equal (sizrng[0], sizrng[1])) + warned = warning_at (loc, OPT_Wnonnull, + "%Kargument %i is null but " + "the corresponding size argument %i " + "value is %E", + exp, ptridx + 1, sizidx + 1, access_size); + else + warned = warning_at (loc, OPT_Wnonnull, + "%Kargument %i is null but " + "the corresponding size argument %i " + "range is [%E, %E]", + exp, ptridx + 1, sizidx + 1, + sizrng[0], sizrng[1]); + } + else if (access_size && access.second.static_p) + { + /* Warn about null pointers for [static N] array arguments + but do not warn for ordinary (i.e., nonstatic) arrays. */ + warned = warning_at (loc, OPT_Wnonnull, + "%Kargument %i to %<%T[static %E]%> null " + "where non-null expected", + exp, ptridx + 1, argtype, + sizrng[0]); + } + if (warned) { append_attrname (access, attrstr, sizeof attrstr); @@ -2057,18 +2061,20 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp) } } - tree objsize = compute_objsize (ptr, 0); + access_data data (ptr, access.second.mode, NULL_TREE, false, + NULL_TREE, false); + access_ref* const pobj = (access.second.mode == access_write_only + ? &data.dst : &data.src); + tree objsize = compute_objsize (ptr, 1, pobj); - tree srcsize; - if (access.second.mode == access_write_only) + /* The size of the destination or source object. */ + tree dstsize = NULL_TREE, srcsize = NULL_TREE; + if (access.second.mode == access_read_only + || access.second.mode == access_none) { - /* For a write-only argument there is no source. */ - srcsize = NULL_TREE; - } - else - { - /* For read-only and read-write attributes also set the source - size. */ + /* For a read-only argument there is no destination. For + no access, set the source as well and differentiate via + the access flag below. */ srcsize = objsize; if (access.second.mode == access_read_only || access.second.mode == access_none) @@ -2080,31 +2086,53 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp) objsize = NULL_TREE; } } + else + dstsize = objsize; - /* Clear the no-warning bit in case it was set in a prior - iteration so that accesses via different arguments are - diagnosed. */ + /* Clear the no-warning bit in case it was set by check_access + in a prior iteration so that accesses via different arguments + are diagnosed. */ TREE_NO_WARNING (exp) = false; - check_access (exp, size, /*maxread=*/ NULL_TREE, srcsize, objsize, - access.second.mode); + access_mode mode = data.mode; + if (mode == access_deferred) + mode = TYPE_READONLY (argtype) ? access_read_only : access_read_write; + check_access (exp, access_size, /*maxread=*/ NULL_TREE, srcsize, + dstsize, mode, &data); if (TREE_NO_WARNING (exp)) - /* If check_access issued a warning above, append the relevant - attribute to the string. */ - append_attrname (access, attrstr, sizeof attrstr); - } + { + warned = true; - if (!*attrstr) - return; + if (access.second.internal_p) + inform (loc, "referencing argument %u of type %qT", + ptridx + 1, ptrtype); + else + /* If check_access issued a warning above, append the relevant + attribute to the string. */ + append_attrname (access, attrstr, sizeof attrstr); + } + } - if (fndecl) - inform (DECL_SOURCE_LOCATION (fndecl), - "in a call to function %qD declared with attribute %qs", - fndecl, attrstr); - else - inform (EXPR_LOCATION (fndecl), - "in a call with type %qT and attribute %qs", - fntype, attrstr); + if (*attrstr) + { + if (fndecl) + inform (DECL_SOURCE_LOCATION (fndecl), + "in a call to function %qD declared with attribute %qs", + fndecl, attrstr); + else + inform (EXPR_LOCATION (fndecl), + "in a call with type %qT and attribute %qs", + fntype, attrstr); + } + else if (warned) + { + if (fndecl) + inform (DECL_SOURCE_LOCATION (fndecl), + "in a call to function %qD", fndecl); + else + inform (EXPR_LOCATION (fndecl), + "in a call with type %qT", fntype); + } /* Set the bit in case if was cleared and not set above. */ TREE_NO_WARNING (exp) = true; diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c index c4127b8..38e06ba 100644 --- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c +++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c @@ -1,7 +1,7 @@ /* Test -Wsizeof-pointer-memaccess warnings. */ /* { dg-do compile } */ -/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument" } */ -/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */ +/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow" } */ +/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat -Wno-stringop-overflow" { target c } } */ /* { dg-require-effective-target alloca } */ typedef __SIZE_TYPE__ size_t; diff --git a/gcc/testsuite/g++.dg/ext/attr-access.C b/gcc/testsuite/g++.dg/ext/attr-access.C index 3b9c1a3..b7b2a5f 100644 --- a/gcc/testsuite/g++.dg/ext/attr-access.C +++ b/gcc/testsuite/g++.dg/ext/attr-access.C @@ -42,8 +42,8 @@ void call_rdwrp1_rdwrr2_O0 (void) int32_t x[1] = { }; rdwrp1_rdwrr2 (x, x[0]); - rdwrp1_rdwrr2 (x, x[1]); // { dg-warning "writing 4 bytes into a region of size 0" } - rdwrp1_rdwrr2 (x + 1, x[0]); // { dg-warning "writing 4 bytes into a region of size 0" } + rdwrp1_rdwrr2 (x, x[1]); // { dg-warning "accessing 4 bytes in a region of size 0" } + rdwrp1_rdwrr2 (x + 1, x[0]); // { dg-warning "accessing 4 bytes in a region of size 0" } } void call_wop1_wor2_O0 (void) @@ -84,12 +84,12 @@ void call_rdwrp1_rdwrr2_O1 (void) int32_t &r2 = *(int32_t*)((char*)p1 + 1); rdwrp1_rdwrr2 (x, x[0]); - rdwrp1_rdwrr2 (x, x[1]); // { dg-warning "writing 4 bytes into a region of size 0" } - rdwrp1_rdwrr2 (x + 1, x[0]); // { dg-warning "writing 4 bytes into a region of size 0" } + rdwrp1_rdwrr2 (x, x[1]); // { dg-warning "accessing 4 bytes in a region of size 0" } + rdwrp1_rdwrr2 (x + 1, x[0]); // { dg-warning "accessing 4 bytes in a region of size 0" } rdwrp1_rdwrr2 (p0, r0); - rdwrp1_rdwrr2 (p0, r2); // { dg-warning "writing 4 bytes into a region of size 2" } - rdwrp1_rdwrr2 (p1, r0); // { dg-warning "writing 4 bytes into a region of size 3" } + rdwrp1_rdwrr2 (p0, r2); // { dg-warning "accessing 4 bytes in a region of size 2" } + rdwrp1_rdwrr2 (p1, r0); // { dg-warning "accessing 4 bytes in a region of size 3" } } void call_wop1_wor2_O1 (void) diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-30.c b/gcc/testsuite/gcc.dg/Warray-bounds-30.c index b996568..048a95d 100644 --- a/gcc/testsuite/gcc.dg/Warray-bounds-30.c +++ b/gcc/testsuite/gcc.dg/Warray-bounds-30.c @@ -73,8 +73,7 @@ void test_global_int_array (void) T (&p[min]); /* { dg-warning "subscript -\[0-9\]+ is \(below|outside\) array bounds of .int\\\[1]." } */ T (&p[-1]); /* { dg-warning "subscript -1 is \(below|outside\) array bounds of .int\\\[1]." } */ - T (&p[0]); - T (&p[1]); + T (&p[0], &p[1]); T (&p[2]); /* { dg-warning "subscript 2 is \(above|outside\) array bounds of .int\\\[1]." } */ T (&p[max]); /* { dg-warning "subscript \[0-9\]+ is \(above|outside\) array bounds of .int\\\[1]." } */ diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c index 69c71cc..bbc1210 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c @@ -21,7 +21,7 @@ typedef __INT32_TYPE__ int32_t; /* Exercise null pointer detection. */ RDONLY (2, 1) void -rd2_1 (int, const void*); // { dg-message "in a call to function 'rd2_1' declared with attribute 'read_only \\\(2, 1\\\)" } +rd2_1 (int, const void*); // { dg-message "in a call to function 'rd2_1' declared with attribute 'access \\\(read_only, 2, 1\\\)" "note" } void test_rd2_1 (void) { @@ -45,7 +45,7 @@ void test_rd2_1 (void) } WRONLY (3, 1) void -wr3_1 (int, int, void*); // { dg-message "in a call to function 'wr3_1' declared with attribute 'write_only \\\(3, 1\\\)" } +wr3_1 (int, int, void*); // { dg-message "in a call to function 'wr3_1' declared with attribute 'access \\\(write_only, 3, 1\\\)" } void test_wr3_1 (void) { diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c index a21a1f1..049d1c6 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-24.c @@ -23,7 +23,7 @@ extern char d1[1], d2[2], d3[3]; the attribute without a size operand. */ RDONLY (1) void -rd1_int (const int32_t*); // { dg-message "in a call to function 'rd1_int' declared with attribute 'read_only \\\(1\\\)'" } +rd1_int (const int32_t*); // { dg-message "in a call to function 'rd1_int' declared with attribute 'access \\\(read_only, 1\\\)'" "note" } void test_rd1_int (void) { @@ -39,7 +39,7 @@ void test_rd1_int (void) the attribute and with non-zero size. */ RDONLY (2, 1) void -rd2_1 (int, const void*); // { dg-message "in a call to function 'rd2_1' declared with attribute 'read_only \\\(2, 1\\\)" } +rd2_1 (int, const void*); // { dg-message "in a call to function 'rd2_1' declared with attribute 'access \\\(read_only, 2, 1\\\)" "note" } void test_rd2_1 (void) { @@ -49,7 +49,7 @@ void test_rd2_1 (void) } WRONLY (3, 1) void -wr3_1 (int, int, void*); // { dg-message "in a call to function 'wr3_1' declared with attribute 'write_only \\\(3, 1\\\)" } +wr3_1 (int, int, void*); // { dg-message "in a call to function 'wr3_1' declared with attribute 'access \\\(write_only, 3, 1\\\)" "note" } void test_wr3_1 (void) { @@ -157,7 +157,7 @@ void test_rd6_1_wr5_2_rd4_3 (void) { rd6_1_wr5_2_rd4_3 (7, 2, 1, d1, d2, s3); // { dg-warning "reading 7 bytes from a region of size 3" } rd6_1_wr5_2_rd4_3 (3, 8, 1, d1, d2, s3); // { dg-warning "writing 8 bytes into a region of size 2" } - rd6_1_wr5_2_rd4_3 (3, 2, 9, d1, d2, s3); // { dg-warning "writing 9 bytes into a region of size 1" } + rd6_1_wr5_2_rd4_3 (3, 2, 9, d1, d2, s3); // { dg-warning "accessing 9 bytes in a region of size 1" } } diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-40.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-40.c new file mode 100644 index 0000000..386c92d --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-40.c @@ -0,0 +1,120 @@ +/* PR c/50584 - No warning for passing small array to C99 static array + declarator + { dg-do compile } + { dg-options "-Wall" } */ + +typedef __INT16_TYPE__ int16_t; + +void fa2 (int16_t[2]); +void fxa2 (int16_t[2]) __attribute__ ((nonnull)); + +void fas2 (int16_t[static 2]); + +void fvla (unsigned n, int16_t[n]); + +void test_array_1_dim (void) +{ + int16_t a1[1]; + int16_t a2[2]; + int16_t i; + + fa2 (0); + fa2 (a2); + fa2 (a1); // { dg-warning "'fa2' accessing 4 bytes in a region of size 2 " } + fa2 (&i); // { dg-warning "'fa2' accessing 4 bytes in a region of size 2 " } + + fxa2 (0); // { dg-warning "\\\[-Wnonnull" } + fxa2 (a2); + fxa2 (a1); // { dg-warning "'fxa2' accessing 4 bytes in a region of size 2 " } + fxa2 (&i); // { dg-warning "'fxa2' accessing 4 bytes in a region of size 2 " } + + fas2 (0); // { dg-warning "\\\[-Wnonnull" } + fas2 (a2); + fas2 (a1); // { dg-warning "'fas2' accessing 4 bytes in a region of size 2 " } + fas2 (&i); // { dg-warning "'fas2' accessing 4 bytes in a region of size 2 " } + + fvla (1, 0); // { dg-warning "\\\[-Wnonnull" } + fvla (1, &i); + fvla (2, a2); + fvla (2, a1); // { dg-warning "'fvla' accessing 4 bytes in a region of size 2 " } + fvla (2, &i); // { dg-warning "'fvla' accessing 4 bytes in a region of size 2 " } +} + + +void fac2 (const int16_t[2]); +void fxac2 (const int16_t[2]) __attribute__ ((nonnull)); + +void facs2 (const int16_t[static 2]); + +void fvlac (unsigned n, const int16_t[n]); + +void test_const_array_1_dim (void) +{ + int16_t a1[1]; + int16_t a2[2]; + int16_t i; + + fac2 (0); + fac2 (a2); + fac2 (a1); // { dg-warning "'fac2' reading 4 bytes from a region of size 2 " } + fac2 (&i); // { dg-warning "'fac2' reading 4 bytes from a region of size 2 " } + + fxac2 (0); // { dg-warning "\\\[-Wnonnull" } + fxac2 (a2); + fxac2 (a1); // { dg-warning "'fxac2' reading 4 bytes from a region of size 2 " } + fxac2 (&i); // { dg-warning "'fxac2' reading 4 bytes from a region of size 2 " } + + facs2 (0); // { dg-warning "\\\[-Wnonnull" } + facs2 (a2); + facs2 (a1); // { dg-warning "'facs2' reading 4 bytes from a region of size 2 " } + facs2 (&i); // { dg-warning "'facs2' reading 4 bytes from a region of size 2 " } + + fvlac (1, 0); // { dg-warning "\\\[-Wnonnull" } + fvlac (1, &i); + fvlac (2, a2); + fvlac (2, a1); // { dg-warning "'fvlac' reading 4 bytes from a region of size 2 " } + fvlac (2, &i); // { dg-warning "'fvlac' reading 4 bytes from a region of size 2 " } +} + + +void fca3x5 (int16_t[3][5]); +void fcas5x7 (int16_t[static 5][7]); + +struct Snx5 { int16_t a3x5[3][5], a2x5[2][5], a1x5[1][5]; }; +struct Snx7 { int16_t a5x7[5][7], a4x7[4][7], a1x7[1][7]; }; +struct S0x7 { int x; int16_t a0x7[0][7]; }; + +void test_array_2_dim (struct Snx5 *px5, struct Snx7 *px7, struct S0x7 *p0x7) +{ + int16_t a0x5[0][5], a1x5[1][5], a2x5[2][5], a3x5[3][5], a4x5[4][5]; + + fca3x5 (a3x5); + fca3x5 (a4x5); + fca3x5 (a2x5); // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 20" } + fca3x5 (a1x5); // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 10" } + fca3x5 (a0x5); // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 0" } + + fca3x5 (px5->a3x5); + fca3x5 (px5->a2x5); // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 20" } + fca3x5 (px5->a1x5); // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 10" "pr96346" { xfail *-*-* } } + + { + int16_t (*pa2x5)[5] = &a2x5[0]; + fca3x5 (pa2x5); // { dg-warning "'fca3x5' accessing 30 bytes in a region of size 10" } + ++pa2x5; + fca3x5 (pa2x5); // { dg-warning "'fca3x5' accessing 30 bytes " } + } + + int16_t a0x7[0][7], a1x7[1][7], a4x7[4][7], a5x7[5][7], a99x7[99][7]; + fcas5x7 (a99x7); + fcas5x7 (a5x7); + fcas5x7 (a4x7); // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 56" } + fcas5x7 (a1x7); // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 14" } + fcas5x7 (a0x7); // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 0" } + + fcas5x7 (px7->a5x7); + fcas5x7 (px7->a4x7); // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 56" } + fcas5x7 (px7->a1x7); // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 14" "pr96346" { xfail *-*-* } } + + fcas5x7 (p0x7->a0x7); // { dg-warning "'fcas5x7' accessing 70 bytes in a region of size 0" "pr96346" { xfail *-*-* } } +} diff --git a/gcc/testsuite/gcc.dg/attr-access-2.c b/gcc/testsuite/gcc.dg/attr-access-2.c new file mode 100644 index 0000000..7476261 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-access-2.c @@ -0,0 +1,116 @@ +/* PR 50584 - No warning for passing small array to C99 static array declarator + Exercise interaction between explicit attribute access and VLA parameters. + { dg-do compile } + { dg-options "-Wall" } */ + +#define RW(...) __attribute__ ((access (read_write, __VA_ARGS__))) + + +void f1 (int n, int[n], int); // { dg-message "designating the bound of variable length array argument 2" "note" } + +// Verify that a redundant attribute access doesn't trigger a warning. +RW (2, 1) void f1 (int n, int[n], int); + +RW (2, 3) void f1 (int n, int[n], int); // { dg-warning "attribute 'access\\\(read_write, 2, 3\\\)' positional argument 2 conflicts with previous designation by argument 1" } + + +/* Verify that applying the attribute to a VLA with an unspecified bound + doesn't trigger any warnings, both with and without a size operand. */ + void f2 (int, int[*], int); +RW (2) void f2 (int, int[*], int); +RW (2, 3) void f2 (int, int[*], int); + +/* Designating a parameter that comes before the VLA is the same as + using the standard VLA int[n] syntax. It might be worth issuing + a portability warning suggesting to prefer the standard syntax. */ + void f3 (int, int[*], int); +RW (2, 1) void f3 (int, int[*], int); + +/* Designating a parameter that comes after the VLA cannot be expressed + using the standard VLA int[n] syntax. Verify it doesn't trigger + a warning. */ + void f4 (int, int[*], int); +RW (2, 3) void f4 (int, int[*], int); + +/* Also verify the same on the same declaration. */ + void f5 (int[*], int) RW (1, 2); +RW (1, 2) void f5 (int[*], int); +RW (1, 2) void f5 (int[*], int) RW (1, 2); + + +/* Verify that designating a VLA parameter with an explicit bound without + also designating the same bound parameter triggers a warning (it has + a different meaning). */ + void f7 (int n, int[n]); // { dg-message "21:note: designating the bound of variable length array argument 2" "note" } +RW (2) void f7 (int n, int[n]); // { dg-warning "attribute 'access\\\(read_write, 2\\\)' missing positional argument 2 provided in previous designation by argument 1" } + + void f8 (int n, int[n]); +RW (2, 1) void f8 (int n, int[n]); + + + void f9 (int, char[]); // { dg-message "25:note: previously declared as an ordinary array 'char\\\[]'" note" } +RW (2) void f9 (int n, char a[n]) // { dg-warning "argument 2 of type 'char\\\[n]' declared as a variable length array" } + // { dg-warning "attribute 'access *\\\(read_write, 2\\\)' positional argument 2 missing in previous designation" "" { target *-*-* } .-1 } + // { dg-message "24:note: designating the bound of variable length array argument 2" "note" { target *-*-* } .-2 } +{ (void)&n; (void)&a; } + + + void f10 (int, char[]); // { dg-message "26:note: previously declared as an ordinary array 'char\\\[]'" "note" } +RW (2, 1) void f10 (int n, char a[n]) // { dg-warning "attribute 'access *\\\(read_write, 2, 1\\\)' positional argument 2 missing in previous designation" "pr????" { xfail *-*-* } } + // { dg-warning "argument 2 of type 'char\\\[n]' declared as a variable length array" "" { target *-*-* } .-1 } +{ (void)&n; (void)&a; } + + +/* The following is diagnosed to point out declarations with the T[*] + form in headers where specifying the bound is just as important as + in the definition (to detect bugs). */ + void f11 (int, char[*]); // { dg-warning "argument 2 of type 'char\\\[\\\*\\\]' declared with 1 unspecified variable bound" } + void f11 (int m, char a[m]); // { dg-message "subsequently declared as 'char\\\[m]' with 0 unspecified variable bounds" "note" } +RW (2, 1) void f11 (int n, char arr[n]) // { dg-message "subsequently declared as 'char\\\[n]' with 0 unspecified variable bounds" "note" } +{ (void)&n; (void)&arr; } + + +/* Verify that redeclaring a function with attribute access applying + to an array parameter of any form is not diagnosed. */ + void f12__ (int, int[]) RW (2, 1); +RW (2, 1) void f12__ (int, int[]); + + void f12_3 (int, int[3]) RW (2, 1); +RW (2, 1) void f12_3 (int, int[3]); + + void f12_n (int n, int[n]) RW (2, 1); +RW (2, 1) void f12_n (int n, int[n]); + + void f12_x (int, int[*]) RW (2, 1); +RW (2, 1) void f12_x (int, int[*]); + + void f13__ (int, int[]); +RW (2, 1) void f13__ (int, int[]); + + void f13_5 (int, int[5]); +RW (2, 1) void f13_5 (int, int[5]); + + void f13_n (int n, int[n]); +RW (2, 1) void f13_n (int n, int[n]); + + void f13_x (int, int[*]); +RW (2, 1) void f13_x (int, int[*]); + +RW (2, 1) void f14__ (int, int[]); + void f14__ (int, int[]); + +RW (2, 1) void f14_7 (int, int[7]); + void f14_7 (int, int[7]); + +RW (2, 1) void f14_n (int n, int[n]); + void f14_n (int n, int[n]); + +RW (2, 1) void f14_x (int, int[*]); + void f14_x (int, int[*]); + +typedef void G1 (int n, int[n], int); + +G1 g1; + +RW (2, 3) void g1 (int n, int[n], int); // { dg-warning "24: attribute 'access *\\\(read_write, 2, 3\\\)' positional argument 2 conflicts with previous designation by argument 3" } +// { dg-message "designating the bound of variable length array argument 2" "note" { target *-*-* } .-1 } diff --git a/gcc/testsuite/gcc.dg/attr-access-none.c b/gcc/testsuite/gcc.dg/attr-access-none.c index d983f2f..dc06d05 100644 --- a/gcc/testsuite/gcc.dg/attr-access-none.c +++ b/gcc/testsuite/gcc.dg/attr-access-none.c @@ -23,7 +23,7 @@ void nowarn_fnone_pcv1 (void) int __attribute__ ((access (none, 1, 2))) -fnone_pcv1_2 (const void*, int); // { dg-message "in a call to function 'fnone_pcv1_2' declared with attribute 'none \\\(1, 2\\\)'" } +fnone_pcv1_2 (const void*, int); // { dg-message "in a call to function 'fnone_pcv1_2' declared with attribute 'access \\\(none, 1, 2\\\)'" "note" } void nowarn_fnone_pcv1_2 (void) { diff --git a/gcc/testsuite/gcc.dg/dfp/composite-type.c b/gcc/testsuite/gcc.dg/dfp/composite-type.c index 6d461c7..ce7d5c1 100644 --- a/gcc/testsuite/gcc.dg/dfp/composite-type.c +++ b/gcc/testsuite/gcc.dg/dfp/composite-type.c @@ -53,3 +53,6 @@ int main() return 0; } + +/* The invalid function redeclarations might also trigger: + { dg-prune-output "-Warray-parameter" } */ diff --git a/gcc/testsuite/gcc.dg/torture/pr57147-1.c b/gcc/testsuite/gcc.dg/torture/pr57147-1.c index 5c2a30e..5fd4ee7 100644 --- a/gcc/testsuite/gcc.dg/torture/pr57147-1.c +++ b/gcc/testsuite/gcc.dg/torture/pr57147-1.c @@ -2,11 +2,11 @@ /* { dg-options "-fdump-tree-optimized" } */ /* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } { "" } } */ -struct __jmp_buf_tag {}; +struct __jmp_buf_tag { int mask; }; typedef struct __jmp_buf_tag jmp_buf[1]; extern int _setjmp (struct __jmp_buf_tag __env[1]); -jmp_buf g_return_jmp_buf; +extern jmp_buf g_return_jmp_buf; void SetNaClSwitchExpectations (void) { diff --git a/gcc/testsuite/gcc.dg/torture/pr57147-3.c b/gcc/testsuite/gcc.dg/torture/pr57147-3.c index 7a5926a..699c7f9 100644 --- a/gcc/testsuite/gcc.dg/torture/pr57147-3.c +++ b/gcc/testsuite/gcc.dg/torture/pr57147-3.c @@ -1,8 +1,7 @@ /* { dg-do compile } */ typedef char * ptr_t; -struct __jmp_buf_tag { -}; +struct __jmp_buf_tag { int mask; }; typedef struct __jmp_buf_tag sigjmp_buf[1]; sigjmp_buf GC_jmp_buf; int __sigsetjmp (sigjmp_buf, int); diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c index 0447bb2..7ed1686 100644 --- a/gcc/tree-ssa-uninit.c +++ b/gcc/tree-ssa-uninit.c @@ -562,7 +562,7 @@ maybe_warn_pass_by_reference (gimple *stmt, wlimits &wlims) if (fndecl) { location_t loc (DECL_SOURCE_LOCATION (fndecl)); - inform (loc, "by argument %u of type %<%s%> to %qD " + inform (loc, "by argument %u of type %s to %qD " "declared here", argno, argtypestr.c_str (), fndecl); } @@ -570,7 +570,7 @@ maybe_warn_pass_by_reference (gimple *stmt, wlimits &wlims) { /* Handle calls through function pointers. */ location_t loc (gimple_location (stmt)); - inform (loc, "by argument %u of type %<%s%> to %qT", + inform (loc, "by argument %u of type %s to %qT", argno, argtypestr.c_str (), fntype); } } -- 2.7.4