On 10/30/18 9:44 AM, Martin Sebor wrote: > On 10/30/2018 09:27 AM, Jeff Law wrote: >> 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/ > > Why? Default arguments (and overloading) exist in C++ to deal > with just this case: to avoid having to provide the common > argument value while letting callers provide a different value > when they need to. What purpose will it serve to make these > unnecessary changes and to force new callers to provide > the default argument value? It will only make using > the function more error-prone and its callers harder > to read.I find them much harder to read especially once you get more than one. In cases where we have a small number of call sites we should just fix them. I think that we're well in that range here. If there's a large number of call sites, then overloading via default args makes plenty of sense.
jeff