On 10/29/18 5:51 PM, Martin Sebor wrote:
The missing nul detection fails when the argument of the %s or
similar sprintf directive is the address of a non-nul character
constant such as in:
const char c = 'a';
int f (void)
{
return snprintf (0, 0, "%s", &c);
}
This is because the string_constant function only succeeds for
arguments that refer to STRRING_CSTs, not to individual characters.
For the same reason, calls to memchr() such as the one below aren't
folded into constants:
const char d = '\0';
void* g (void)
{
return memchr (&d, 0, 1);
}
To detect and diagnose the missing nul in the first example and
to fold the second, the attached patch modifies string_constant
to return a synthesized STRING_CST object for such references
(while also indicating whether such an object is properly
nul-terminated).
Tested on x86_64-linux.
Martin
gcc-87756.diff
PR tree-optimization/87756 - missing unterminated argument warning
using address of a constant character
gcc/ChangeLog:
PR tree-optimization/87756
* expr.c (string_constant): Synthesize a string literal from
the address of a constant character.
* tree.c (build_string_literal): Add an argument.
* tree.h (build_string_literal): Same.
gcc/testsuite/ChangeLog:
PR tree-optimization/87756
* gcc.dg/builtin-memchr-2.c: New test.
* gcc.dg/builtin-memchr-3.c: Same.
* gcc.dg/warn-sprintf-no-nul-2.c: Same.
Index: gcc/expr.c
===================================================================
--- gcc/expr.c (revision 265496)
+++ gcc/expr.c (working copy)
@@ -11484,18 +11484,40 @@ string_constant (tree arg, tree
*ptr_offset, tree
offset = off;
}
- if (!init || TREE_CODE (init) != STRING_CST)
+ if (!init)
return NULL_TREE;
+ *ptr_offset = offset;
+
+ tree eltype = TREE_TYPE (init);
+ tree initsize = TYPE_SIZE_UNIT (eltype);
if (mem_size)
- *mem_size = TYPE_SIZE_UNIT (TREE_TYPE (init));
+ *mem_size = initsize;
+
if (decl)
*decl = array;
- gcc_checking_assert (tree_to_shwi (TYPE_SIZE_UNIT (TREE_TYPE
(init)))
- >= TREE_STRING_LENGTH (init));
+ if (TREE_CODE (init) == INTEGER_CST)
+ {
+ /* For a reference to (address of) a single constant character,
+ store the native representation of the character in
CHARBUF. */
+ unsigned char charbuf[MAX_BITSIZE_MODE_ANY_MODE /
BITS_PER_UNIT];
+ int len = native_encode_expr (init, charbuf, sizeof charbuf,
0);
+ if (len > 0)
+ {
+ /* Construct a string literal with elements of ELTYPE and
+ the representation above. Then strip
+ the ADDR_EXPR (ARRAY_REF (...)) around the STRING_CST. */
+ init = build_string_literal (len, (char *)charbuf, eltype);
+ init = TREE_OPERAND (TREE_OPERAND (init, 0), 0);
+ }
+ }
- *ptr_offset = offset;
+ if (TREE_CODE (init) != STRING_CST)
+ return NULL_TREE;
+
+ gcc_checking_assert (tree_to_shwi (initsize) >= TREE_STRING_LENGTH
(init));
+
return init;
}
Index: gcc/tree.c
===================================================================
--- gcc/tree.c (revision 265496)
+++ gcc/tree.c (working copy)
Index: gcc/tree.h
===================================================================
--- gcc/tree.h (revision 265496)
+++ gcc/tree.h (working copy)
@@ -4194,7 +4194,7 @@ extern tree
build_call_expr_internal_loc_array (lo
extern tree maybe_build_call_expr_loc (location_t, combined_fn, tree,
int, ...);
extern tree build_alloca_call_expr (tree, unsigned int,
HOST_WIDE_INT);
-extern tree build_string_literal (int, const char *);
+extern tree build_string_literal (int, const char *, tree =
char_type_node);
/* Construct various nodes representing data types. */
There's only about a dozen calls to build_string_literal. Instead of
defaulting the argument, just fix them. OK with that change. Make
sure to catch those in config/{rs6000,i386}/ and cp/