#include "stringpool.h"
#include "attribs.h"
#include "builtins.h"
+#include "gimple-fold.h"
/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
/* The bound argument to a bounded string function like strncpy. */
tree bound = NULL_TREE;
+ /* The range of lengths of a string argument to one of the comparison
+ functions. If the length is less than the bound it is used instead. */
+ tree lenrng[2] = { NULL_TREE, NULL_TREE };
+
/* It's safe to call "bounded" string functions with a non-string
argument since the functions provide an explicit bound for this
purpose. */
switch (DECL_FUNCTION_CODE (fndecl))
{
- case BUILT_IN_STPNCPY:
- case BUILT_IN_STPNCPY_CHK:
+ case BUILT_IN_STRCMP:
case BUILT_IN_STRNCMP:
case BUILT_IN_STRNCASECMP:
+ {
+ /* For these, if one argument refers to one or more of a set
+ of string constants or arrays of known size, determine
+ the range of their known or possible lengths and use it
+ conservatively as the bound for the unbounded function,
+ and to adjust the range of the bound of the bounded ones. */
+ unsigned stride = with_bounds ? 2 : 1;
+ for (unsigned argno = 0; argno < nargs && !*lenrng; argno += stride)
+ {
+ tree arg = CALL_EXPR_ARG (exp, argno);
+ if (!get_attr_nonstring_decl (arg))
+ get_range_strlen (arg, lenrng);
+ }
+ }
+ /* Fall through. */
+
+ case BUILT_IN_STPNCPY:
+ case BUILT_IN_STPNCPY_CHK:
case BUILT_IN_STRNCPY:
case BUILT_IN_STRNCPY_CHK:
{
if (bound)
get_size_range (bound, bndrng);
+ if (*lenrng)
+ {
+ /* Add one for the nul. */
+ lenrng[0] = const_binop (PLUS_EXPR, TREE_TYPE (lenrng[0]),
+ lenrng[0], size_one_node);
+ lenrng[1] = const_binop (PLUS_EXPR, TREE_TYPE (lenrng[1]),
+ lenrng[1], size_one_node);
+
+ if (!bndrng[0])
+ {
+ /* Conservatively use the upper bound of the lengths for
+ both the lower and the upper bound of the operation. */
+ bndrng[0] = lenrng[1];
+ bndrng[1] = lenrng[1];
+ bound = void_type_node;
+ }
+ else
+ {
+ /* Replace the bound on the oparation with the upper bound
+ of the length of the string if the latter is smaller. */
+ if (tree_int_cst_lt (lenrng[1], bndrng[0]))
+ bndrng[0] = lenrng[1];
+ else if (tree_int_cst_lt (lenrng[1], bndrng[1]))
+ bndrng[1] = lenrng[1];
+ }
+ }
+
/* Iterate over the built-in function's formal arguments and check
each const char* against the actual argument. If the actual
argument is declared attribute non-string issue a warning unless
tree type = TREE_TYPE (decl);
+ /* The maximum number of array elements accessed. */
offset_int wibnd = 0;
if (bndrng[0])
wibnd = wi::to_offset (bndrng[0]);
+ /* Size of the array. */
offset_int asize = wibnd;
+ /* Determine the array size. For arrays of unknown bound and
+ pointers reset BOUND to trigger the appropriate warning. */
if (TREE_CODE (type) == ARRAY_TYPE)
- if (tree arrbnd = TYPE_DOMAIN (type))
- {
- if ((arrbnd = TYPE_MAX_VALUE (arrbnd)))
- asize = wi::to_offset (arrbnd) + 1;
- }
+ {
+ if (tree arrbnd = TYPE_DOMAIN (type))
+ {
+ if ((arrbnd = TYPE_MAX_VALUE (arrbnd)))
+ asize = wi::to_offset (arrbnd) + 1;
+ }
+ else if (bound == void_type_node)
+ bound = NULL_TREE;
+ }
+ else if (bound == void_type_node)
+ bound = NULL_TREE;
location_t loc = EXPR_LOCATION (exp);
--- /dev/null
+/* PR 85623 - strncmp() warns about attribute 'nonstring' incorrectly
+ in -Wstringop-overflow
+ { dg-do compile }
+ { dg-options "-O2 -Wstringop-overflow -ftrack-macro-expansion=0" } */
+
+#include "../gcc.dg/range.h"
+
+#if __cplusplus
+extern "C" {
+#endif
+
+extern int strcmp (const char*, const char*);
+extern int strncmp (const char*, const char*, size_t);
+extern int strncasecmp (const char*, const char*, size_t);
+
+extern size_t strspn (const char*, const char*);
+extern size_t strcspn (const char*, const char*);
+
+#if __cplusplus
+}
+#endif
+
+#define S26 "0123456789abcdefghijklmnopqrstuvwxyz"
+#define S(n) (S26 + sizeof S26 - 1 - (n))
+
+char __attribute__ ((nonstring)) a3[3];
+char __attribute__ ((nonstring)) a5[5];
+
+void sink (int);
+
+#define T(call) sink (call)
+
+void test_strcmp_cst (void)
+{
+ /* Verify that no warning is issued for strcmp() calls with a non-string
+ array argument when the other argument is a string whose length is
+ less than the size of the array. Because the function stops reading
+ at the first nul character there is no chance that it will read past
+ the end of the array. */
+ T (strcmp (S (0), a3));
+ T (strcmp (S (1), a3));
+ T (strcmp (S (2), a3));
+ /* The following reads a3[3]. */
+ T (strcmp (S (3), a3)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ /* The following also reads past the end of a3. */
+ T (strcmp (S (9), a3)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ T (strcmp (a3, S (0)));
+ T (strcmp (a3, S (1)));
+ T (strcmp (a3, S (2)));
+ T (strcmp (a3, S (3))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strcmp (a3, S (9))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+}
+
+
+void test_strcmp_range (const char *s)
+{
+ s = signed_value () < 0 ? S (0) : S (1);
+ T (strcmp (a3, s));
+
+ s = signed_value () < 0 ? S (0) : S (2);
+ T (strcmp (a3, s));
+
+ s = signed_value () < 0 ? S (0) : S (3);
+ T (strcmp (a3, s)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ s = signed_value () < 0 ? S (1) : S (2);
+ T (strcmp (a3, s));
+
+ s = signed_value () < 0 ? S (1) : S (3);
+ T (strcmp (a3, s)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ s = signed_value () < 0 ? S (3) : S (4);
+ T (strcmp (a3, s)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+}
+
+
+void test_strncmp_cst (void)
+{
+ T (strncmp (S (0), a3, 1));
+ T (strncmp (S (1), a3, 2));
+ T (strncmp (S (2), a3, 3));
+ T (strncmp (S (3), a3, 3));
+ T (strncmp (S (3), a3, 4)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ T (strncmp (S (9), a3, 3));
+ T (strncmp (S (9), a3, 4)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strncmp (S (9), a3, 5)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ T (strncmp (a3, S (0), 1));
+ T (strncmp (a3, S (1), 2));
+ T (strncmp (a3, S (2), 3));
+ T (strncmp (a3, S (3), 3));
+ T (strncmp (a3, S (3), 4)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ T (strncmp (a3, S (9), 3));
+ T (strncmp (a3, S (9), 4)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strncmp (a3, S (9), 5)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+}
+
+void test_strncmp_range (const char *s)
+{
+ T (strncmp (a3, S (2), UR (0, 3)));
+ T (strncmp (a3, S (2), UR (1, 4)));
+ T (strncmp (a3, S (2), UR (2, 5)));
+ T (strncmp (a3, S (2), UR (3, 6)));
+ T (strncmp (a3, S (2), UR (4, 7)));
+
+ T (strncmp (a3, S (5), UR (0, 3)));
+ T (strncmp (a3, S (5), UR (1, 4)));
+ T (strncmp (a3, S (5), UR (2, 5)));
+ T (strncmp (a3, S (5), UR (3, 6)));
+ T (strncmp (a3, S (5), UR (4, 7))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strncmp (a3, S (5), UR (7, 9))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ s = signed_value () < 0 ? S (0) : S (1);
+ T (strncmp (a3, s, UR (1, 3)));
+ T (strncmp (a3, s, UR (2, 5)));
+
+ s = signed_value () < 0 ? S (2) : S (5);
+ T (strncmp (a3, s, UR (1, 3)));
+
+ s = signed_value () < 0 ? S (2) : S (5);
+ T (strncmp (a3, s, UR (1, 4)));
+ T (strncmp (a3, s, UR (2, 5)));
+ T (strncmp (a3, s, UR (3, 6)));
+ T (strncmp (a3, s, UR (4, 7))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+}
+
+void test_strncasecmp (void)
+{
+ T (strncasecmp (S (0), a3, 1));
+ T (strncasecmp (S (1), a3, 2));
+ T (strncasecmp (S (2), a3, 3));
+ T (strncasecmp (S (3), a3, 3));
+ T (strncasecmp (S (3), a3, 4)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ T (strncasecmp (S (9), a3, 3));
+ T (strncasecmp (S (9), a3, 4)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strncasecmp (S (9), a3, 5)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ T (strncasecmp (a3, S (0), 1));
+ T (strncasecmp (a3, S (1), 2));
+ T (strncasecmp (a3, S (2), 3));
+ T (strncasecmp (a3, S (3), 3));
+ T (strncasecmp (a3, S (3), 4)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ T (strncasecmp (a3, S (9), 3));
+ T (strncasecmp (a3, S (9), 4)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strncasecmp (a3, S (9), 5)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+}
+
+void test_strspn (void)
+{
+ /* strspn must traverse all characters in the second argument except
+ when the first string is empty. */
+ T (strspn (S (0), a3));
+ T (strspn (S (1), a3)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strspn (S (2), a3)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strspn (S (3), a3)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strspn (S (9), a3)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ /* Similarly, strspn must traverse all characters in the first argument
+ except when the second string is empty. */
+ T (strspn (a3, S (0)));
+ T (strspn (a3, S (1))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strspn (a3, S (2))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strspn (a3, S (3))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strspn (a3, S (9))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+}
+
+void test_strcspn (void)
+{
+ T (strcspn (S (0), a3));
+ T (strcspn (S (1), a3)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strcspn (S (2), a3)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strcspn (S (3), a3)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strcspn (S (9), a3)); /* { dg-warning "\\\[-Wstringop-overflow" } */
+
+ T (strcspn (a3, S (0))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strcspn (a3, S (1))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strcspn (a3, S (2))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strcspn (a3, S (3))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+ T (strcspn (a3, S (9))); /* { dg-warning "\\\[-Wstringop-overflow" } */
+}