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> 2018-10-30 Jan Hubicka <jh@suse.cz>
* params.def (lto-partitions): Bump from 32 to 128. * 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> 2018-10-30 Marek Polacek <polacek@redhat.com>
Implement P0892R2, explicit(bool). Implement P0892R2, explicit(bool).
......
...@@ -3123,8 +3123,11 @@ check_format_types (const substring_loc &fmt_loc, ...@@ -3123,8 +3123,11 @@ check_format_types (const substring_loc &fmt_loc,
warning (OPT_Wformat_, "writing through null pointer " warning (OPT_Wformat_, "writing through null pointer "
"(argument %d)", arg_num); "(argument %d)", arg_num);
/* Check for reading through a NULL pointer. */ /* Check for reading through a NULL pointer. Ignore
if (types->reading_from_flag 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 && i == 0
&& cur_param != 0 && cur_param != 0
&& integer_zerop (cur_param)) && integer_zerop (cur_param))
......
...@@ -68,6 +68,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -68,6 +68,7 @@ along with GCC; see the file COPYING3. If not see
#include "intl.h" #include "intl.h"
#include "langhooks.h" #include "langhooks.h"
#include "attribs.h"
#include "builtins.h" #include "builtins.h"
#include "stor-layout.h" #include "stor-layout.h"
...@@ -2796,8 +2797,9 @@ format_directive (const sprintf_dom_walker::call_info &info, ...@@ -2796,8 +2797,9 @@ format_directive (const sprintf_dom_walker::call_info &info,
if (fmtres.nullp) if (fmtres.nullp)
{ {
fmtwarn (dirloc, argloc, NULL, info.warnopt (), fmtwarn (dirloc, argloc, NULL, info.warnopt (),
"%<%.*s%> directive argument is null", "%G%<%.*s%> directive argument is null",
dirlen, target_to_host (hostdir, sizeof hostdir, dir.beg)); info.callstmt, dirlen,
target_to_host (hostdir, sizeof hostdir, dir.beg));
/* Don't bother processing the rest of the format string. */ /* Don't bother processing the rest of the format string. */
res->warned = true; res->warned = true;
...@@ -3475,7 +3477,6 @@ sprintf_dom_walker::compute_format_length (call_info &info, ...@@ -3475,7 +3477,6 @@ sprintf_dom_walker::compute_format_length (call_info &info,
by the known range [0, 0] (with no conversion resulting in a failure by the known range [0, 0] (with no conversion resulting in a failure
or producing more than 4K bytes) until determined otherwise. */ or producing more than 4K bytes) until determined otherwise. */
res->knownrange = true; res->knownrange = true;
res->posunder4k = true;
res->floating = false; res->floating = false;
res->warned = false; res->warned = false;
...@@ -3518,6 +3519,10 @@ sprintf_dom_walker::compute_format_length (call_info &info, ...@@ -3518,6 +3519,10 @@ sprintf_dom_walker::compute_format_length (call_info &info,
static unsigned HOST_WIDE_INT static unsigned HOST_WIDE_INT
get_destination_size (tree dest) get_destination_size (tree dest)
{ {
/* When there is no destination return -1. */
if (!dest)
return HOST_WIDE_INT_M1U;
/* Initialize object size info before trying to compute it. */ /* Initialize object size info before trying to compute it. */
init_object_sizes (); init_object_sizes ();
...@@ -3738,6 +3743,37 @@ try_simplify_call (gimple_stmt_iterator *gsi, ...@@ -3738,6 +3743,37 @@ try_simplify_call (gimple_stmt_iterator *gsi,
return false; return false;
} }
/* Return the zero-based index of the format string argument of a printf
like function and set *IDX_ARGS to the first format argument. When
no such index exists return UINT_MAX. */
static unsigned
get_user_idx_format (tree fndecl, unsigned *idx_args)
{
tree attrs = lookup_attribute ("format", DECL_ATTRIBUTES (fndecl));
if (!attrs)
attrs = lookup_attribute ("format", TYPE_ATTRIBUTES (TREE_TYPE (fndecl)));
if (!attrs)
return UINT_MAX;
attrs = TREE_VALUE (attrs);
tree archetype = TREE_VALUE (attrs);
if (strcmp ("printf", IDENTIFIER_POINTER (archetype)))
return UINT_MAX;
attrs = TREE_CHAIN (attrs);
tree fmtarg = TREE_VALUE (attrs);
attrs = TREE_CHAIN (attrs);
tree elliparg = TREE_VALUE (attrs);
/* Attribute argument indices are 1-based but we use zero-based. */
*idx_args = tree_to_uhwi (elliparg) - 1;
return tree_to_uhwi (fmtarg) - 1;
}
/* Determine if a GIMPLE CALL is to one of the sprintf-like built-in /* Determine if a GIMPLE CALL is to one of the sprintf-like built-in
functions and if so, handle it. Return true if the call is removed functions and if so, handle it. Return true if the call is removed
and gsi_next should not be performed in the caller. */ and gsi_next should not be performed in the caller. */
...@@ -3748,29 +3784,93 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -3748,29 +3784,93 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
call_info info = call_info (); call_info info = call_info ();
info.callstmt = gsi_stmt (*gsi); info.callstmt = gsi_stmt (*gsi);
if (!gimple_call_builtin_p (info.callstmt, BUILT_IN_NORMAL)) info.func = gimple_call_fndecl (info.callstmt);
if (!info.func)
return false; return false;
info.func = gimple_call_fndecl (info.callstmt);
info.fncode = DECL_FUNCTION_CODE (info.func); info.fncode = DECL_FUNCTION_CODE (info.func);
/* Format string argument number (valid for all functions). */
unsigned idx_format = UINT_MAX;
if (!gimple_call_builtin_p (info.callstmt, BUILT_IN_NORMAL))
{
unsigned idx_args;
idx_format = get_user_idx_format (info.func, &idx_args);
if (idx_format == UINT_MAX)
return false;
info.argidx = idx_args;
}
/* The size of the destination as in snprintf(dest, size, ...). */ /* The size of the destination as in snprintf(dest, size, ...). */
unsigned HOST_WIDE_INT dstsize = HOST_WIDE_INT_M1U; unsigned HOST_WIDE_INT dstsize = HOST_WIDE_INT_M1U;
/* The size of the destination determined by __builtin_object_size. */ /* The size of the destination determined by __builtin_object_size. */
unsigned HOST_WIDE_INT objsize = HOST_WIDE_INT_M1U; unsigned HOST_WIDE_INT objsize = HOST_WIDE_INT_M1U;
/* Buffer size argument number (snprintf and vsnprintf). */ /* Zero-based buffer size argument number (snprintf and vsnprintf). */
unsigned HOST_WIDE_INT idx_dstsize = HOST_WIDE_INT_M1U; unsigned idx_dstsize = UINT_MAX;
/* Object size argument number (snprintf_chk and vsnprintf_chk). */ /* Object size argument number (snprintf_chk and vsnprintf_chk). */
unsigned HOST_WIDE_INT idx_objsize = HOST_WIDE_INT_M1U; unsigned idx_objsize = UINT_MAX;
/* Format string argument number (valid for all functions). */ /* Destinaton argument number (valid for sprintf functions only). */
unsigned idx_format; unsigned idx_dstptr = 0;
switch (info.fncode) switch (info.fncode)
{ {
case BUILT_IN_NONE:
// User-defined function with attribute format (printf).
idx_dstptr = -1;
break;
case BUILT_IN_FPRINTF:
// Signature:
// __builtin_fprintf (FILE*, format, ...)
idx_format = 1;
info.argidx = 2;
idx_dstptr = -1;
break;
case BUILT_IN_FPRINTF_CHK:
// Signature:
// __builtin_fprintf_chk (FILE*, ost, format, ...)
idx_format = 2;
info.argidx = 3;
idx_dstptr = -1;
break;
case BUILT_IN_FPRINTF_UNLOCKED:
// Signature:
// __builtin_fprintf_unnlocked (FILE*, format, ...)
idx_format = 1;
info.argidx = 2;
idx_dstptr = -1;
break;
case BUILT_IN_PRINTF:
// Signature:
// __builtin_printf (format, ...)
idx_format = 0;
info.argidx = 1;
idx_dstptr = -1;
break;
case BUILT_IN_PRINTF_CHK:
// Signature:
// __builtin_printf_chk (it, format, ...)
idx_format = 1;
info.argidx = 2;
idx_dstptr = -1;
break;
case BUILT_IN_PRINTF_UNLOCKED:
// Signature:
// __builtin_printf (format, ...)
idx_format = 0;
info.argidx = 1;
idx_dstptr = -1;
break;
case BUILT_IN_SPRINTF: case BUILT_IN_SPRINTF:
// Signature: // Signature:
// __builtin_sprintf (dst, format, ...) // __builtin_sprintf (dst, format, ...)
...@@ -3805,6 +3905,38 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -3805,6 +3905,38 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
info.bounded = true; info.bounded = true;
break; break;
case BUILT_IN_VFPRINTF:
// Signature:
// __builtin_vprintf (FILE*, format, va_list)
idx_format = 1;
info.argidx = -1;
idx_dstptr = -1;
break;
case BUILT_IN_VFPRINTF_CHK:
// Signature:
// __builtin___vfprintf_chk (FILE*, ost, format, va_list)
idx_format = 2;
info.argidx = -1;
idx_dstptr = -1;
break;
case BUILT_IN_VPRINTF:
// Signature:
// __builtin_vprintf (format, va_list)
idx_format = 0;
info.argidx = -1;
idx_dstptr = -1;
break;
case BUILT_IN_VPRINTF_CHK:
// Signature:
// __builtin___vprintf_chk (ost, format, va_list)
idx_format = 1;
info.argidx = -1;
idx_dstptr = -1;
break;
case BUILT_IN_VSNPRINTF: case BUILT_IN_VSNPRINTF:
// Signature: // Signature:
// __builtin_vsprintf (dst, size, format, va) // __builtin_vsprintf (dst, size, format, va)
...@@ -3846,8 +3978,10 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -3846,8 +3978,10 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
/* Set the global warning level for this function. */ /* Set the global warning level for this function. */
warn_level = info.bounded ? warn_format_trunc : warn_format_overflow; warn_level = info.bounded ? warn_format_trunc : warn_format_overflow;
/* The first argument is a pointer to the destination. */ /* For all string functions the first argument is a pointer to
tree dstptr = gimple_call_arg (info.callstmt, 0); the destination. */
tree dstptr = (idx_dstptr < gimple_call_num_args (info.callstmt)
? gimple_call_arg (info.callstmt, 0) : NULL_TREE);
info.format = gimple_call_arg (info.callstmt, idx_format); info.format = gimple_call_arg (info.callstmt, idx_format);
...@@ -3855,7 +3989,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -3855,7 +3989,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
or upper bound of a range. */ or upper bound of a range. */
bool dstsize_cst_p = true; bool dstsize_cst_p = true;
if (idx_dstsize == HOST_WIDE_INT_M1U) if (idx_dstsize == UINT_MAX)
{ {
/* For non-bounded functions like sprintf, determine the size /* For non-bounded functions like sprintf, determine the size
of the destination from the object or pointer passed to it of the destination from the object or pointer passed to it
...@@ -3880,7 +4014,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -3880,7 +4014,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
/* Avoid warning if -Wstringop-overflow is specified since /* Avoid warning if -Wstringop-overflow is specified since
it also warns for the same thing though only for the it also warns for the same thing though only for the
checking built-ins. */ checking built-ins. */
if ((idx_objsize == HOST_WIDE_INT_M1U if ((idx_objsize == UINT_MAX
|| !warn_stringop_overflow)) || !warn_stringop_overflow))
warning_at (gimple_location (info.callstmt), info.warnopt (), warning_at (gimple_location (info.callstmt), info.warnopt (),
"specified bound %wu exceeds maximum object size " "specified bound %wu exceeds maximum object size "
...@@ -3910,7 +4044,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -3910,7 +4044,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
} }
} }
if (idx_objsize != HOST_WIDE_INT_M1U) if (idx_objsize != UINT_MAX)
if (tree size = gimple_call_arg (info.callstmt, idx_objsize)) if (tree size = gimple_call_arg (info.callstmt, idx_objsize))
if (tree_fits_uhwi_p (size)) if (tree_fits_uhwi_p (size))
objsize = tree_to_uhwi (size); objsize = tree_to_uhwi (size);
...@@ -3930,14 +4064,15 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -3930,14 +4064,15 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
/* For calls to non-bounded functions or to those of bounded /* For calls to non-bounded functions or to those of bounded
functions with a non-zero size, warn if the destination functions with a non-zero size, warn if the destination
pointer is null. */ pointer is null. */
if (integer_zerop (dstptr)) if (dstptr && integer_zerop (dstptr))
{ {
/* This is diagnosed with -Wformat only when the null is a constant /* This is diagnosed with -Wformat only when the null is a constant
pointer. The warning here diagnoses instances where the pointer pointer. The warning here diagnoses instances where the pointer
is not constant. */ is not constant. */
location_t loc = gimple_location (info.callstmt); location_t loc = gimple_location (info.callstmt);
warning_at (EXPR_LOC_OR_LOC (dstptr, loc), warning_at (EXPR_LOC_OR_LOC (dstptr, loc),
info.warnopt (), "null destination pointer"); info.warnopt (), "%Gnull destination pointer",
info.callstmt);
return false; return false;
} }
...@@ -3950,7 +4085,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -3950,7 +4085,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
/* Avoid warning if -Wstringop-overflow is specified since /* Avoid warning if -Wstringop-overflow is specified since
it also warns for the same thing though only for the it also warns for the same thing though only for the
checking built-ins. */ checking built-ins. */
&& (idx_objsize == HOST_WIDE_INT_M1U && (idx_objsize == UINT_MAX
|| !warn_stringop_overflow)) || !warn_stringop_overflow))
{ {
warning_at (gimple_location (info.callstmt), info.warnopt (), warning_at (gimple_location (info.callstmt), info.warnopt (),
...@@ -3959,14 +4094,15 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -3959,14 +4094,15 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
} }
} }
if (integer_zerop (info.format)) /* Determine if the format argument may be null and warn if not
and if the argument is null. */
if (integer_zerop (info.format)
&& gimple_call_builtin_p (info.callstmt, BUILT_IN_NORMAL))
{ {
/* This is diagnosed with -Wformat only when the null is a constant
pointer. The warning here diagnoses instances where the pointer
is not constant. */
location_t loc = gimple_location (info.callstmt); location_t loc = gimple_location (info.callstmt);
warning_at (EXPR_LOC_OR_LOC (info.format, loc), warning_at (EXPR_LOC_OR_LOC (info.format, loc),
info.warnopt (), "null format string"); info.warnopt (), "%Gnull format string",
info.callstmt);
return false; return false;
} }
...@@ -3978,6 +4114,14 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -3978,6 +4114,14 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
including the terminating NUL. */ including the terminating NUL. */
format_result res = format_result (); format_result res = format_result ();
/* I/O functions with no destination argument (i.e., all forms of fprintf
and printf) may fail under any conditions. Others (i.e., all forms of
sprintf) may only fail under specific conditions determined for each
directive. Clear POSUNDER4K for the former set of functions and set
it to true for the latter (it can only be cleared later, but it is
never set to true again). */
res.posunder4k = dstptr;
bool success = compute_format_length (info, &res); bool success = compute_format_length (info, &res);
if (res.warned) if (res.warned)
gimple_set_no_warning (info.callstmt, true); gimple_set_no_warning (info.callstmt, true);
......
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> 2018-10-30 Marek Polacek <polacek@redhat.com>
Implement P0892R2, explicit(bool). 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