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
This commit is contained in:
parent
448af20a27
commit
91e3ec29af
12 changed files with 1009 additions and 25 deletions
|
@ -1,3 +1,17 @@
|
|||
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.
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
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))
|
||||
|
|
|
@ -68,6 +68,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "intl.h"
|
||||
#include "langhooks.h"
|
||||
|
||||
#include "attribs.h"
|
||||
#include "builtins.h"
|
||||
#include "stor-layout.h"
|
||||
|
||||
|
@ -2796,8 +2797,9 @@ format_directive (const sprintf_dom_walker::call_info &info,
|
|||
if (fmtres.nullp)
|
||||
{
|
||||
fmtwarn (dirloc, argloc, NULL, info.warnopt (),
|
||||
"%<%.*s%> directive argument is null",
|
||||
dirlen, target_to_host (hostdir, sizeof hostdir, dir.beg));
|
||||
"%G%<%.*s%> directive argument is null",
|
||||
info.callstmt, dirlen,
|
||||
target_to_host (hostdir, sizeof hostdir, dir.beg));
|
||||
|
||||
/* Don't bother processing the rest of the format string. */
|
||||
res->warned = true;
|
||||
|
@ -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
|
||||
or producing more than 4K bytes) until determined otherwise. */
|
||||
res->knownrange = true;
|
||||
res->posunder4k = true;
|
||||
res->floating = false;
|
||||
res->warned = false;
|
||||
|
||||
|
@ -3518,6 +3519,10 @@ sprintf_dom_walker::compute_format_length (call_info &info,
|
|||
static unsigned HOST_WIDE_INT
|
||||
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. */
|
||||
init_object_sizes ();
|
||||
|
||||
|
@ -3738,6 +3743,37 @@ try_simplify_call (gimple_stmt_iterator *gsi,
|
|||
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
|
||||
functions and if so, handle it. Return true if the call is removed
|
||||
and gsi_next should not be performed in the caller. */
|
||||
|
@ -3748,29 +3784,93 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
|
|||
call_info info = call_info ();
|
||||
|
||||
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;
|
||||
|
||||
info.func = gimple_call_fndecl (info.callstmt);
|
||||
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, ...). */
|
||||
unsigned HOST_WIDE_INT dstsize = HOST_WIDE_INT_M1U;
|
||||
|
||||
/* The size of the destination determined by __builtin_object_size. */
|
||||
unsigned HOST_WIDE_INT objsize = HOST_WIDE_INT_M1U;
|
||||
|
||||
/* Buffer size argument number (snprintf and vsnprintf). */
|
||||
unsigned HOST_WIDE_INT idx_dstsize = HOST_WIDE_INT_M1U;
|
||||
/* Zero-based buffer size argument number (snprintf and vsnprintf). */
|
||||
unsigned idx_dstsize = UINT_MAX;
|
||||
|
||||
/* 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). */
|
||||
unsigned idx_format;
|
||||
/* Destinaton argument number (valid for sprintf functions only). */
|
||||
unsigned idx_dstptr = 0;
|
||||
|
||||
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:
|
||||
// Signature:
|
||||
// __builtin_sprintf (dst, format, ...)
|
||||
|
@ -3805,6 +3905,38 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
|
|||
info.bounded = true;
|
||||
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:
|
||||
// Signature:
|
||||
// __builtin_vsprintf (dst, size, format, va)
|
||||
|
@ -3846,8 +3978,10 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
|
|||
/* Set the global warning level for this function. */
|
||||
warn_level = info.bounded ? warn_format_trunc : warn_format_overflow;
|
||||
|
||||
/* The first argument is a pointer to the destination. */
|
||||
tree dstptr = gimple_call_arg (info.callstmt, 0);
|
||||
/* For all string functions the first argument is a pointer to
|
||||
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);
|
||||
|
||||
|
@ -3855,7 +3989,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
|
|||
or upper bound of a range. */
|
||||
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
|
||||
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)
|
|||
/* Avoid warning if -Wstringop-overflow is specified since
|
||||
it also warns for the same thing though only for the
|
||||
checking built-ins. */
|
||||
if ((idx_objsize == HOST_WIDE_INT_M1U
|
||||
if ((idx_objsize == UINT_MAX
|
||||
|| !warn_stringop_overflow))
|
||||
warning_at (gimple_location (info.callstmt), info.warnopt (),
|
||||
"specified bound %wu exceeds maximum object size "
|
||||
|
@ -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_fits_uhwi_p (size))
|
||||
objsize = tree_to_uhwi (size);
|
||||
|
@ -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
|
||||
functions with a non-zero size, warn if the destination
|
||||
pointer is null. */
|
||||
if (integer_zerop (dstptr))
|
||||
if (dstptr && integer_zerop (dstptr))
|
||||
{
|
||||
/* 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);
|
||||
warning_at (EXPR_LOC_OR_LOC (dstptr, loc),
|
||||
info.warnopt (), "null destination pointer");
|
||||
info.warnopt (), "%Gnull destination pointer",
|
||||
info.callstmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -3950,7 +4085,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
|
|||
/* Avoid warning if -Wstringop-overflow is specified since
|
||||
it also warns for the same thing though only for the
|
||||
checking built-ins. */
|
||||
&& (idx_objsize == HOST_WIDE_INT_M1U
|
||||
&& (idx_objsize == UINT_MAX
|
||||
|| !warn_stringop_overflow))
|
||||
{
|
||||
warning_at (gimple_location (info.callstmt), info.warnopt (),
|
||||
|
@ -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);
|
||||
warning_at (EXPR_LOC_OR_LOC (info.format, loc),
|
||||
info.warnopt (), "null format string");
|
||||
info.warnopt (), "%Gnull format string",
|
||||
info.callstmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -3978,6 +4114,14 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
|
|||
including the terminating NUL. */
|
||||
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);
|
||||
if (res.warned)
|
||||
gimple_set_no_warning (info.callstmt, true);
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
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).
|
||||
|
|
53
gcc/testsuite/gcc.c-torture/execute/fprintf-2.c
Normal file
53
gcc/testsuite/gcc.c-torture/execute/fprintf-2.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
/* 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;
|
||||
}
|
60
gcc/testsuite/gcc.c-torture/execute/printf-2.c
Normal file
60
gcc/testsuite/gcc.c-torture/execute/printf-2.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
/* 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;
|
||||
}
|
64
gcc/testsuite/gcc.c-torture/execute/user-printf.c
Normal file
64
gcc/testsuite/gcc.c-torture/execute/user-printf.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/* 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;
|
||||
}
|
132
gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-warn-1.c
Normal file
132
gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-warn-1.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/* 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." } */
|
||||
}
|
213
gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-2.c
Normal file
213
gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-2.c
Normal file
|
@ -0,0 +1,213 @@
|
|||
/* 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"} } */
|
||||
}
|
129
gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-warn-1.c
Normal file
129
gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-warn-1.c
Normal file
|
@ -0,0 +1,129 @@
|
|||
/* 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." } */
|
||||
}
|
155
gcc/testsuite/gcc.dg/tree-ssa/user-printf-warn-1.c
Normal file
155
gcc/testsuite/gcc.dg/tree-ssa/user-printf-warn-1.c
Normal file
|
@ -0,0 +1,155 @@
|
|||
/* 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." } */
|
||||
}
|
Loading…
Add table
Reference in a new issue