https://gcc.gnu.org/g:622968990beee7499e951590258363545b4a3b57

commit r15-7900-g622968990beee7499e951590258363545b4a3b57
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Fri Mar 7 23:59:34 2025 +0100

    c: do not warn about truncating NUL char when initializing nonstring arrays 
[PR117178]
    
    When initializing a nonstring char array when compiled with
    -Wunterminated-string-initialization the warning trips even when
    truncating the trailing NUL character from the string constant.  Only
    warn about this when running under -Wc++-compat since under C++ we should
    not initialize nonstrings from C strings.
    
    This patch separates the -Wunterminated-string-initialization and
    -Wc++-compat warnings, they are now independent option, the former implied
    by -Wextra, the latter not implied by anything.  If -Wc++-compat is in 
effect,
    it takes precedence over -Wunterminated-string-initialization and warns 
regardless
    of nonstring attribute, otherwise if -Wunterminated-string-initialization is
    enabled, it warns only if there isn't nonstring attribute.
    In all cases, the warnings and also pedwarn_init for even larger sizes now
    provide details on the lengths.
    
    2025-03-07  Kees Cook  <k...@kernel.org>
                Jakub Jelinek  <ja...@redhat.com>
    
            PR c/117178
    gcc/
            * doc/invoke.texi (Wunterminated-string-initialization): Document
            the new interaction between this warning and -Wc++-compat and that
            initialization of decls with nonstring attribute aren't warned 
about.
    gcc/c-family/
            * c.opt (Wunterminated-string-initialization): Don't depend on
            -Wc++-compat.
    gcc/c/
            * c-typeck.cc (digest_init): Add DECL argument.  Adjust wording of
            pedwarn_init for too long strings and provide details on the 
lengths,
            for string literals where just the trailing NULL doesn't fit warn 
for
            warn_cxx_compat with OPT_Wc___compat, wording which mentions "for 
C++"
            and provides details on lengths, otherwise for
            warn_unterminated_string_initialization adjust the warning, provide
            details on lengths and don't warn if get_attr_nonstring_decl (decl).
            (build_c_cast, store_init_value, output_init_element): Adjust
            digest_init callers.
    gcc/testsuite/
            * gcc.dg/Wunterminated-string-initialization.c: Add additional test
            coverage.
            * gcc.dg/Wcxx-compat-14.c: Check in dg-warning for "for C++" part of
            the diagnostics.
            * gcc.dg/Wcxx-compat-23.c: New test.
            * gcc.dg/Wcxx-compat-24.c: New test.
    
    Signed-off-by: Kees Cook <k...@kernel.org>

Diff:
---
 gcc/c-family/c.opt                                 |  2 +-
 gcc/c/c-typeck.cc                                  | 62 +++++++++++++---------
 gcc/doc/invoke.texi                                | 15 +++---
 gcc/testsuite/gcc.dg/Wcxx-compat-14.c              |  2 +-
 gcc/testsuite/gcc.dg/Wcxx-compat-23.c              | 33 ++++++++++++
 gcc/testsuite/gcc.dg/Wcxx-compat-24.c              | 33 ++++++++++++
 .../gcc.dg/Wunterminated-string-initialization.c   | 31 ++++++++++-
 7 files changed, 143 insertions(+), 35 deletions(-)

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index f432203b2ebc..6c6922ab2018 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1550,7 +1550,7 @@ C ObjC Var(warn_unsuffixed_float_constants) Warning
 Warn about unsuffixed float constants.
 
 Wunterminated-string-initialization
-C ObjC Var(warn_unterminated_string_initialization) Warning LangEnabledBy(C 
ObjC,Wextra || Wc++-compat)
+C ObjC Var(warn_unterminated_string_initialization) Warning LangEnabledBy(C 
ObjC,Wextra)
 Warn about character arrays initialized as unterminated character sequences 
with a string literal.
 
 Wunused
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index a13989a66076..bc51cc2693bf 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -116,8 +116,8 @@ static void push_member_name (tree);
 static int spelling_length (void);
 static char *print_spelling (char *);
 static void warning_init (location_t, int, const char *);
-static tree digest_init (location_t, tree, tree, tree, bool, bool, bool, bool,
-                        bool, bool);
+static tree digest_init (location_t, tree, tree, tree, tree, bool, bool, bool,
+                        bool, bool, bool);
 static void output_init_element (location_t, tree, tree, bool, tree, tree, 
bool,
                                 bool, struct obstack *);
 static void output_pending_init_elements (int, struct obstack *);
@@ -6844,7 +6844,7 @@ build_c_cast (location_t loc, tree type, tree expr)
          t = build_constructor_single (type, field, t);
          if (!maybe_const)
            t = c_wrap_maybe_const (t, true);
-         t = digest_init (loc, type, t,
+         t = digest_init (loc, field, type, t,
                           NULL_TREE, false, false, false, true, false, false);
          TREE_CONSTANT (t) = TREE_CONSTANT (value);
          return t;
@@ -8874,8 +8874,8 @@ store_init_value (location_t init_loc, tree decl, tree 
init, tree origtype)
     }
   bool constexpr_p = (VAR_P (decl)
                      && C_DECL_DECLARED_CONSTEXPR (decl));
-  value = digest_init (init_loc, type, init, origtype, npc, int_const_expr,
-                      arith_const_expr, true,
+  value = digest_init (init_loc, decl, type, init, origtype, npc,
+                      int_const_expr, arith_const_expr, true,
                       TREE_STATIC (decl) || constexpr_p, constexpr_p);
 
   /* Store the expression if valid; else report error.  */
@@ -9201,7 +9201,8 @@ check_constexpr_init (location_t loc, tree type, tree 
init,
              "type of object");
 }
 
-/* Digest the parser output INIT as an initializer for type TYPE.
+/* Digest the parser output INIT as an initializer for type TYPE
+   initializing DECL.
    Return a C expression of type TYPE to represent the initial value.
 
    If ORIGTYPE is not NULL_TREE, it is the original type of INIT.
@@ -9224,8 +9225,8 @@ check_constexpr_init (location_t loc, tree type, tree 
init,
    on initializers for 'constexpr' objects apply.  */
 
 static tree
-digest_init (location_t init_loc, tree type, tree init, tree origtype,
-            bool null_pointer_constant, bool int_const_expr,
+digest_init (location_t init_loc, tree decl, tree type, tree init,
+            tree origtype, bool null_pointer_constant, bool int_const_expr,
             bool arith_const_expr, bool strict_string,
             bool require_constant, bool require_constexpr)
 {
@@ -9360,27 +9361,38 @@ digest_init (location_t init_loc, tree type, tree init, 
tree origtype,
              && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST)
            {
              unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (inside_init);
-             unsigned unit = TYPE_PRECISION (typ1) / BITS_PER_UNIT;
-
-             /* Subtract the size of a single (possibly wide) character
-                because it's ok to ignore the terminating null char
-                that is counted in the length of the constant.  */
-             if (compare_tree_int (TYPE_SIZE_UNIT (type), len - unit) < 0)
-               pedwarn_init (init_loc, 0,
-                             ("initializer-string for array of %qT "
-                              "is too long"), typ1);
-             else if (warn_unterminated_string_initialization
-                      && compare_tree_int (TYPE_SIZE_UNIT (type), len) < 0)
-               warning_at (init_loc, OPT_Wunterminated_string_initialization,
-                           ("initializer-string for array of %qT "
-                            "is too long"), typ1);
+
              if (compare_tree_int (TYPE_SIZE_UNIT (type), len) < 0)
                {
-                 unsigned HOST_WIDE_INT size
+                 unsigned HOST_WIDE_INT avail
                    = tree_to_uhwi (TYPE_SIZE_UNIT (type));
+                 unsigned unit = TYPE_PRECISION (typ1) / BITS_PER_UNIT;
                  const char *p = TREE_STRING_POINTER (inside_init);
 
-                 inside_init = build_string (size, p);
+                 /* Construct truncated string.  */
+                 inside_init = build_string (avail, p);
+
+                 /* Subtract the size of a single (possibly wide) character
+                    because it may be ok to ignore the terminating NUL char
+                    that is counted in the length of the constant.  */
+                 if (len - unit > avail)
+                   pedwarn_init (init_loc, 0,
+                                 "initializer-string for array of %qT "
+                                 "is too long (%wu chars into %wu "
+                                 "available)", typ1, len, avail);
+                 else if (warn_cxx_compat)
+                   warning_at (init_loc, OPT_Wc___compat,
+                               "initializer-string for array of %qT "
+                               "is too long for C++ (%wu chars into %wu "
+                               "available)", typ1, len, avail);
+                 else if (warn_unterminated_string_initialization
+                          && get_attr_nonstring_decl (decl) == NULL_TREE)
+                   warning_at (init_loc,
+                               OPT_Wunterminated_string_initialization,
+                               "initializer-string for array of %qT "
+                               "truncates NUL terminator but destination "
+                               "lacks %qs attribute (%wu chars into %wu "
+                               "available)", typ1, "nonstring", len, avail);
                }
            }
 
@@ -11407,7 +11419,7 @@ output_init_element (location_t loc, tree value, tree 
origtype,
   if (!require_constexpr_value
       || !npc
       || TREE_CODE (constructor_type) != POINTER_TYPE)
-    new_value = digest_init (loc, type, new_value, origtype, npc,
+    new_value = digest_init (loc, field, type, new_value, origtype, npc,
                             int_const_expr, arith_const_expr, strict_string,
                             require_constant_value, require_constexpr_value);
   if (new_value == error_mark_node)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index e01d64c5229f..3781697ae55f 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -8689,17 +8689,20 @@ give a larger number of false positives and is 
deactivated by default.
 @opindex Wunterminated-string-initialization
 @opindex Wno-unterminated-string-initialization
 @item -Wunterminated-string-initialization @r{(C and Objective-C only)}
-Warn about character arrays
-initialized as unterminated character sequences
-with a string literal.
+Warn about character arrays initialized as unterminated character sequences
+with a string literal, unless the declaration being initialized has
+the @code{nonstring} attribute.
 For example:
 
 @smallexample
-char arr[3] = "foo";
+char arr[3] = "foo"; /* Warning.  */
+char arr2[3] __attribute__((nonstring)) = "bar"; /* No warning.  */
 @end smallexample
 
-This warning is enabled by @option{-Wextra} and @option{-Wc++-compat}.
-In C++, such initializations are an error.
+This warning is enabled by @option{-Wextra}.  If @option{-Wc++-compat}
+is enabled, the warning has slightly different wording and warns even
+if the declaration being initialized has the @code{nonstring} warning,
+as in C++ such initializations are an error.
 
 @opindex Warray-compare
 @opindex Wno-array-compare
diff --git a/gcc/testsuite/gcc.dg/Wcxx-compat-14.c 
b/gcc/testsuite/gcc.dg/Wcxx-compat-14.c
index 6df0ee197cca..ef1f0c09baa3 100644
--- a/gcc/testsuite/gcc.dg/Wcxx-compat-14.c
+++ b/gcc/testsuite/gcc.dg/Wcxx-compat-14.c
@@ -2,5 +2,5 @@
 /* { dg-options "-Wc++-compat" } */
 
 char a1[] = "a";
-char a2[1] = "a";      /* { dg-warning "initializer-string for array of 'char' 
is too long" } */
+char a2[1] = "a";      /* { dg-warning "initializer-string for array of 'char' 
is too long for C\\\+\\\+" } */
 char a3[2] = "a";
diff --git a/gcc/testsuite/gcc.dg/Wcxx-compat-23.c 
b/gcc/testsuite/gcc.dg/Wcxx-compat-23.c
new file mode 100644
index 000000000000..fd091495bb88
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wcxx-compat-23.c
@@ -0,0 +1,33 @@
+/* PR c/117178 */
+/* { dg-do compile } */
+/* { dg-options "-Wc++-compat -Wunterminated-string-initialization" } */
+
+char a1[] = "a";
+char a2[1] = "a";      /* { dg-warning "initializer-string for array of 'char' 
is too long for C\\\+\\\+" } */
+char a2nonstring[1] __attribute__((nonstring)) = "a";  /* { dg-warning 
"initializer-string for array of 'char' is too long for C\\\+\\\+" } */
+char a3[1] = "aa";     /* { dg-warning "initializer-string for array of 'char' 
is too long" } */
+char a4[2] = "a";
+
+struct has_str {
+  int a;
+  char str1[4];
+  char str2[4];
+  char str3[4];
+  char str4[4];
+  char tag1[4] __attribute__((nonstring));
+  char tag2[4] __attribute__((nonstring));
+  char tag3[4] __attribute__((nonstring));
+  char tag4[4] __attribute__((nonstring));
+  int b;
+};
+
+struct has_str foo = {
+  .str1 = "111",
+  .str2 = "2222",      /* { dg-warning "initializer-string for array of 'char' 
is too long for C\\\+\\\+" } */
+  .str3 = "33333",     /* { dg-warning "initializer-string for array of 'char' 
is too long" } */
+  .str4 = { '4', '4', '4', '4' },
+  .tag1 = "AAA",
+  .tag2 = "BBBB",      /* { dg-warning "initializer-string for array of 'char' 
is too long for C\\\+\\\+" } */
+  .tag3 = "CCCCC",     /* { dg-warning "initializer-string for array of 'char' 
is too long" } */
+  .tag4 = { 'D', 'D', 'D', 'D' }
+};
diff --git a/gcc/testsuite/gcc.dg/Wcxx-compat-24.c 
b/gcc/testsuite/gcc.dg/Wcxx-compat-24.c
new file mode 100644
index 000000000000..da652ae9fc8a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wcxx-compat-24.c
@@ -0,0 +1,33 @@
+/* PR c/117178 */
+/* { dg-do compile } */
+/* { dg-options "-Wc++-compat -Wno-unterminated-string-initialization" } */
+
+char a1[] = "a";
+char a2[1] = "a";      /* { dg-warning "initializer-string for array of 'char' 
is too long for C\\\+\\\+" } */
+char a2nonstring[1] __attribute__((nonstring)) = "a";  /* { dg-warning 
"initializer-string for array of 'char' is too long for C\\\+\\\+" } */
+char a3[1] = "aa";     /* { dg-warning "initializer-string for array of 'char' 
is too long" } */
+char a4[2] = "a";
+
+struct has_str {
+  int a;
+  char str1[4];
+  char str2[4];
+  char str3[4];
+  char str4[4];
+  char tag1[4] __attribute__((nonstring));
+  char tag2[4] __attribute__((nonstring));
+  char tag3[4] __attribute__((nonstring));
+  char tag4[4] __attribute__((nonstring));
+  int b;
+};
+
+struct has_str foo = {
+  .str1 = "111",
+  .str2 = "2222",      /* { dg-warning "initializer-string for array of 'char' 
is too long for C\\\+\\\+" } */
+  .str3 = "33333",     /* { dg-warning "initializer-string for array of 'char' 
is too long" } */
+  .str4 = { '4', '4', '4', '4' },
+  .tag1 = "AAA",
+  .tag2 = "BBBB",      /* { dg-warning "initializer-string for array of 'char' 
is too long for C\\\+\\\+" } */
+  .tag3 = "CCCCC",     /* { dg-warning "initializer-string for array of 'char' 
is too long" } */
+  .tag4 = { 'D', 'D', 'D', 'D' }
+};
diff --git a/gcc/testsuite/gcc.dg/Wunterminated-string-initialization.c 
b/gcc/testsuite/gcc.dg/Wunterminated-string-initialization.c
index 13d5dbc66400..5f25f0299197 100644
--- a/gcc/testsuite/gcc.dg/Wunterminated-string-initialization.c
+++ b/gcc/testsuite/gcc.dg/Wunterminated-string-initialization.c
@@ -1,6 +1,33 @@
+/* PR c/117178 */
 /* { dg-do compile } */
 /* { dg-options "-Wunterminated-string-initialization" } */
 
 char a1[] = "a";
-char a2[1] = "a";      /* { dg-warning "initializer-string for array of 'char' 
is too long" } */
-char a3[2] = "a";
+char a2[1] = "a";      /* { dg-warning "initializer-string for array of 'char' 
truncates" } */
+char a2nonstring[1] __attribute__((nonstring)) = "a";
+char a3[1] = "aa";     /* { dg-warning "initializer-string for array of 'char' 
is too long" } */
+char a4[2] = "a";
+
+struct has_str {
+  int a;
+  char str1[4];
+  char str2[4];
+  char str3[4];
+  char str4[4];
+  char tag1[4] __attribute__((nonstring));
+  char tag2[4] __attribute__((nonstring));
+  char tag3[4] __attribute__((nonstring));
+  char tag4[4] __attribute__((nonstring));
+  int b;
+};
+
+struct has_str foo = {
+  .str1 = "111",
+  .str2 = "2222",      /* { dg-warning "initializer-string for array of 'char' 
truncates" } */
+  .str3 = "33333",     /* { dg-warning "initializer-string for array of 'char' 
is too long" } */
+  .str4 = { '4', '4', '4', '4' },
+  .tag1 = "AAA",
+  .tag2 = "BBBB",
+  .tag3 = "CCCCC",     /* { dg-warning "initializer-string for array of 'char' 
is too long" } */
+  .tag4 = { 'D', 'D', 'D', 'D' }
+};

Reply via email to