Patch 4 adds support to the machinery behind -Wstringop-overflow to issue warnings for (likely) out of bounds accesses in calls to functions with the internal attribute access specification. This implements the feature pr50584 asks for (plus more).
[4/5] - Extend -Wstringop-overflow to detect out-of-bounds accesses to array parameters.
gcc/ChangeLog: * tree-ssa-uninit.c (maybe_warn_pass_by_reference): Handle attribute access internal representation of arrays. gcc/testsuite/ChangeLog: * gcc.dg/uninit-37.c: New test. 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. 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/Wstringop-overflow-40.c: New test. * gcc.dg/attr-access-2.c: New test. diff --git a/gcc/builtins.c b/gcc/builtins.c index 228db78f32b..bbaeb3aedd1 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -3312,26 +3312,122 @@ determine_block_size (tree len, rtx len_rtx, } /* 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, int opt, tree range[2], - tree slen, bool access) + 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, 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) + exp, func, range[0], size) : warning_n (loc, opt, 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. */ @@ -3339,59 +3435,47 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2], ? warning_at (loc, opt, "%K%qD reading %E or more bytes from a region " "of size %E", - exp, func, range[0], slen) + exp, func, range[0], size) : warning_at (loc, opt, "%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, "%K%qD reading between %E and %E bytes from " "a region of size %E", - exp, func, range[0], range[1], slen) + exp, func, range[0], range[1], size) : warning_at (loc, opt, "%Kreading between %E and %E bytes from " "a region of size %E", - exp, range[0], range[1], slen)); + exp, range[0], range[1], size)); 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, 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) - : warning_n (loc, opt, 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)); - 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 expecting %E or more bytes in a region " - "of size %E", - exp, func, range[0], slen) - : warning_at (loc, opt, - "%Kexpecting %E or more bytes in a region " - "of size %E", - exp, range[0], slen)); - } + ? warning_at (loc, OPT_Warray_parameter_, + "%K%qD expecting %E or more bytes in a region " + "of size %E", + exp, func, range[0], size) + : warning_at (loc, OPT_Warray_parameter_, + "%Kexpecting %E or more bytes in a region " + "of size %E", + exp, range[0], size)); else warned = (func - ? warning_at (loc, opt, + ? warning_at (loc, OPT_Warray_parameter_, "%K%qD expecting between %E and %E bytes in " "a region of size %E", - exp, func, range[0], range[1], slen) - : warning_at (loc, opt, + exp, func, range[0], range[1], size) + : warning_at (loc, OPT_Warray_parameter_, "%Kexpectting between %E and %E bytes in " "a region of size %E", - exp, range[0], range[1], slen)); + exp, range[0], range[1], size)); return warned; } @@ -3545,8 +3629,9 @@ inform_access (const access_ref &ref, bool write) 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. @@ -3556,7 +3641,7 @@ inform_access (const access_ref &ref, bool write) bool check_access (tree exp, tree, tree, tree dstwrite, tree maxread, tree srcstr, tree dstsize, - bool access /* = true */, + bool write /* = true */, bool read /* = true */, const access_data *pad /* = NULL */) { int opt = OPT_Wstringop_overflow_; @@ -3636,6 +3721,11 @@ check_access (tree exp, tree, tree, tree dstwrite, get_size_range (dstwrite, range); 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. */ @@ -3717,49 +3807,10 @@ check_access (tree exp, tree, tree, tree dstwrite, "the destination", exp, range[0], dstsize)); } - else 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], dstsize) - : 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], dstsize)); - 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], dstsize) - : warning_at (loc, opt, - "%Kwriting %E or more bytes into " - "a region of size %E overflows " - "the destination", - exp, range[0], dstsize)); - } 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], - dstsize) - : warning_at (loc, opt, - "%Kwriting between %E and %E bytes " - "into a region of size %E overflows " - "the destination", - exp, range[0], range[1], - dstsize)); + warned = warn_for_access (loc, func, exp, opt, range, dstsize, + write, read && !builtin); + if (warned) { TREE_NO_WARNING (exp) = true; @@ -3872,7 +3923,8 @@ check_access (tree exp, tree, tree, 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, opt, range, slen, access)) + if (warn_for_access (loc, func, exp, opt, range, slen, + write && !builtin, read)) { TREE_NO_WARNING (exp) = true; if (pad) @@ -3885,8 +3937,11 @@ check_access (tree exp, tree, tree, tree dstwrite, } /* 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 */, @@ -3942,8 +3997,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); @@ -3953,10 +4014,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); @@ -3966,7 +4030,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); @@ -3976,6 +4039,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. */ @@ -4168,6 +4286,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)) @@ -4220,9 +4353,9 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, /* Convenience wrapper around the above. */ -static tree +tree compute_objsize (tree ptr, int ostype, access_ref *pref, - const vr_values *rvals = NULL) + const vr_values *rvals /* = NULL */) { bitmap visited = NULL; @@ -4291,7 +4424,7 @@ check_memop_access (tree exp, tree dest, tree src, tree size) tree dstsize = compute_objsize (dest, 0, &data.dst); return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE, - srcsize, dstsize, true, &data); + srcsize, dstsize, true, true, &data); } /* Validate memchr arguments without performing any expansion. @@ -4315,7 +4448,7 @@ expand_builtin_memchr (tree exp, rtx) tree size = compute_objsize (arg1, 0, &data.src); check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len, /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE, - true, &data); + false, true, &data); } return NULL_RTX; @@ -4599,7 +4732,7 @@ expand_builtin_strcat (tree exp) tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst); check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src, - destsize, true, &data); + destsize, true, true, &data); return NULL_RTX; } @@ -4624,7 +4757,7 @@ expand_builtin_strcpy (tree exp, rtx target) tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst); check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, - src, destsize, true, &data); + src, destsize, true, true, &data); } if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target)) @@ -4684,7 +4817,7 @@ expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode) tree destsize = compute_objsize (dst, warn_stringop_overflow - 1, &data.dst); check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, - src, destsize, true, &data); + src, destsize, true, true, &data); } /* If return value is ignored, transform stpcpy into strcpy. */ @@ -4798,7 +4931,7 @@ expand_builtin_stpncpy (tree exp, rtx) tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst); check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize, - true, &data); + true, true, &data); return NULL_RTX; } @@ -4878,7 +5011,7 @@ check_strncat_sizes (tree exp, tree objsize) /* The number of bytes to write is LEN but check_access will alsoa check SRCLEN if LEN's value isn't known. */ return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen, - objsize, true, &data); + objsize, true, true, &data); } /* Similar to expand_builtin_strcat, do some very basic size validation @@ -4951,7 +5084,7 @@ expand_builtin_strncat (tree exp, rtx) srclen = maxread; check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize, - true, &data); + true, true, &data); return NULL_RTX; } @@ -4987,7 +5120,7 @@ expand_builtin_strncpy (tree exp, rtx target) /* The number of bytes to write is LEN but check_access will also check SLEN if LEN's value isn't known. */ check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, - destsize, true, &data); + destsize, true, true, &data); } /* We must be passed a constant len and src parameter. */ @@ -5304,14 +5437,14 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq) tree size = compute_objsize (arg1, 0, &data.src); no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len, /*maxread=*/NULL_TREE, size, - /*objsize=*/NULL_TREE, true, &data); + /*objsize=*/NULL_TREE, false, true, &data); if (no_overflow) { access_data data; size = compute_objsize (arg2, 0, &data.src); no_overflow = check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len, /*maxread=*/NULL_TREE, size, - /*objsize=*/NULL_TREE, true, &data); + /*objsize=*/NULL_TREE, false, true, &data); } /* If the specified length exceeds the size of either object, diff --git a/gcc/builtins.h b/gcc/builtins.h index 8b812ceb2c4..c909ae11845 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; @@ -184,7 +177,15 @@ struct access_data access_ref dst, src; }; +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, tree, tree, - bool = true, const access_data * = NULL); + bool = true, bool = true, const access_data * = NULL); #endif /* GCC_BUILTINS_H */ diff --git a/gcc/calls.c b/gcc/calls.c index 622417ff74b..ecb966d6cb6 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) @@ -1883,36 +1885,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<int, attr_access> &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 == attr_access::read_only - ? "read_only" - : (access.second.mode == attr_access::write_only - ? "write_only" - : (access.second.mode == attr_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 @@ -1939,6 +1925,7 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp) return; 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 @@ -1967,35 +1954,40 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, 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", @@ -2012,44 +2004,56 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, 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); @@ -2058,54 +2062,76 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp) } } - tree objsize = compute_objsize (ptr, 0); + access_data data; + access_ref* const pobj = (access.second.mode == attr_access::write_only + ? &data.dst : &data.src); + tree objsize = compute_objsize (ptr, 1, pobj); - tree srcsize; - if (access.second.mode == attr_access::write_only) - { - /* For a write-only argument there is no source. */ - srcsize = NULL_TREE; - } - else + /* The size of the destination or source object. */ + tree dstsize = NULL_TREE, srcsize = NULL_TREE; + if (access.second.mode == attr_access::read_only + || access.second.mode == attr_access::none) { - /* 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 == attr_access::read_only - || access.second.mode == attr_access::none) - { - /* For a read-only attribute there is no destination so - clear OBJSIZE. This emits "reading N bytes" kind of - diagnostics instead of the "writing N bytes" kind, - unless MODE is none. */ - 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, NULL_TREE, NULL_TREE, size, /*maxread=*/ NULL_TREE, - srcsize, objsize, access.second.mode != attr_access::none); + /* Set for accesses by functions explicitly declared with attribute + access other than none, and clear for functions internally decorated + with the attribute for user-defined functions with mode of none. */ + const bool read_access + = (access.second.mode != attr_access::none + && access.second.mode != attr_access::write_only); + const bool write_access + = (access.second.mode != attr_access::none + && access.second.mode != attr_access::read_only + && !TYPE_READONLY (argtype)); + check_access (exp, /*dst=*/ NULL_TREE, /*src=*/ NULL_TREE, access_size, + /*maxread=*/ NULL_TREE, srcsize, dstsize, write_access, + read_access, &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 c4127b805ab..38e06ba5e62 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 3b9c1a36e30..b7b2a5fc4bf 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 b9965682101..048a95d6dcf 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 6d9fcb9d52e..755ebea5ff4 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-23.c @@ -20,7 +20,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) { @@ -44,7 +44,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 a21a1f17a2b..049d1c6981c 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 00000000000..386c92dc7a8 --- /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 00000000000..7167c756320 --- /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-message "previously declared as 'char\\\[\\\*]' with 1 unspecified variable bound" "note" } + void f11 (int m, char a[m]); // { dg-warning "argument 2 of type 'char\\\[m]' declared with 0 unspecified variable bounds" } +RW (2, 1) void f11 (int n, char arr[n]) // { dg-warning "argument 2 of type 'char\\\[n]' declared with 0 unspecified variable bounds" } +{ (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 d983f2fac06..85f9e73b3f4 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) { @@ -34,5 +34,5 @@ void nowarn_fnone_pcv1_2 (void) void warn_fnone_pcv1_2 (void) { char a[3]; - fnone_pcv1_2 (a, 4); // { dg-warning "expecting 4 bytes in a region of size 3" } + fnone_pcv1_2 (a, 4); // { dg-warning "expecting 4 or more bytes in a region of size 3" } }