Commit 91e3ec29 by Martin Sebor Committed by Martin Sebor

PR middle-end/87041 - -Wformat reading through null pointer on unreachable code

gcc/ChangeLog:

	PR middle-end/87041
	* gimple-ssa-sprintf.c (format_directive): Use %G to include
	inlining context.
	(sprintf_dom_walker::compute_format_length):
	Avoid setting POSUNDER4K here.
	(get_destination_size): Handle null argument values.
	(get_user_idx_format): New function.
	(sprintf_dom_walker::handle_gimple_call): Handle all printf-like
	functions, including user-defined with attribute format printf.
	Use %G to include inlining context.
	Set POSUNDER4K here.

gcc/c-family/ChangeLog:

	PR middle-end/87041
	* c-format.c (check_format_types): Avoid diagnosing null pointer
	arguments to printf-family of functions.

gcc/testsuite/ChangeLog:

	PR middle-end/87041
	* gcc.c-torture/execute/fprintf-2.c: New test.
	* gcc.c-torture/execute/printf-2.c: Same.
	* gcc.c-torture/execute/user-printf.c: Same.
	* gcc.dg/tree-ssa/builtin-fprintf-warn-1.c: Same.
	* gcc.dg/tree-ssa/builtin-printf-2.c: Same.
	* gcc.dg/tree-ssa/builtin-printf-warn-1.c: Same.
	* gcc.dg/tree-ssa/user-printf-warn-1.c: Same.

From-SVN: r265648
parent 448af20a
2018-10-30 Martin Sebor <msebor@redhat.com>
PR middle-end/87041
* gimple-ssa-sprintf.c (format_directive): Use %G to include
inlining context.
(sprintf_dom_walker::compute_format_length):
Avoid setting POSUNDER4K here.
(get_destination_size): Handle null argument values.
(get_user_idx_format): New function.
(sprintf_dom_walker::handle_gimple_call): Handle all printf-like
functions, including user-defined with attribute format printf.
Use %G to include inlining context.
Set POSUNDER4K here.
2018-10-30 Jan Hubicka <jh@suse.cz>
* params.def (lto-partitions): Bump from 32 to 128.
2018-10-30 Martin Sebor <msebor@redhat.com>
PR middle-end/87041
* c-format.c (check_format_types): Avoid diagnosing null pointer
arguments to printf-family of functions.
2018-10-30 Marek Polacek <polacek@redhat.com>
Implement P0892R2, explicit(bool).
......
......@@ -3123,8 +3123,11 @@ check_format_types (const substring_loc &fmt_loc,
warning (OPT_Wformat_, "writing through null pointer "
"(argument %d)", arg_num);
/* Check for reading through a NULL pointer. */
if (types->reading_from_flag
/* Check for reading through a NULL pointer. Ignore
printf-family of functions as they are checked for
null arguments by the middle-end. */
if (fki->conversion_specs != print_char_table
&& types->reading_from_flag
&& i == 0
&& cur_param != 0
&& integer_zerop (cur_param))
......
2018-10-30 Martin Sebor <msebor@redhat.com>
PR middle-end/87041
* gcc.c-torture/execute/fprintf-2.c: New test.
* gcc.c-torture/execute/printf-2.c: Same.
* gcc.c-torture/execute/user-printf.c: Same.
* gcc.dg/tree-ssa/builtin-fprintf-warn-1.c: Same.
* gcc.dg/tree-ssa/builtin-printf-2.c: Same.
* gcc.dg/tree-ssa/builtin-printf-warn-1.c: Same.
* gcc.dg/tree-ssa/user-printf-warn-1.c: Same.
2018-10-30 Marek Polacek <polacek@redhat.com>
Implement P0892R2, explicit(bool).
......
/* Verify that calls to fprintf don't get eliminated even if their
result on success can be computed at compile time (they can fail).
The calls can still be transformed into those of other functions.
{ dg-skip-if "requires io" { freestanding } } */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void)
{
char *tmpfname = tmpnam (0);
FILE *f = fopen (tmpfname, "w");
if (!f)
{
perror ("fopen for writing");
return 1;
}
fprintf (f, "1");
fprintf (f, "%c", '2');
fprintf (f, "%c%c", '3', '4');
fprintf (f, "%s", "5");
fprintf (f, "%s%s", "6", "7");
fprintf (f, "%i", 8);
fprintf (f, "%.1s\n", "9x");
fclose (f);
f = fopen (tmpfname, "r");
if (!f)
{
perror ("fopen for reading");
remove (tmpfname);
return 1;
}
char buf[12] = "";
if (1 != fscanf (f, "%s", buf))
{
perror ("fscanf");
fclose (f);
remove (tmpfname);
return 1;
}
fclose (f);
remove (tmpfname);
if (strcmp (buf, "123456789"))
abort ();
return 0;
}
/* Verify that calls to printf don't get eliminated even if their
result on success can be computed at compile time (they can fail).
The calls can still be transformed into those of other functions.
{ dg-skip-if "requires io" { freestanding } } */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
__attribute__ ((noipa)) void
write_file (void)
{
printf ("1");
printf ("%c", '2');
printf ("%c%c", '3', '4');
printf ("%s", "5");
printf ("%s%s", "6", "7");
printf ("%i", 8);
printf ("%.1s\n", "9x");
}
int main (void)
{
char *tmpfname = tmpnam (0);
FILE *f = freopen (tmpfname, "w", stdout);
if (!f)
{
perror ("fopen for writing");
return 1;
}
write_file ();
fclose (f);
f = fopen (tmpfname, "r");
if (!f)
{
perror ("fopen for reading");
remove (tmpfname);
return 1;
}
char buf[12] = "";
if (1 != fscanf (f, "%s", buf))
{
perror ("fscanf");
fclose (f);
remove (tmpfname);
return 1;
}
fclose (f);
remove (tmpfname);
if (strcmp (buf, "123456789"))
abort ();
return 0;
}
/* Verify that calls to a function declared wiith attribute format (printf)
don't get eliminated even if their result on success can be computed at
compile time (they can fail).
{ dg-skip-if "requires io" { freestanding } } */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void __attribute__ ((format (printf, 1, 2), noipa))
user_print (const char *fmt, ...)
{
va_list va;
va_start (va, fmt);
vfprintf (stdout, fmt, va);
va_end (va);
}
int main (void)
{
char *tmpfname = tmpnam (0);
FILE *f = freopen (tmpfname, "w", stdout);
if (!f)
{
perror ("fopen for writing");
return 1;
}
user_print ("1");
user_print ("%c", '2');
user_print ("%c%c", '3', '4');
user_print ("%s", "5");
user_print ("%s%s", "6", "7");
user_print ("%i", 8);
user_print ("%.1s\n", "9x");
fclose (f);
f = fopen (tmpfname, "r");
if (!f)
{
perror ("fopen for reading");
remove (tmpfname);
return 1;
}
char buf[12] = "";
if (1 != fscanf (f, "%s", buf))
{
perror ("fscanf");
fclose (f);
remove (tmpfname);
return 1;
}
fclose (f);
remove (tmpfname);
if (strcmp (buf, "123456789"))
abort ();
return 0;
}
/* PR middle-end/87041 - -Wformat "reading through null pointer" on
unreachable code
Test to verify that the applicable subset of -Wformat-overflow warnings
are issued for the fprintf function.
{ dg-do compile }
{ dg-options "-O -Wformat -Wformat-overflow=1 -ftrack-macro-expansion=0" }
{ dg-require-effective-target int32plus } */
/* When debugging, define LINE to the line number of the test case to exercise
and avoid exercising any of the others. The buffer and objsize macros
below make use of LINE to avoid warnings for other lines. */
#ifndef LINE
# define LINE 0
#endif
#define INT_MAX __INT_MAX__
typedef __SIZE_TYPE__ size_t;
#if !__cplusplus
typedef __WCHAR_TYPE__ wchar_t;
#endif
typedef __WINT_TYPE__ wint_t;
void sink (void*, ...);
/* Declare as void* to work around bug 87775. */
typedef void FILE;
int dummy_fprintf (FILE*, const char*, ...);
FILE *fp;
const char chr_no_nul = 'a';
const char arr_no_nul[] = { 'a', 'b' };
/* Helper to expand function to either __builtin_f or dummy_f to
make debugging GCC easy. */
#define T(...) \
(((!LINE || LINE == __LINE__) \
? __builtin_fprintf : dummy_fprintf) (fp, __VA_ARGS__))
/* Exercise the "%c" directive with constant arguments. */
void test_fprintf_c_const (int width)
{
/* Verify that a warning is issued for exceeding INT_MAX bytes and
not otherwise. */
T ("%*c", INT_MAX - 1, '1');
T ("%*c", INT_MAX, '1');
T ("X%*c", INT_MAX - 1, '1');
T ("X%*c", INT_MAX, '1'); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
T ("%*c%*c", INT_MAX - 1, '1', INT_MAX - 1, '2'); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
T ("%*cX", INT_MAX - 2, '1');
T ("%*cX", INT_MAX - 1, '1');
T ("%*cX", INT_MAX, '1'); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
if (width < INT_MAX - 1)
width = INT_MAX - 1;
T ("%*cX", width, '1');
T ("%*cXY", width, '1'); /* { dg-warning ".XY. directive output of 2 bytes causes result to exceed .INT_MAX." } */
/* Also exercise a non-constant format string. The warning points
to the line where the format is declared (see bug 87773) so avoid
triggering that bug here. */
const char *fmt = "%*cXYZ"; T (fmt, width, '1'); /* { dg-warning ".XYZ. directive output of 3 bytes causes result to exceed .INT_MAX." } */
}
/* Exercise the "%s" directive with constant arguments. */
void test_fprintf_s_const (int width)
{
const char *nulptr = 0;
T ("%s", nulptr); /* { dg-warning "\\\[-Wformat|-Wnonnull" } */
T ("%.0s", nulptr); /* { dg-warning ".%.0s. directive argument is null" } */
/* Verify no warning is issued for unreachable code. */
if (nulptr)
T ("%s", nulptr);
T ("%s", &chr_no_nul); /* { dg-warning ".%s. directive argument is not a nul-terminated string" "pr87756" { xfail *-*-* } } */
T ("%s", arr_no_nul); /* { dg-warning ".%s. directive argument is not a nul-terminated string" "pr87756" { xfail *-*-* } } */
/* Verify that output in excess of INT_MAX bytes is diagnosed even
when the size of the destination object is unknown. */
T ("%*s", INT_MAX - 1, "");
T ("%*s", INT_MAX, "");
T ("X%*s", INT_MAX, ""); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
if (width < INT_MAX - 1)
width = INT_MAX - 1;
T ("%*sX", width, "1");
T ("%*sXY", width, "1"); /* { dg-warning ".XY. directive output of 2 bytes causes result to exceed .INT_MAX." } */
}
const wchar_t wchr_no_nul = L'a';
const wchar_t warr_no_nul[] = { L'a', L'b' };
/* Exercise the "%s" directive with constant arguments. */
void test_fprintf_ls_const (int width)
{
const wchar_t *nulptr = 0;
T ("%ls", nulptr); /* { dg-warning ".%ls. directive argument is null" } */
T ("%.0ls", nulptr); /* { dg-warning ".%.0ls. directive argument is null" } */
/* Verify no warning is issued for unreachable code. */
if (nulptr)
T ("%ls", nulptr);
T ("%ls", &wchr_no_nul); /* { dg-warning ".%ls. directive argument is not a nul-terminated string" "pr87756" { xfail *-*-* } } */
T ("%ls", warr_no_nul); /* { dg-warning ".%ls. directive argument is not a nul-terminated string" "pr87756" { xfail *-*-* } } */
/* Verify that output in excess of INT_MAX bytes is diagnosed even
when the size of the destination object is unknown. */
T ("%*ls", INT_MAX - 1, L"");
T ("%*ls", INT_MAX, L"");
T ("X%*ls", INT_MAX, L""); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
if (width < INT_MAX - 1)
width = INT_MAX - 1;
T ("%*lsX", width, L"1");
T ("%*lsXY", width, L"1"); /* { dg-warning ".XY. directive output of 2 bytes causes result to exceed .INT_MAX." } */
}
/* Verify that tests for the result of calls to fprintf, printf, vfprintf,
and vprintf are not eliminated, even if it is possible to determine
their value on success (the calls may fail and return a negative value).
{ dg-do compile }
{ dg-options "-O2 -fdump-tree-optimized" } */
typedef struct FILE FILE;
typedef __builtin_va_list va_list;
extern int printf (const char *, ...);
extern int printf_unlocked (const char *, ...);
extern int vprintf (const char *, va_list);
extern int fprintf (FILE*, const char *, ...);
extern int fprintf_unlocked (FILE*, const char *, ...);
extern int vfprintf (FILE*, const char *, va_list);
#define fprintf_chk __builtin___fprintf_chk
#define printf_chk __builtin___printf_chk
#define vfprintf_chk __builtin___vfprintf_chk
#define vprintf_chk __builtin___vprintf_chk
#define CAT(s, n) s ## n
#define KEEP(func, line) CAT (func ## _test_on_line_, line)
/* Emit one call to a function named call_on_line_NNN when the result
of the call FUNC ARGS is less than zero, zero, or greater than zero.
This verifies that the expression is not eliminated.
For known output it is possible to bound the return value to
[INT_MIN, -1] U [0, N] with N being the size of the output, but
that optimization isn't implemented (yet). */
#define T(func, args) \
do { \
extern void KEEP (func, __LINE__)(const char*); \
if ((func args) < 0) KEEP (func, __LINE__)("< 0"); \
if ((func args) >= 0) KEEP (func, __LINE__)(">= 0"); \
} while (0)
void test_fprintf (FILE *f, const char *s)
{
/* Here the result is in [INT_MIN, 0], i.e., it cannot be positive.
It might be a useful enhancement to implement this optimization. */
T (fprintf, (f, ""));
T (fprintf, (f, "1"));
T (fprintf, (f, "123"));
T (fprintf, (f, s));
T (fprintf, (f, "%c", 0));
T (fprintf, (f, "%c", '1'));
T (fprintf, (f, "%c", *s));
T (fprintf, (f, "%s", ""));
T (fprintf, (f, "%s", "1"));
T (fprintf, (f, "%.0s", ""));
T (fprintf, (f, "%.0s", s));
/* { dg-final { scan-tree-dump-times " fprintf_test_on_line_" 22 "optimized"} } */
}
void test_fprintf_unlocked (FILE *f, const char *s)
{
T (fprintf_unlocked, (f, ""));
T (fprintf_unlocked, (f, "1"));
T (fprintf_unlocked, (f, "123"));
T (fprintf_unlocked, (f, s));
T (fprintf_unlocked, (f, "%c", 0));
T (fprintf_unlocked, (f, "%c", '1'));
T (fprintf_unlocked, (f, "%c", *s));
T (fprintf_unlocked, (f, "%s", ""));
T (fprintf_unlocked, (f, "%s", "1"));
T (fprintf_unlocked, (f, "%.0s", ""));
T (fprintf_unlocked, (f, "%.0s", s));
/* { dg-final { scan-tree-dump-times " fprintf_unlocked_test_on_line_" 22 "optimized"} } */
}
void test_fprintf_chk (FILE *f, const char *s)
{
T (fprintf_chk, (f, 0, ""));
T (fprintf_chk, (f, 0, "1"));
T (fprintf_chk, (f, 0, "123"));
T (fprintf_chk, (f, 0, s));
T (fprintf_chk, (f, 0, "%c", 0));
T (fprintf_chk, (f, 0, "%c", '1'));
T (fprintf_chk, (f, 0, "%c", *s));
T (fprintf_chk, (f, 0, "%s", ""));
T (fprintf_chk, (f, 0, "%s", "1"));
T (fprintf_chk, (f, 0, "%.0s", ""));
T (fprintf_chk, (f, 0, "%.0s", s));
/* { dg-final { scan-tree-dump-times " __builtin___fprintf_chk_test_on_line_" 22 "optimized"} } */
}
void test_vfprintf (FILE *f, va_list va)
{
T (vfprintf, (f, "", va));
T (vfprintf, (f, "123", va));
T (vfprintf, (f, "%c", va));
T (vfprintf, (f, "%.0s", va));
/* { dg-final { scan-tree-dump-times " vfprintf_test_on_line_" 8 "optimized"} } */
}
void test_vfprintf_chk (FILE *f, va_list va)
{
T (vfprintf_chk, (f, 0, "", va));
T (vfprintf_chk, (f, 0, "123", va));
T (vfprintf_chk, (f, 0, "%c", va));
T (vfprintf_chk, (f, 0, "%.0s", va));
/* { dg-final { scan-tree-dump-times " __builtin___vfprintf_chk_test_on_line_" 8 "optimized"} } */
}
void test_printf (const char *s)
{
T (printf, (""));
T (printf, ("1"));
T (printf, ("123"));
T (printf, (s));
T (printf, ("%c", 0));
T (printf, ("%c", '1'));
T (printf, ("%c", *s));
T (printf, ("%s", ""));
T (printf, ("%s", "1"));
T (printf, ("%.0s", ""));
T (printf, ("%.0s", s));
/* { dg-final { scan-tree-dump-times " printf_test_on_line_" 22 "optimized"} } */
}
void test_printf_unlocked (const char *s)
{
T (printf_unlocked, (""));
T (printf_unlocked, ("1"));
T (printf_unlocked, ("123"));
T (printf_unlocked, (s));
T (printf_unlocked, ("%c", 0));
T (printf_unlocked, ("%c", '1'));
T (printf_unlocked, ("%c", *s));
T (printf_unlocked, ("%s", ""));
T (printf_unlocked, ("%s", "1"));
T (printf_unlocked, ("%.0s", ""));
T (printf_unlocked, ("%.0s", s));
/* { dg-final { scan-tree-dump-times " printf_unlocked_test_on_line_" 22 "optimized"} } */
}
void test_printf_chk (const char *s)
{
T (printf_chk, (0, ""));
T (printf_chk, (0, "1"));
T (printf_chk, (0, "123"));
T (printf_chk, (0, s));
T (printf_chk, (0, "%c", 0));
T (printf_chk, (0, "%c", '1'));
T (printf_chk, (0, "%c", *s));
T (printf_chk, (0, "%s", ""));
T (printf_chk, (0, "%s", "1"));
T (printf_chk, (0, "%.0s", ""));
T (printf_chk, (0, "%.0s", s));
/* { dg-final { scan-tree-dump-times " __builtin___printf_chk_test_on_line_" 22 "optimized"} } */
}
void test_vprintf (va_list va)
{
T (vprintf, ("", va));
T (vprintf, ("123", va));
T (vprintf, ("%c", va));
T (vprintf, ("%.0s", va));
/* { dg-final { scan-tree-dump-times " vprintf_test_on_line_" 8 "optimized"} } */
}
void test_vprintf_chk (va_list va)
{
T (vprintf_chk, (0, "", va));
T (vprintf_chk, (0, "123", va));
T (vprintf_chk, (0, "%c", va));
T (vprintf_chk, (0, "%.0s", va));
/* { dg-final { scan-tree-dump-times " __builtin___vprintf_chk_test_on_line_" 8 "optimized"} } */
}
/* PR middle-end/87041 - -Wformat "reading through null pointer" on
unreachable code
Test to verify that the applicable subset of -Wformat-overflow warnings
are issued for the printf function.
{ dg-do compile }
{ dg-options "-O -Wformat -Wformat-overflow=1 -ftrack-macro-expansion=0" }
{ dg-require-effective-target int32plus } */
/* When debugging, define LINE to the line number of the test case to exercise
and avoid exercising any of the others. The buffer and objsize macros
below make use of LINE to avoid warnings for other lines. */
#ifndef LINE
# define LINE 0
#endif
#define INT_MAX __INT_MAX__
typedef __SIZE_TYPE__ size_t;
#if !__cplusplus
typedef __WCHAR_TYPE__ wchar_t;
#endif
typedef __WINT_TYPE__ wint_t;
typedef unsigned char UChar;
void sink (void*, ...);
int dummy_printf (const char*, ...);
const char chr_no_nul = 'a';
const char arr_no_nul[] = { 'a', 'b' };
/* Helper to expand function to either __builtin_f or dummy_f to
make debugging GCC easy. */
#define T(...) \
(((!LINE || LINE == __LINE__) \
? __builtin_printf : dummy_printf) (__VA_ARGS__))
/* Exercise the "%c" directive with constant arguments. */
void test_printf_c_const (int width)
{
/* Verify that a warning is issued for exceeding INT_MAX bytes and
not otherwise. */
T ("%*c", INT_MAX - 1, '1');
T ("%*c", INT_MAX, '1');
T ("X%*c", INT_MAX - 1, '1');
T ("X%*c", INT_MAX, '1'); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
T ("%*c%*c", INT_MAX - 1, '1', INT_MAX - 1, '2'); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
T ("%*cX", INT_MAX - 2, '1');
T ("%*cX", INT_MAX - 1, '1');
T ("%*cX", INT_MAX, '1'); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
if (width < INT_MAX - 1)
width = INT_MAX - 1;
T ("%*cX", width, '1');
T ("%*cXY", width, '1'); /* { dg-warning ".XY. directive output of 2 bytes causes result to exceed .INT_MAX." } */
/* Also exercise a non-constant format string. The warning points
to the line where the format is declared (see bug 87773) so avoid
triggering that bug here. */
const char *fmt = "%*cXYZ"; T (fmt, width, '1'); /* { dg-warning ".XYZ. directive output of 3 bytes causes result to exceed .INT_MAX." } */
}
/* Exercise the "%s" directive with constant arguments. */
void test_printf_s_const (int width)
{
const char *nulptr = 0;
T ("%s", nulptr); /* { dg-warning "\\\[-Wformat|-Wnonnull]" } */
T ("%.0s", nulptr); /* { dg-warning ".%.0s. directive argument is null" } */
/* Verify no warning is issued for unreachable code. */
if (nulptr)
T ("%s", nulptr);
T ("%s", &chr_no_nul); /* { dg-warning ".%s. directive argument is not a nul-terminated string" "pr87756" { xfail *-*-* } } */
T ("%s", arr_no_nul); /* { dg-warning ".%s. directive argument is not a nul-terminated string" } */
/* Verify that output in excess of INT_MAX bytes is diagnosed even
when the size of the destination object is unknown. */
T ("%*s", INT_MAX - 1, "");
T ("%*s", INT_MAX, "");
T ("X%*s", INT_MAX, ""); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
if (width < INT_MAX - 1)
width = INT_MAX - 1;
T ("%*sX", width, "1");
T ("%*sXY", width, "1"); /* { dg-warning ".XY. directive output of 2 bytes causes result to exceed .INT_MAX." } */
}
const wchar_t wchr_no_nul = L'a';
const wchar_t warr_no_nul[] = { L'a', L'b' };
/* Exercise the "%s" directive with constant arguments. */
void test_printf_ls_const (int width)
{
const wchar_t *nulptr = 0;
T ("%ls", nulptr); /* { dg-warning ".%ls. directive argument is null" } */
T ("%.0ls", nulptr); /* { dg-warning ".%.0ls. directive argument is null" } */
/* Verify no warning is issued for unreachable code. */
if (nulptr)
T ("%ls", nulptr);
T ("%ls", &wchr_no_nul); /* { dg-warning ".%ls. directive argument is not a nul-terminated string" "pr87756" { xfail *-*-* } } */
T ("%ls", warr_no_nul); /* { dg-warning ".%ls. directive argument is not a nul-terminated string" "pr87756" { xfail *-*-* } } */
/* Verify that output in excess of INT_MAX bytes is diagnosed even
when the size of the destination object is unknown. */
T ("%*ls", INT_MAX - 1, L"");
T ("%*ls", INT_MAX, L"");
T ("X%*ls", INT_MAX, L""); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
if (width < INT_MAX - 1)
width = INT_MAX - 1;
T ("%*lsX", width, L"1");
T ("%*lsXY", width, L"1"); /* { dg-warning ".XY. directive output of 2 bytes causes result to exceed .INT_MAX." } */
}
/* PR middle-end/87041 - -Wformat "reading through null pointer" on
unreachable code
Test to verify that the applicable subset of -Wformat-overflow warnings
are issued for user-defined function declared attribute format printf.
{ dg-do compile }
{ dg-options "-O -Wformat -Wformat-overflow=1 -ftrack-macro-expansion=0" }
{ dg-require-effective-target int32plus } */
/* When debugging, define LINE to the line number of the test case to exercise
and avoid exercising any of the others. The buffer and objsize macros
below make use of LINE to avoid warnings for other lines. */
#ifndef LINE
# define LINE 0
#endif
#define INT_MAX __INT_MAX__
#define ATTR(...) __attribute__ ((__VA_ARGS__))
typedef __SIZE_TYPE__ size_t;
#if !__cplusplus
typedef __WCHAR_TYPE__ wchar_t;
#endif
typedef __WINT_TYPE__ wint_t;
ATTR (format (printf, 2, 3)) void
user_print (char*, const char*, ...);
ATTR (format (printf, 2, 3), nonnull) void
user_print_nonnull (char*, const char*, ...);
ATTR (format (printf, 2, 3), nonnull (2)) void
user_print_nonnull_fmt (char*, const char*, ...);
ATTR (format (printf, 2, 4), nonnull (3)) void
user_print_nonnull_other (char*, const char*, char*, ...);
void dummy_print (char*, const char*, ...);
const char chr_no_nul = 'a';
const char arr_no_nul[] = { 'a', 'b' };
/* Helper to expand function to either __builtin_f or dummy_f to
make debugging GCC easy. */
#define T(...) \
(((!LINE || LINE == __LINE__) \
? user_print : dummy_print) (0, __VA_ARGS__))
/* Exercise the "%c" directive with constant arguments. */
void test_user_print_format_string (void)
{
char *null = 0;
/* Verify that no warning is issued for a null format string unless
the corresponding parameter is declared nonnull. */
user_print (0, null);
user_print_nonnull ("x", "y");
user_print_nonnull ("x", null); /* { dg-warning "\\\[-Wnonnull]" } */
user_print_nonnull_fmt (null, "x");
user_print_nonnull_fmt (0, null); /* { dg-warning "\\\[-Wnonnull]" } */
user_print_nonnull_other (null, "x", "y");
user_print_nonnull_other (null, "x", null); /* { dg-warning "\\\[-Wnonnull]" } */
}
/* Exercise the "%c" directive with constant arguments. */
void test_user_print_c_const (int width)
{
/* Verify that a warning is issued for exceeding INT_MAX bytes and
not otherwise. */
T ("%*c", INT_MAX - 1, '1');
T ("%*c", INT_MAX, '1');
T ("X%*c", INT_MAX - 1, '1');
T ("X%*c", INT_MAX, '1'); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
T ("%*c%*c", INT_MAX - 1, '1', INT_MAX - 1, '2'); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
T ("%*cX", INT_MAX - 2, '1');
T ("%*cX", INT_MAX - 1, '1');
T ("%*cX", INT_MAX, '1'); /* { dg-warning "output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
if (width < INT_MAX - 1)
width = INT_MAX - 1;
T ("%*cX", width, '1');
T ("%*cXY", width, '1'); /* { dg-warning ".XY. directive output of 2 bytes causes result to exceed .INT_MAX." } */
/* Also exercise a non-constant format string. The warning points
to the line where the format is declared (see bug 87773) so avoid
triggering that bug here. */
const char *fmt = "%*cXYZ"; T (fmt, width, '1'); /* { dg-warning ".XYZ. directive output of 3 bytes causes result to exceed .INT_MAX." } */
}
/* Exercise the "%s" directive with constant arguments. */
void test_user_print_s_const (int width)
{
const char *null = 0;
T ("%s", null); /* { dg-warning ".%s. directive argument is null" } */
T ("%.0s", null); /* { dg-warning ".%.0s. directive argument is null" } */
/* Verify no warning is issued for unreachable code. */
if (null)
T ("%s", null);
T ("%s", &chr_no_nul); /* { dg-warning ".%s. directive argument is not a nul-terminated string" "pr87756" { xfail *-*-* } } */
T ("%s", arr_no_nul); /* { dg-warning ".%s. directive argument is not a nul-terminated string" } */
/* Verify that output in excess of INT_MAX bytes is diagnosed even
when the size of the destination object is unknown. */
T ("%*s", INT_MAX - 1, "");
T ("%*s", INT_MAX, "");
T ("X%*s", INT_MAX, ""); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
if (width < INT_MAX - 1)
width = INT_MAX - 1;
T ("%*sX", width, "1");
T ("%*sXY", width, "1"); /* { dg-warning ".XY. directive output of 2 bytes causes result to exceed .INT_MAX." } */
}
const wchar_t wchr_no_nul = L'a';
const wchar_t warr_no_nul[] = { L'a', L'b' };
/* Exercise the "%s" directive with constant arguments. */
void test_user_print_ls_const (int width)
{
const wchar_t *null = 0;
T ("%ls", null); /* { dg-warning ".%ls. directive argument is null" } */
T ("%.0ls", null); /* { dg-warning ".%.0ls. directive argument is null" } */
/* Verify no warning is issued for unreachable code. */
if (null)
T ("%ls", null);
T ("%ls", &wchr_no_nul); /* { dg-warning ".%ls. directive argument is not a nul-terminated string" "pr87756" { xfail *-*-* } } */
T ("%ls", warr_no_nul); /* { dg-warning ".%ls. directive argument is not a nul-terminated string" "pr87756" { xfail *-*-* } } */
/* Verify that output in excess of INT_MAX bytes is diagnosed even
when the size of the destination object is unknown. */
T ("%*ls", INT_MAX - 1, L"");
T ("%*ls", INT_MAX, L"");
T ("X%*ls", INT_MAX, L""); /* { dg-warning "directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
if (width < INT_MAX - 1)
width = INT_MAX - 1;
T ("%*lsX", width, L"1");
T ("%*lsXY", width, L"1"); /* { dg-warning ".XY. directive output of 2 bytes causes result to exceed .INT_MAX." } */
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment