aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2025-03-07 23:59:34 +0100
committerJakub Jelinek <jakub@gcc.gnu.org>2025-03-07 23:59:34 +0100
commit622968990beee7499e951590258363545b4a3b57 (patch)
tree571e0e1d8d465449c6ec0afaa8a22aca32d177b7 /gcc
parent313edeeeb607fe32da5633cfb6f91977add446f6 (diff)
downloadgcc-622968990beee7499e951590258363545b4a3b57.zip
gcc-622968990beee7499e951590258363545b4a3b57.tar.gz
gcc-622968990beee7499e951590258363545b4a3b57.tar.bz2
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 <kees@kernel.org> Jakub Jelinek <jakub@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 <kees@kernel.org>
Diffstat (limited to 'gcc')
-rw-r--r--gcc/c-family/c.opt2
-rw-r--r--gcc/c/c-typeck.cc62
-rw-r--r--gcc/doc/invoke.texi15
-rw-r--r--gcc/testsuite/gcc.dg/Wcxx-compat-14.c2
-rw-r--r--gcc/testsuite/gcc.dg/Wcxx-compat-23.c33
-rw-r--r--gcc/testsuite/gcc.dg/Wcxx-compat-24.c33
-rw-r--r--gcc/testsuite/gcc.dg/Wunterminated-string-initialization.c31
7 files changed, 143 insertions, 35 deletions
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index f432203..6c6922a 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 a13989a..bc51cc2 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 e01d64c..3781697 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 6df0ee1..ef1f0c0 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 0000000..fd09149
--- /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 0000000..da652ae
--- /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 13d5dbc..5f25f02 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' }
+};