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);
}
/* 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;
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. */
? 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;
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.
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. */
"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;
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;
}
}
/* 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 */,
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);
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);
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);
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. */
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))
/* 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 */)
{
/* 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;
}
check_access (exp, /*dstwrite=*/NULL_TREE, maxread, srclen,
destsize, data.mode, &data);
-
return NULL_RTX;
}
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;
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 */
#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)
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 == 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
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
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",
{
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);
}
}
- 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)
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;
/* 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;
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)
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)
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]." } */
/* 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)
{
}
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)
{
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)
{
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)
{
}
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)
{
{
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" }
}
--- /dev/null
+/* 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 *-*-* } }
+}
--- /dev/null
+/* 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 }
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)
{
return 0;
}
+
+/* The invalid function redeclarations might also trigger:
+ { dg-prune-output "-Warray-parameter" } */
/* { 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)
{
/* { 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);
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);
}
{
/* 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);
}
}