From 70c70369ce868fd8b049f710c01a899e502f6f68 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Fri, 23 Nov 2018 18:45:45 +0000 Subject: [PATCH] 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. From-SVN: r266418 --- gcc/ChangeLog | 8 ++ gcc/expr.c | 32 ++++- gcc/testsuite/ChangeLog | 7 + gcc/testsuite/gcc.dg/builtin-memchr-2.c | 55 ++++++++ gcc/testsuite/gcc.dg/builtin-memchr-3.c | 72 ++++++++++ gcc/testsuite/gcc.dg/warn-sprintf-no-nul-2.c | 131 +++++++++++++++++++ gcc/tree.c | 25 ++-- gcc/tree.h | 2 +- 8 files changed, 315 insertions(+), 17 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/builtin-memchr-2.c create mode 100644 gcc/testsuite/gcc.dg/builtin-memchr-3.c create mode 100644 gcc/testsuite/gcc.dg/warn-sprintf-no-nul-2.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index fb107374807..c377aa534dd 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,11 @@ +2018-11-23 Martin Sebor + + 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. + 2018-11-23 Christoph Muellner * config/aarch64/aarch64-cores.def: Define emag. diff --git a/gcc/expr.c b/gcc/expr.c index 7ae3e37378c..021a04c5944 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -11484,18 +11484,40 @@ string_constant (tree arg, tree *ptr_offset, tree *mem_size, tree *decl) 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); + } + } + + if (TREE_CODE (init) != STRING_CST) + return NULL_TREE; + + gcc_checking_assert (tree_to_shwi (initsize) >= TREE_STRING_LENGTH (init)); - *ptr_offset = offset; return init; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 0338c9aeedc..eba7f5ed3cd 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2018-11-23 Martin Sebor + + 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. + 2018-11-23 Martin Sebor PR testsuite/88098 diff --git a/gcc/testsuite/gcc.dg/builtin-memchr-2.c b/gcc/testsuite/gcc.dg/builtin-memchr-2.c new file mode 100644 index 00000000000..65f9d9ca700 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-memchr-2.c @@ -0,0 +1,55 @@ +/* Verify that memchr calls with the address of a constant character + are folded as expected even at -O0. + { dg-do compile } + { dg-options "-O0 -Wall -fdump-tree-gimple" } */ + +typedef __SIZE_TYPE__ size_t; +typedef __WCHAR_TYPE__ wchar_t; + +extern void* memchr (const void*, int, size_t); +extern int printf (const char*, ...); +extern void abort (void); + +#define A(expr) \ + ((expr) \ + ? (void)0 \ + : (printf ("assertion failed on line %i: %s\n", \ + __LINE__, #expr), \ + abort ())) + +const char nul = 0; +const char cha = 'a'; + +const struct +{ + char c; +} snul = { 0 }, + schb = { 'b' }, + sarr[] = { + { 0 }, + { 'c' } + }; + + +void test_memchr_cst_char (void) +{ + A (&nul == memchr (&nul, 0, 1)); + A (!memchr (&nul, 'a', 1)); + + A (&cha == memchr (&cha, 'a', 1)); + A (!memchr (&cha, 0, 1)); + + A (&snul.c == memchr (&snul.c, 0, 1)); + A (!memchr (&snul.c, 'a', 1)); + + A (&schb.c == memchr (&schb.c, 'b', 1)); + A (!memchr (&schb.c, 0, 1)); + + A (&sarr[0].c == memchr (&sarr[0].c, 0, 1)); + A (!memchr (&sarr[0].c, 'a', 1)); + + A (&sarr[1].c == memchr (&sarr[1].c, 'c', 1)); + A (!memchr (&sarr[1].c, 0, 1)); +} + +/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/builtin-memchr-3.c b/gcc/testsuite/gcc.dg/builtin-memchr-3.c new file mode 100644 index 00000000000..b891e536215 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-memchr-3.c @@ -0,0 +1,72 @@ +/* Verify that memchr calls with a pointer to a constant character + are folded as expected. + { dg-do compile } + { dg-options "-O1 -Wall -fdump-tree-gimple" } */ + +typedef __SIZE_TYPE__ size_t; +typedef __WCHAR_TYPE__ wchar_t; + +extern void* memchr (const void*, int, size_t); +extern int printf (const char*, ...); +extern void abort (void); + +#define A(expr) \ + ((expr) \ + ? (void)0 \ + : (printf ("assertion failed on line %i: %s\n", \ + __LINE__, #expr), \ + abort ())) + +const char nul = 0; +const char cha = 'a'; + +const char* const pnul = &nul; +const char* const pcha = &cha; + +const struct +{ + char c; +} snul = { 0 }, + schb = { 'b' }, + sarr[] = { + { 0 }, + { 'c' } + }; + +const char* const psarr0c = &sarr[0].c; +const char* const psarr1c = &sarr[1].c; + +void test_memchr_cst_char (void) +{ + A (&nul == memchr (&nul, 0, 1)); + A (!memchr (&nul, 'a', 1)); + + A (&cha == memchr (&cha, 'a', 1)); + A (!memchr (&cha, 0, 1)); + + A (&nul == memchr (pnul, 0, 1)); + A (!memchr (pnul, 'a', 1)); + + A (&cha == memchr (pcha, 'a', 1)); + A (!memchr (pcha, 0, 1)); + + A (&snul.c == memchr (&snul.c, 0, 1)); + A (!memchr (&snul.c, 'a', 1)); + + A (&schb.c == memchr (&schb.c, 'b', 1)); + A (!memchr (&schb.c, 0, 1)); + + A (&sarr[0].c == memchr (&sarr[0].c, 0, 1)); + A (!memchr (&sarr[0].c, 'a', 1)); + + A (&sarr[1].c == memchr (&sarr[1].c, 'c', 1)); + A (!memchr (&sarr[1].c, 0, 1)); + + A (&sarr[0].c == memchr (psarr0c, 0, 1)); + A (!memchr (psarr0c, 'a', 1)); + + A (&sarr[1].c == memchr (psarr1c, 'c', 1)); + A (!memchr (psarr1c, 0, 1)); +} + +/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/warn-sprintf-no-nul-2.c b/gcc/testsuite/gcc.dg/warn-sprintf-no-nul-2.c new file mode 100644 index 00000000000..d3d776c03a8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/warn-sprintf-no-nul-2.c @@ -0,0 +1,131 @@ +/* PR tree-optimization/87756 - missing unterminated argument warning + using address of a constant character + { dg-do compile } + { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */ + +typedef __SIZE_TYPE__ size_t; +typedef __WCHAR_TYPE__ wchar_t; + +int sprintf (char*, const char*, ...); + +extern char* dest (void); +extern void sink (int, ...); + +#define D dest () +#define T(expr) sink (0, (expr)) + + +const char cnul = '\0'; +const char cnonul = 'a'; +const char str3[] = "123"; + +const struct +{ + char a, b, s[3]; +} s1 = { '\0', 'b', "123" }, + s2[2] = { + { '\0', 'c', "12" }, + { 'd', '\0', "123" } + }; + +void test_sprintf_s (void) +{ + T (sprintf (D, "%s", &cnul)); + T (sprintf (D, "%s", &cnonul)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.1s", &cnonul)); + T (sprintf (D, "%.2s", &cnonul)); /* { dg-warning "nul-terminated" } */ + + T (sprintf (D, "%s", &s1.a)); + T (sprintf (D, "%s", &s1.b)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.1s", &s1.b)); + T (sprintf (D, "%.2s", &s1.b)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%s", s1.s)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.3s", s1.s)); + T (sprintf (D, "%.4s", s1.s)); /* { dg-warning "nul-terminated" } */ + + T (sprintf (D, "%.2s", s1.s + 1)); + T (sprintf (D, "%.3s", s1.s + 1)); /* { dg-warning "nul-terminated" } */ + + T (sprintf (D, "%s", &s2[0].a)); + T (sprintf (D, "%s", &s2[0].b)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.1s", &s2[0].b)); + T (sprintf (D, "%.2s", &s2[0].b)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%s", s2[0].s)); + T (sprintf (D, "%.3s", s2[0].s)); + T (sprintf (D, "%.4s", s2[0].s)); + + T (sprintf (D, "%.2s", s2[0].s + 1)); + T (sprintf (D, "%.3s", s2[0].s + 1)); + + T (sprintf (D, "%s", &s2[1].a)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.1s", &s2[1].a)); + T (sprintf (D, "%.2s", &s2[1].a)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%s", &s2[1].b)); + T (sprintf (D, "%s", s2[1].s)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.3s", s2[1].s)); + T (sprintf (D, "%.4s", s2[1].s)); /* { dg-warning "nul-terminated" } */ + + T (sprintf (D, "%.2s", s2[1].s + 1)); + T (sprintf (D, "%.3s", s2[1].s + 1)); /* { dg-warning "nul-terminated" } */ + + T (sprintf (D, "%s", &str3[3])); + T (sprintf (D, "%s", &str3[4])); /* { dg-warning "\\\[-Warray-bounds" } */ +} + + +const char wnul = '\0'; +const char wnonul = 'a'; +const char wcs3[] = "123"; + +const struct +{ + char a, b, s[3]; +} w1 = { '\0', 'b', "123" }, + w2[2] = { + { '\0', 'c', "12" }, + { 'd', '\0', "123" } + }; + +void test_sprintf_ls (void) +{ + T (sprintf (D, "%s", &wnul)); + T (sprintf (D, "%s", &wnonul)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.1s", &wnonul)); + T (sprintf (D, "%.2s", &wnonul)); /* { dg-warning "nul-terminated" } */ + + T (sprintf (D, "%s", &w1.a)); + T (sprintf (D, "%s", &w1.b)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.1s", &w1.b)); + T (sprintf (D, "%.2s", &w1.b)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%s", w1.s)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.3s", w1.s)); + T (sprintf (D, "%.4s", w1.s)); /* { dg-warning "nul-terminated" } */ + + T (sprintf (D, "%.2s", w1.s + 1)); + T (sprintf (D, "%.3s", w1.s + 1)); /* { dg-warning "nul-terminated" } */ + + T (sprintf (D, "%s", &w2[0].a)); + T (sprintf (D, "%s", &w2[0].b)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.1s", &w2[0].b)); + T (sprintf (D, "%.2s", &w2[0].b)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%s", w2[0].s)); + T (sprintf (D, "%.3s", w2[0].s)); + T (sprintf (D, "%.4s", w2[0].s)); + + T (sprintf (D, "%.2s", w2[0].s + 1)); + T (sprintf (D, "%.3s", w2[0].s + 1)); + + T (sprintf (D, "%s", &w2[1].a)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.1s", &w2[1].a)); + T (sprintf (D, "%.2s", &w2[1].a)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%s", &w2[1].b)); + T (sprintf (D, "%s", w2[1].s)); /* { dg-warning "nul-terminated" } */ + T (sprintf (D, "%.3s", w2[1].s)); + T (sprintf (D, "%.4s", w2[1].s)); /* { dg-warning "nul-terminated" } */ + + T (sprintf (D, "%.2s", w2[1].s + 1)); + T (sprintf (D, "%.3s", w2[1].s + 1)); /* { dg-warning "nul-terminated" } */ + + T (sprintf (D, "%s", &wcs3[3])); + T (sprintf (D, "%s", &wcs3[4])); /* { dg-warning "\\\[-Warray-bounds" } */ +} diff --git a/gcc/tree.c b/gcc/tree.c index 39a92464414..1e19a0bbdac 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -11517,25 +11517,28 @@ build_alloca_call_expr (tree size, unsigned int align, HOST_WIDE_INT max_size) } } -/* Create a new constant string literal and return a char* pointer to it. - The STRING_CST value is the LEN characters at STR. */ +/* Create a new constant string literal consisting of elements of type + ELTYPE and return a tree node representing char* pointer to it as + an ADDR_EXPR (ARRAY_REF (ELTYPE, ...)). The STRING_CST value is + the LEN bytes at STR (the representation of the string, which may + be wide). */ + tree -build_string_literal (int len, const char *str) +build_string_literal (int len, const char *str, + tree eltype /* = char_type_node */) { - tree t, elem, index, type; - - t = build_string (len, str); - elem = build_type_variant (char_type_node, 1, 0); - index = build_index_type (size_int (len - 1)); - type = build_array_type (elem, index); + tree t = build_string (len, str); + tree index = build_index_type (size_int (len - 1)); + eltype = build_type_variant (eltype, 1, 0); + tree type = build_array_type (eltype, index); TREE_TYPE (t) = type; TREE_CONSTANT (t) = 1; TREE_READONLY (t) = 1; TREE_STATIC (t) = 1; - type = build_pointer_type (elem); + type = build_pointer_type (eltype); t = build1 (ADDR_EXPR, type, - build4 (ARRAY_REF, elem, + build4 (ARRAY_REF, eltype, t, integer_zero_node, NULL_TREE, NULL_TREE)); return t; } diff --git a/gcc/tree.h b/gcc/tree.h index c21af9ff6d9..0767ee80ad3 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -4230,7 +4230,7 @@ extern tree build_call_expr_internal_loc_array (location_t, enum internal_fn, 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. */ -- 2.30.2