In the committed patch I forgot that the CONSTRUCTOR to STRING_CST
transformation introduced this summer only takes place for arrays
of char and not also those of wide characters. That resulted in
sprintf incorrectly getting STRING_CSTs for individual elements
of constant wide character arrays like in the example below, and
issuing a warning when that element was not nul and was passed
to %s.
The test that was supposed top exercise this was incomplete (it
was testing the %s directive rather than %ls), and so the bug
sneaked in unnoticed.
The attached patch restricts the change committed in r266418 to
only do its thing for non-array variables of wide character types
or for plain chars. It also moves the wide string test into a new
file and corrects the problems there. Since the the CONSTRUCTOR
to STRING_CST transformation doesn't take place for wide arrays
the test fails a bunch of cases. I have XFAILed those until
the transformation is implemented.
I have tested this fix on x86_64-linux with no regression. Unless
there are concerns with this I'll commit it in the next day or so
to unblock the failing Glibc builds.
Martin
On 11/26/18 12:27 PM, Martin Sebor wrote:
On 11/26/18 10:27 AM, Joseph Myers wrote:
On Fri, 23 Nov 2018, Martin Sebor wrote:
I have now committed this patch as r266418.
This commit introduced spurious warnings that break the glibc testsuite
build.
bug-ungetwc2.c:62:17: error: '%ls' directive argument is not a
nul-terminated string [-Werror=format-overflow=]
62 | fprintf (fp, "%ls", write_wchars);
| ^~~ ~~~~~~~~~~~~
bug-ungetwc2.c:10:15: note: referenced argument declared here
10 | const wchar_t write_wchars[] = {L'A', 0x00C4, L'B', L'\0'};
| ^~~~~~~~~~~~
Reduced test (compile with -Wall, should not produce any warnings but
does after that commit):
const __WCHAR_TYPE__ s[] = { L'A', L'\0' };
void f (void) { __builtin_printf ("%ls", s); }
Thanks for the test case. I see the problem and I'm testing a simple
fix.
Martin
gcc/ChangeLog:
* expr.c (string_constant): Handle top-level decls of all character
types and subobjects of narrow character type.
gcc/testsuite/ChangeLog:
* gcc.dg/warn-sprintf-no-nul-2.c: Move incomplete tests from here...
* gcc.dg/warn-sprintf-no-nul-3.c: ...to here and complete them.
Index: gcc/expr.c
===================================================================
--- gcc/expr.c (revision 266472)
+++ gcc/expr.c (working copy)
@@ -11497,10 +11497,16 @@ string_constant (tree arg, tree *ptr_offset, tree
if (decl)
*decl = array;
- if (TREE_CODE (init) == INTEGER_CST)
+ if (TREE_CODE (init) == INTEGER_CST
+ && (TREE_CODE (TREE_TYPE (array)) == INTEGER_TYPE
+ || TYPE_MAIN_VARIANT (eltype) == char_type_node))
{
/* For a reference to (address of) a single constant character,
- store the native representation of the character in CHARBUF. */
+ store the native representation of the character in CHARBUF.
+ If the reference is to an element of an array or a member
+ of a struct, only consider narrow characters until ctors
+ for wide character arrays are transformed to STRING_CSTs
+ like those for narrow arrays. */
unsigned char charbuf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT];
int len = native_encode_expr (init, charbuf, sizeof charbuf, 0);
if (len > 0)
Index: gcc/testsuite/gcc.dg/warn-sprintf-no-nul-2.c
===================================================================
--- gcc/testsuite/gcc.dg/warn-sprintf-no-nul-2.c (revision 266472)
+++ gcc/testsuite/gcc.dg/warn-sprintf-no-nul-2.c (working copy)
@@ -3,9 +3,6 @@
{ 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);
@@ -71,61 +68,3 @@ void test_sprintf_s (void)
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" } */
-}
Index: gcc/testsuite/gcc.dg/warn-sprintf-no-nul-3.c
===================================================================
--- gcc/testsuite/gcc.dg/warn-sprintf-no-nul-3.c (nonexistent)
+++ gcc/testsuite/gcc.dg/warn-sprintf-no-nul-3.c (working copy)
@@ -0,0 +1,71 @@
+/* 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 __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 wchar_t wnul = L'\0';
+const wchar_t wnonul = L'a';
+const wchar_t wcs3[] = L"123";
+
+const struct
+{
+ wchar_t a, b, s[3];
+} w1 = { L'\0', L'b', L"123" },
+ w2[2] = {
+ { L'\0', L'c', L"12" },
+ { L'd', L'\0', L"123" }
+ };
+
+void test_sprintf_ls (void)
+{
+ T (sprintf (D, "%ls", &wnul));
+ T (sprintf (D, "%ls", &wnonul)); /* { dg-warning "nul-terminated" } */
+ T (sprintf (D, "%.1ls", &wnonul));
+ T (sprintf (D, "%.2ls", &wnonul)); /* { dg-warning "nul-terminated" } */
+
+ T (sprintf (D, "%ls", &w1.a));
+ T (sprintf (D, "%ls", &w1.b)); /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+ T (sprintf (D, "%.1ls", &w1.b));
+ T (sprintf (D, "%.2ls", &w1.b)); /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+ T (sprintf (D, "%ls", w1.s)); /* { dg-warning "nul-terminated" } */
+ T (sprintf (D, "%.3ls", w1.s));
+ T (sprintf (D, "%.4ls", w1.s)); /* { dg-warning "nul-terminated" } */
+
+ T (sprintf (D, "%.2ls", w1.s + 1));
+ T (sprintf (D, "%.3ls", w1.s + 1)); /* { dg-warning "nul-terminated" } */
+
+ T (sprintf (D, "%ls", &w2[0].a));
+ T (sprintf (D, "%ls", &w2[0].b)); /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+ T (sprintf (D, "%.1ls", &w2[0].b));
+ T (sprintf (D, "%.2ls", &w2[0].b)); /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+ T (sprintf (D, "%ls", w2[0].s));
+ T (sprintf (D, "%.3ls", w2[0].s));
+ T (sprintf (D, "%.4ls", w2[0].s));
+
+ T (sprintf (D, "%.2ls", w2[0].s + 1));
+ T (sprintf (D, "%.3ls", w2[0].s + 1));
+
+ T (sprintf (D, "%ls", &w2[1].a)); /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+ T (sprintf (D, "%.1ls", &w2[1].a));
+ T (sprintf (D, "%.2ls", &w2[1].a)); /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+ T (sprintf (D, "%ls", &w2[1].b));
+ T (sprintf (D, "%ls", w2[1].s)); /* { dg-warning "nul-terminated" } */
+ T (sprintf (D, "%.3ls", w2[1].s));
+ T (sprintf (D, "%.4ls", w2[1].s)); /* { dg-warning "nul-terminated" } */
+
+ T (sprintf (D, "%.2ls", w2[1].s + 1));
+ T (sprintf (D, "%.3ls", w2[1].s + 1));/* { dg-warning "nul-terminated" } */
+
+ T (sprintf (D, "%ls", &wcs3[3]));
+ T (sprintf (D, "%ls", &wcs3[4])); /* { dg-warning "\\\[-Warray-bounds" } */
+}