analyzer: fix ICE on __builtin_ms_va_copy [PR105765]
gcc/analyzer/ChangeLog: PR analyzer/105765 * varargs.cc (get_BT_VALIST_ARG): Rename to... (get_va_copy_arg): ...this, and update logic for determining level of indirection of va_copy's argument to use type of argument, rather than looking at va_list_type_node, to correctly handle __builtin_ms_va_copy. (get_stateful_BT_VALIST_ARG): Rename to... (get_stateful_va_copy_arg): ...this. (va_list_state_machine::on_va_copy): Update for renaming. (region_model::impl_call_va_copy): Likewise. gcc/testsuite/ChangeLog: PR analyzer/105765 * gcc.dg/analyzer/stdarg-1-ms_abi.c: New test, based on stdarg-1.c. * gcc.dg/analyzer/stdarg-1-sysv_abi.c: Likewise. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
parent
fb4db9bc1c
commit
ebe87edadc
3 changed files with 897 additions and 16 deletions
|
@ -132,7 +132,7 @@ namespace ana {
|
|||
__builtin_va_start (&ap, [...]);
|
||||
|
||||
except for the 2nd param of __builtin_va_copy, where the type
|
||||
is already target-dependent (see the discussion of BT_VALIST_ARG
|
||||
is already target-dependent (see the discussion of get_va_copy_arg
|
||||
below). */
|
||||
|
||||
/* Get a tree for diagnostics.
|
||||
|
@ -147,26 +147,33 @@ get_va_list_diag_arg (tree va_list_tree)
|
|||
return va_list_tree;
|
||||
}
|
||||
|
||||
/* Get argument ARG_IDX of type BT_VALIST_ARG (for use by va_copy).
|
||||
/* Get argument ARG_IDX of va_copy.
|
||||
|
||||
builtin-types.def has:
|
||||
DEF_PRIMITIVE_TYPE (BT_VALIST_ARG, va_list_arg_type_node)
|
||||
|
||||
and c_common_nodes_and_builtins initializes va_list_arg_type_node
|
||||
based on whether TREE_CODE (va_list_type_node) is of ARRAY_TYPE or
|
||||
not, giving either one or zero levels of indirection. */
|
||||
not, giving either one or zero levels of indirection.
|
||||
|
||||
Alternatively we could be dealing with __builtin_ms_va_copy or
|
||||
__builtin_sysv_va_copy.
|
||||
|
||||
Handle this by looking at the types of the argument in question. */
|
||||
|
||||
static const svalue *
|
||||
get_BT_VALIST_ARG (const region_model *model,
|
||||
region_model_context *ctxt,
|
||||
const gcall *call,
|
||||
unsigned arg_idx)
|
||||
get_va_copy_arg (const region_model *model,
|
||||
region_model_context *ctxt,
|
||||
const gcall *call,
|
||||
unsigned arg_idx)
|
||||
{
|
||||
tree arg = gimple_call_arg (call, arg_idx);
|
||||
const svalue *arg_sval = model->get_rvalue (arg, ctxt);
|
||||
if (const svalue *cast = arg_sval->maybe_undo_cast ())
|
||||
arg_sval = cast;
|
||||
if (TREE_CODE (va_list_type_node) == ARRAY_TYPE)
|
||||
/* Expect a POINTER_TYPE; does it point to an array type? */
|
||||
gcc_assert (TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE);
|
||||
if (TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == ARRAY_TYPE)
|
||||
{
|
||||
/* va_list_arg_type_node is a pointer to a va_list;
|
||||
return *ARG_SVAL. */
|
||||
|
@ -551,19 +558,19 @@ va_list_state_machine::check_for_ended_va_list (sm_context *sm_ctxt,
|
|||
usage_fnname));
|
||||
}
|
||||
|
||||
/* Get the svalue with associated va_list_state_machine state for a
|
||||
BT_VALIST_ARG for ARG_IDX of CALL, if SM_CTXT supports this,
|
||||
/* Get the svalue with associated va_list_state_machine state for
|
||||
ARG_IDX of CALL to va_copy, if SM_CTXT supports this,
|
||||
or NULL otherwise. */
|
||||
|
||||
static const svalue *
|
||||
get_stateful_BT_VALIST_ARG (sm_context *sm_ctxt,
|
||||
const gcall *call,
|
||||
unsigned arg_idx)
|
||||
get_stateful_va_copy_arg (sm_context *sm_ctxt,
|
||||
const gcall *call,
|
||||
unsigned arg_idx)
|
||||
{
|
||||
if (const program_state *new_state = sm_ctxt->get_new_program_state ())
|
||||
{
|
||||
const region_model *new_model = new_state->m_region_model;
|
||||
const svalue *arg = get_BT_VALIST_ARG (new_model, NULL, call, arg_idx);
|
||||
const svalue *arg = get_va_copy_arg (new_model, NULL, call, arg_idx);
|
||||
return arg;
|
||||
}
|
||||
return NULL;
|
||||
|
@ -576,7 +583,7 @@ va_list_state_machine::on_va_copy (sm_context *sm_ctxt,
|
|||
const supernode *node,
|
||||
const gcall *call) const
|
||||
{
|
||||
const svalue *src_arg = get_stateful_BT_VALIST_ARG (sm_ctxt, call, 1);
|
||||
const svalue *src_arg = get_stateful_va_copy_arg (sm_ctxt, call, 1);
|
||||
if (src_arg)
|
||||
check_for_ended_va_list (sm_ctxt, node, call, src_arg, "va_copy");
|
||||
|
||||
|
@ -686,7 +693,7 @@ region_model::impl_call_va_copy (const call_details &cd)
|
|||
{
|
||||
const svalue *out_dst_ptr = cd.get_arg_svalue (0);
|
||||
const svalue *in_va_list
|
||||
= get_BT_VALIST_ARG (this, cd.get_ctxt (), cd.get_call_stmt (), 1);
|
||||
= get_va_copy_arg (this, cd.get_ctxt (), cd.get_call_stmt (), 1);
|
||||
in_va_list = check_for_poison (in_va_list,
|
||||
get_va_list_diag_arg (cd.get_arg_tree (1)),
|
||||
cd.get_ctxt ());
|
||||
|
|
437
gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c
Normal file
437
gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c
Normal file
|
@ -0,0 +1,437 @@
|
|||
/* As per stdarg-1.c, but using the ms_abi versions of the builtins. */
|
||||
|
||||
/* { dg-do compile { target { x86_64-*-* && lp64 } } } */
|
||||
|
||||
#include "analyzer-decls.h"
|
||||
|
||||
/* Unpacking a va_list. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_1 (int placeholder, ...)
|
||||
{
|
||||
const char *s;
|
||||
int i;
|
||||
char c;
|
||||
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
|
||||
s = __builtin_va_arg (ap, char *);
|
||||
__analyzer_eval (s[0] == 'f'); /* { dg-warning "TRUE" } */
|
||||
|
||||
i = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
|
||||
c = (char)__builtin_va_arg (ap, int);
|
||||
__analyzer_eval (c == '@'); /* { dg-warning "TRUE" } */
|
||||
|
||||
__builtin_ms_va_end (ap);
|
||||
}
|
||||
|
||||
void test_1 (void)
|
||||
{
|
||||
__analyzer_called_by_test_1 (42, "foo", 1066, '@');
|
||||
}
|
||||
|
||||
/* Unpacking a va_list passed from an intermediate function. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_2_inner (__builtin_ms_va_list ap)
|
||||
{
|
||||
const char *s;
|
||||
int i;
|
||||
char c;
|
||||
|
||||
s = __builtin_va_arg (ap, char *);
|
||||
__analyzer_eval (s[0] == 'f'); /* { dg-warning "TRUE" } */
|
||||
|
||||
i = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
|
||||
c = (char)__builtin_va_arg (ap, int);
|
||||
__analyzer_eval (c == '@'); /* { dg-warning "TRUE" } */
|
||||
}
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_2_middle (int placeholder, ...)
|
||||
{
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
__analyzer_test_2_inner (ap);
|
||||
__builtin_ms_va_end (ap);
|
||||
}
|
||||
|
||||
void test_2 (void)
|
||||
{
|
||||
__analyzer_test_2_middle (42, "foo", 1066, '@');
|
||||
}
|
||||
|
||||
/* Not enough args. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_not_enough_args (int placeholder, ...)
|
||||
{
|
||||
const char *s;
|
||||
int i;
|
||||
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
|
||||
s = __builtin_va_arg (ap, char *);
|
||||
__analyzer_eval (s[0] == 'f'); /* { dg-warning "TRUE" } */
|
||||
|
||||
i = __builtin_va_arg (ap, int); /* { dg-warning "'ap' has no more arguments \\(1 consumed\\) \\\[CWE-685\\\]" } */
|
||||
|
||||
__builtin_ms_va_end (ap);
|
||||
}
|
||||
|
||||
void test_not_enough_args (void)
|
||||
{
|
||||
__analyzer_called_by_test_not_enough_args (42, "foo");
|
||||
}
|
||||
|
||||
/* Not enough args, with an intermediate function. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_not_enough_args_2_inner (__builtin_ms_va_list ap)
|
||||
{
|
||||
const char *s;
|
||||
int i;
|
||||
|
||||
s = __builtin_va_arg (ap, char *);
|
||||
__analyzer_eval (s[0] == 'f'); /* { dg-warning "TRUE" } */
|
||||
|
||||
i = __builtin_va_arg (ap, int); /* { dg-warning "'ap' has no more arguments \\(1 consumed\\)" } */
|
||||
}
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_not_enough_args_2_middle (int placeholder, ...)
|
||||
{
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
__analyzer_test_not_enough_args_2_inner (ap);
|
||||
__builtin_ms_va_end (ap);
|
||||
}
|
||||
|
||||
void test_not_enough_args_2 (void)
|
||||
{
|
||||
__analyzer_test_not_enough_args_2_middle (42, "foo");
|
||||
}
|
||||
|
||||
/* Excess args (not a problem). */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_excess_args (int placeholder, ...)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
|
||||
s = __builtin_va_arg (ap, char *);
|
||||
__analyzer_eval (s[0] == 'f'); /* { dg-warning "TRUE" } */
|
||||
|
||||
__builtin_ms_va_end (ap);
|
||||
}
|
||||
|
||||
void test_excess_args (void)
|
||||
{
|
||||
__analyzer_called_by_test_excess_args (42, "foo", "bar");
|
||||
}
|
||||
|
||||
/* Missing va_start. */
|
||||
|
||||
void test_missing_va_start (int placeholder, ...)
|
||||
{
|
||||
__builtin_ms_va_list ap; /* { dg-message "region created on stack here" } */
|
||||
int i = __builtin_va_arg (ap, int); /* { dg-warning "use of uninitialized value 'ap'" } */
|
||||
}
|
||||
|
||||
/* Missing va_end. */
|
||||
|
||||
void test_missing_va_end (int placeholder, ...)
|
||||
{
|
||||
int i;
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder); /* { dg-message "\\(1\\) 'va_start' called here" } */
|
||||
i = __builtin_va_arg (ap, int);
|
||||
} /* { dg-warning "missing call to 'va_end'" "warning" } */
|
||||
/* { dg-message "\\(2\\) missing call to 'va_end' to match 'va_start' at \\(1\\)" "final event" { target *-*-* } .-1 } */
|
||||
|
||||
/* Missing va_end due to error-handling. */
|
||||
|
||||
int test_missing_va_end_2 (int placeholder, ...)
|
||||
{
|
||||
int i, j;
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder); /* { dg-message "\\(1\\) 'va_start' called here" } */
|
||||
i = __builtin_va_arg (ap, int);
|
||||
if (i == 42)
|
||||
{
|
||||
__builtin_ms_va_end (ap);
|
||||
return -1;
|
||||
}
|
||||
j = __builtin_va_arg (ap, int);
|
||||
if (j == 1066) /* { dg-message "branch" } */
|
||||
return -1; /* { dg-message "here" } */
|
||||
__builtin_ms_va_end (ap);
|
||||
return 0;
|
||||
} /* { dg-warning "missing call to 'va_end'" "warning" } */
|
||||
|
||||
/* va_arg after va_end. */
|
||||
|
||||
void test_va_arg_after_va_end (int placeholder, ...)
|
||||
{
|
||||
int i;
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
__builtin_ms_va_end (ap); /* { dg-message "'va_end' called here" } */
|
||||
i = __builtin_va_arg (ap, int); /* { dg-warning "'va_arg' after 'va_end'" } */
|
||||
}
|
||||
|
||||
/* Type mismatch: expect int, but passed a char *. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_type_mismatch_1 (int placeholder, ...)
|
||||
{
|
||||
int i;
|
||||
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
|
||||
i = __builtin_va_arg (ap, int); /* { dg-warning "'va_arg' expected 'int' but received '\[^\n\r\]*' for variadic argument 1 of 'ap' \\\[CWE-686\\\]" } */
|
||||
|
||||
__builtin_ms_va_end (ap);
|
||||
}
|
||||
|
||||
void test_type_mismatch_1 (void)
|
||||
{
|
||||
__analyzer_called_by_test_type_mismatch_1 (42, "foo");
|
||||
}
|
||||
|
||||
/* Type mismatch: expect char *, but passed an int. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_type_mismatch_2 (int placeholder, ...)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
|
||||
str = __builtin_va_arg (ap, const char *); /* { dg-warning "'va_arg' expected 'const char \\*' but received 'int' for variadic argument 1" } */
|
||||
|
||||
__builtin_ms_va_end (ap);
|
||||
}
|
||||
|
||||
void test_type_mismatch_2 (void)
|
||||
{
|
||||
__analyzer_called_by_test_type_mismatch_2 (42, 1066);
|
||||
}
|
||||
|
||||
/* As above, but with an intermediate function. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_type_mismatch_3_inner (__builtin_ms_va_list ap)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
str = __builtin_va_arg (ap, const char *); /* { dg-warning "'va_arg' expected 'const char \\*' but received 'int' for variadic argument 1 of 'ap'" } */
|
||||
}
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_type_mismatch_3_middle (int placeholder, ...)
|
||||
{
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
|
||||
__analyzer_test_type_mismatch_3_inner (ap);
|
||||
|
||||
__builtin_ms_va_end (ap);
|
||||
}
|
||||
|
||||
void test_type_mismatch_3 (void)
|
||||
{
|
||||
__analyzer_test_type_mismatch_3_middle (42, 1066);
|
||||
}
|
||||
|
||||
/* Multiple traversals of the args. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_multiple_traversals (int placeholder, ...)
|
||||
{
|
||||
__builtin_ms_va_list ap;
|
||||
|
||||
/* First traversal. */
|
||||
{
|
||||
int i, j;
|
||||
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
|
||||
i = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
|
||||
j = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
|
||||
__builtin_ms_va_end (ap);
|
||||
}
|
||||
|
||||
/* Second traversal. */
|
||||
{
|
||||
int i, j;
|
||||
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
|
||||
i = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
|
||||
j = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
|
||||
__builtin_ms_va_end (ap);
|
||||
}
|
||||
}
|
||||
|
||||
void test_multiple_traversals (void)
|
||||
{
|
||||
__analyzer_called_by_test_multiple_traversals (0, 1066, 42);
|
||||
}
|
||||
|
||||
/* Multiple traversals, using va_copy. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_multiple_traversals_2 (int placeholder, ...)
|
||||
{
|
||||
int i, j;
|
||||
__builtin_ms_va_list args1;
|
||||
__builtin_ms_va_list args2;
|
||||
|
||||
__builtin_ms_va_start (args1, placeholder);
|
||||
__builtin_ms_va_copy (args2, args1);
|
||||
|
||||
/* First traversal. */
|
||||
i = __builtin_va_arg (args1, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
j = __builtin_va_arg (args1, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
__builtin_ms_va_end (args1);
|
||||
|
||||
/* Traversal of copy. */
|
||||
i = __builtin_va_arg (args2, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
j = __builtin_va_arg (args2, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
__builtin_ms_va_end (args2);
|
||||
}
|
||||
|
||||
void test_multiple_traversals_2 (void)
|
||||
{
|
||||
__analyzer_called_by_test_multiple_traversals_2 (0, 1066, 42);
|
||||
}
|
||||
|
||||
/* Multiple traversals, using va_copy after a va_arg. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_multiple_traversals_3 (int placeholder, ...)
|
||||
{
|
||||
int i, j;
|
||||
__builtin_ms_va_list args1;
|
||||
__builtin_ms_va_list args2;
|
||||
|
||||
__builtin_ms_va_start (args1, placeholder);
|
||||
|
||||
/* First traversal. */
|
||||
i = __builtin_va_arg (args1, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
|
||||
/* va_copy after the first va_arg. */
|
||||
__builtin_ms_va_copy (args2, args1);
|
||||
|
||||
j = __builtin_va_arg (args1, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
__builtin_ms_va_end (args1);
|
||||
|
||||
/* Traversal of copy. */
|
||||
j = __builtin_va_arg (args2, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
__builtin_ms_va_end (args2);
|
||||
}
|
||||
|
||||
void test_multiple_traversals_3 (void)
|
||||
{
|
||||
__analyzer_called_by_test_multiple_traversals_3 (0, 1066, 42);
|
||||
}
|
||||
|
||||
/* va_copy after va_end. */
|
||||
|
||||
void test_va_copy_after_va_end (int placeholder, ...)
|
||||
{
|
||||
__builtin_ms_va_list ap1, ap2;
|
||||
__builtin_ms_va_start (ap1, placeholder);
|
||||
__builtin_ms_va_end (ap1); /* { dg-message "'va_end' called here" } */
|
||||
__builtin_ms_va_copy (ap2, ap1); /* { dg-warning "'va_copy' after 'va_end'" } */
|
||||
__builtin_ms_va_end (ap2);
|
||||
}
|
||||
|
||||
/* leak of va_copy. */
|
||||
|
||||
void test_leak_of_va_copy (int placeholder, ...)
|
||||
{
|
||||
__builtin_ms_va_list ap1, ap2;
|
||||
__builtin_ms_va_start (ap1, placeholder);
|
||||
__builtin_ms_va_copy (ap2, ap1); /* { dg-message "'va_copy' called here" } */
|
||||
__builtin_ms_va_end (ap1);
|
||||
} /* { dg-warning "missing call to 'va_end'" "warning" } */
|
||||
/* { dg-message "missing call to 'va_end' to match 'va_copy' at \\(1\\)" "final event" { target *-*-* } .-1 } */
|
||||
|
||||
/* double va_end. */
|
||||
|
||||
void test_double_va_end (int placeholder, ...)
|
||||
{
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder);
|
||||
__builtin_ms_va_end (ap); /* { dg-message "'va_end' called here" } */
|
||||
__builtin_ms_va_end (ap); /* { dg-warning "'va_end' after 'va_end'" } */
|
||||
}
|
||||
|
||||
/* double va_start. */
|
||||
|
||||
void test_double_va_start (int placeholder, ...)
|
||||
{
|
||||
int i;
|
||||
__builtin_ms_va_list ap;
|
||||
__builtin_ms_va_start (ap, placeholder); /* { dg-message "'va_start' called here" } */
|
||||
__builtin_ms_va_start (ap, placeholder); /* { dg-warning "missing call to 'va_end'" "warning" } */
|
||||
/* { dg-message "missing call to 'va_end' to match 'va_start' at \\(1\\)" "final event" { target *-*-* } .-1 } */
|
||||
__builtin_ms_va_end (ap);
|
||||
}
|
||||
|
||||
/* va_copy before va_start. */
|
||||
|
||||
void test_va_copy_before_va_start (int placeholder, ...)
|
||||
{
|
||||
__builtin_ms_va_list ap1; /* { dg-message "region created on stack here" } */
|
||||
__builtin_ms_va_list ap2;
|
||||
__builtin_ms_va_copy (ap2, ap1); /* { dg-warning "use of uninitialized value 'ap1'" } */
|
||||
__builtin_ms_va_end (ap2);
|
||||
}
|
||||
|
||||
/* Verify that we complain about uses of a va_list after the function
|
||||
in which va_start was called has returned. */
|
||||
|
||||
__builtin_ms_va_list global_ap;
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_va_arg_after_return (int placeholder, ...)
|
||||
{
|
||||
__builtin_ms_va_start (global_ap, placeholder);
|
||||
__builtin_ms_va_end (global_ap);
|
||||
}
|
||||
|
||||
void test_va_arg_after_return (void)
|
||||
{
|
||||
int i;
|
||||
__analyzer_called_by_test_va_arg_after_return (42, 1066);
|
||||
i = __builtin_va_arg (global_ap, int); /* { dg-warning "dereferencing pointer 'global_ap' to within stale stack frame" } */
|
||||
}
|
437
gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c
Normal file
437
gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c
Normal file
|
@ -0,0 +1,437 @@
|
|||
/* As per stdarg-1.c, but using the sysv_abi versions of the builtins. */
|
||||
|
||||
/* { dg-do compile { target { x86_64-*-* && lp64 } } } */
|
||||
|
||||
#include "analyzer-decls.h"
|
||||
|
||||
/* Unpacking a va_list. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_1 (int placeholder, ...)
|
||||
{
|
||||
const char *s;
|
||||
int i;
|
||||
char c;
|
||||
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
|
||||
s = __builtin_va_arg (ap, char *);
|
||||
__analyzer_eval (s[0] == 'f'); /* { dg-warning "TRUE" } */
|
||||
|
||||
i = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
|
||||
c = (char)__builtin_va_arg (ap, int);
|
||||
__analyzer_eval (c == '@'); /* { dg-warning "TRUE" } */
|
||||
|
||||
__builtin_sysv_va_end (ap);
|
||||
}
|
||||
|
||||
void test_1 (void)
|
||||
{
|
||||
__analyzer_called_by_test_1 (42, "foo", 1066, '@');
|
||||
}
|
||||
|
||||
/* Unpacking a va_list passed from an intermediate function. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_2_inner (__builtin_sysv_va_list ap)
|
||||
{
|
||||
const char *s;
|
||||
int i;
|
||||
char c;
|
||||
|
||||
s = __builtin_va_arg (ap, char *);
|
||||
__analyzer_eval (s[0] == 'f'); /* { dg-warning "TRUE" } */
|
||||
|
||||
i = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
|
||||
c = (char)__builtin_va_arg (ap, int);
|
||||
__analyzer_eval (c == '@'); /* { dg-warning "TRUE" } */
|
||||
}
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_2_middle (int placeholder, ...)
|
||||
{
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
__analyzer_test_2_inner (ap);
|
||||
__builtin_sysv_va_end (ap);
|
||||
}
|
||||
|
||||
void test_2 (void)
|
||||
{
|
||||
__analyzer_test_2_middle (42, "foo", 1066, '@');
|
||||
}
|
||||
|
||||
/* Not enough args. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_not_enough_args (int placeholder, ...)
|
||||
{
|
||||
const char *s;
|
||||
int i;
|
||||
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
|
||||
s = __builtin_va_arg (ap, char *);
|
||||
__analyzer_eval (s[0] == 'f'); /* { dg-warning "TRUE" } */
|
||||
|
||||
i = __builtin_va_arg (ap, int); /* { dg-warning "'ap' has no more arguments \\(1 consumed\\) \\\[CWE-685\\\]" } */
|
||||
|
||||
__builtin_sysv_va_end (ap);
|
||||
}
|
||||
|
||||
void test_not_enough_args (void)
|
||||
{
|
||||
__analyzer_called_by_test_not_enough_args (42, "foo");
|
||||
}
|
||||
|
||||
/* Not enough args, with an intermediate function. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_not_enough_args_2_inner (__builtin_sysv_va_list ap)
|
||||
{
|
||||
const char *s;
|
||||
int i;
|
||||
|
||||
s = __builtin_va_arg (ap, char *);
|
||||
__analyzer_eval (s[0] == 'f'); /* { dg-warning "TRUE" } */
|
||||
|
||||
i = __builtin_va_arg (ap, int); /* { dg-warning "'ap' has no more arguments \\(1 consumed\\)" } */
|
||||
}
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_not_enough_args_2_middle (int placeholder, ...)
|
||||
{
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
__analyzer_test_not_enough_args_2_inner (ap);
|
||||
__builtin_sysv_va_end (ap);
|
||||
}
|
||||
|
||||
void test_not_enough_args_2 (void)
|
||||
{
|
||||
__analyzer_test_not_enough_args_2_middle (42, "foo");
|
||||
}
|
||||
|
||||
/* Excess args (not a problem). */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_excess_args (int placeholder, ...)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
|
||||
s = __builtin_va_arg (ap, char *);
|
||||
__analyzer_eval (s[0] == 'f'); /* { dg-warning "TRUE" } */
|
||||
|
||||
__builtin_sysv_va_end (ap);
|
||||
}
|
||||
|
||||
void test_excess_args (void)
|
||||
{
|
||||
__analyzer_called_by_test_excess_args (42, "foo", "bar");
|
||||
}
|
||||
|
||||
/* Missing va_start. */
|
||||
|
||||
void test_missing_va_start (int placeholder, ...)
|
||||
{
|
||||
__builtin_sysv_va_list ap; /* { dg-message "region created on stack here" } */
|
||||
int i = __builtin_va_arg (ap, int); /* { dg-warning "use of uninitialized value 'ap'" } */
|
||||
}
|
||||
|
||||
/* Missing va_end. */
|
||||
|
||||
void test_missing_va_end (int placeholder, ...)
|
||||
{
|
||||
int i;
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder); /* { dg-message "\\(1\\) 'va_start' called here" } */
|
||||
i = __builtin_va_arg (ap, int);
|
||||
} /* { dg-warning "missing call to 'va_end'" "warning" } */
|
||||
/* { dg-message "\\(2\\) missing call to 'va_end' to match 'va_start' at \\(1\\)" "final event" { target *-*-* } .-1 } */
|
||||
|
||||
/* Missing va_end due to error-handling. */
|
||||
|
||||
int test_missing_va_end_2 (int placeholder, ...)
|
||||
{
|
||||
int i, j;
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder); /* { dg-message "\\(1\\) 'va_start' called here" } */
|
||||
i = __builtin_va_arg (ap, int);
|
||||
if (i == 42)
|
||||
{
|
||||
__builtin_sysv_va_end (ap);
|
||||
return -1;
|
||||
}
|
||||
j = __builtin_va_arg (ap, int);
|
||||
if (j == 1066) /* { dg-message "branch" } */
|
||||
return -1; /* { dg-message "here" } */
|
||||
__builtin_sysv_va_end (ap);
|
||||
return 0;
|
||||
} /* { dg-warning "missing call to 'va_end'" "warning" } */
|
||||
|
||||
/* va_arg after va_end. */
|
||||
|
||||
void test_va_arg_after_va_end (int placeholder, ...)
|
||||
{
|
||||
int i;
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
__builtin_sysv_va_end (ap); /* { dg-message "'va_end' called here" } */
|
||||
i = __builtin_va_arg (ap, int); /* { dg-warning "'va_arg' after 'va_end'" } */
|
||||
}
|
||||
|
||||
/* Type mismatch: expect int, but passed a char *. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_type_mismatch_1 (int placeholder, ...)
|
||||
{
|
||||
int i;
|
||||
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
|
||||
i = __builtin_va_arg (ap, int); /* { dg-warning "'va_arg' expected 'int' but received '\[^\n\r\]*' for variadic argument 1 of 'ap' \\\[CWE-686\\\]" } */
|
||||
|
||||
__builtin_sysv_va_end (ap);
|
||||
}
|
||||
|
||||
void test_type_mismatch_1 (void)
|
||||
{
|
||||
__analyzer_called_by_test_type_mismatch_1 (42, "foo");
|
||||
}
|
||||
|
||||
/* Type mismatch: expect char *, but passed an int. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_type_mismatch_2 (int placeholder, ...)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
|
||||
str = __builtin_va_arg (ap, const char *); /* { dg-warning "'va_arg' expected 'const char \\*' but received 'int' for variadic argument 1" } */
|
||||
|
||||
__builtin_sysv_va_end (ap);
|
||||
}
|
||||
|
||||
void test_type_mismatch_2 (void)
|
||||
{
|
||||
__analyzer_called_by_test_type_mismatch_2 (42, 1066);
|
||||
}
|
||||
|
||||
/* As above, but with an intermediate function. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_type_mismatch_3_inner (__builtin_sysv_va_list ap)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
str = __builtin_va_arg (ap, const char *); /* { dg-warning "'va_arg' expected 'const char \\*' but received 'int' for variadic argument 1 of 'ap'" } */
|
||||
}
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_test_type_mismatch_3_middle (int placeholder, ...)
|
||||
{
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
|
||||
__analyzer_test_type_mismatch_3_inner (ap);
|
||||
|
||||
__builtin_sysv_va_end (ap);
|
||||
}
|
||||
|
||||
void test_type_mismatch_3 (void)
|
||||
{
|
||||
__analyzer_test_type_mismatch_3_middle (42, 1066);
|
||||
}
|
||||
|
||||
/* Multiple traversals of the args. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_multiple_traversals (int placeholder, ...)
|
||||
{
|
||||
__builtin_sysv_va_list ap;
|
||||
|
||||
/* First traversal. */
|
||||
{
|
||||
int i, j;
|
||||
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
|
||||
i = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
|
||||
j = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
|
||||
__builtin_sysv_va_end (ap);
|
||||
}
|
||||
|
||||
/* Second traversal. */
|
||||
{
|
||||
int i, j;
|
||||
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
|
||||
i = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
|
||||
j = __builtin_va_arg (ap, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
|
||||
__builtin_sysv_va_end (ap);
|
||||
}
|
||||
}
|
||||
|
||||
void test_multiple_traversals (void)
|
||||
{
|
||||
__analyzer_called_by_test_multiple_traversals (0, 1066, 42);
|
||||
}
|
||||
|
||||
/* Multiple traversals, using va_copy. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_multiple_traversals_2 (int placeholder, ...)
|
||||
{
|
||||
int i, j;
|
||||
__builtin_sysv_va_list args1;
|
||||
__builtin_sysv_va_list args2;
|
||||
|
||||
__builtin_sysv_va_start (args1, placeholder);
|
||||
__builtin_sysv_va_copy (args2, args1);
|
||||
|
||||
/* First traversal. */
|
||||
i = __builtin_va_arg (args1, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
j = __builtin_va_arg (args1, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
__builtin_sysv_va_end (args1);
|
||||
|
||||
/* Traversal of copy. */
|
||||
i = __builtin_va_arg (args2, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
j = __builtin_va_arg (args2, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
__builtin_sysv_va_end (args2);
|
||||
}
|
||||
|
||||
void test_multiple_traversals_2 (void)
|
||||
{
|
||||
__analyzer_called_by_test_multiple_traversals_2 (0, 1066, 42);
|
||||
}
|
||||
|
||||
/* Multiple traversals, using va_copy after a va_arg. */
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_multiple_traversals_3 (int placeholder, ...)
|
||||
{
|
||||
int i, j;
|
||||
__builtin_sysv_va_list args1;
|
||||
__builtin_sysv_va_list args2;
|
||||
|
||||
__builtin_sysv_va_start (args1, placeholder);
|
||||
|
||||
/* First traversal. */
|
||||
i = __builtin_va_arg (args1, int);
|
||||
__analyzer_eval (i == 1066); /* { dg-warning "TRUE" } */
|
||||
|
||||
/* va_copy after the first va_arg. */
|
||||
__builtin_sysv_va_copy (args2, args1);
|
||||
|
||||
j = __builtin_va_arg (args1, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
__builtin_sysv_va_end (args1);
|
||||
|
||||
/* Traversal of copy. */
|
||||
j = __builtin_va_arg (args2, int);
|
||||
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
|
||||
__builtin_sysv_va_end (args2);
|
||||
}
|
||||
|
||||
void test_multiple_traversals_3 (void)
|
||||
{
|
||||
__analyzer_called_by_test_multiple_traversals_3 (0, 1066, 42);
|
||||
}
|
||||
|
||||
/* va_copy after va_end. */
|
||||
|
||||
void test_va_copy_after_va_end (int placeholder, ...)
|
||||
{
|
||||
__builtin_sysv_va_list ap1, ap2;
|
||||
__builtin_sysv_va_start (ap1, placeholder);
|
||||
__builtin_sysv_va_end (ap1); /* { dg-message "'va_end' called here" } */
|
||||
__builtin_sysv_va_copy (ap2, ap1); /* { dg-warning "'va_copy' after 'va_end'" } */
|
||||
__builtin_sysv_va_end (ap2);
|
||||
}
|
||||
|
||||
/* leak of va_copy. */
|
||||
|
||||
void test_leak_of_va_copy (int placeholder, ...)
|
||||
{
|
||||
__builtin_sysv_va_list ap1, ap2;
|
||||
__builtin_sysv_va_start (ap1, placeholder);
|
||||
__builtin_sysv_va_copy (ap2, ap1); /* { dg-message "'va_copy' called here" } */
|
||||
__builtin_sysv_va_end (ap1);
|
||||
} /* { dg-warning "missing call to 'va_end'" "warning" } */
|
||||
/* { dg-message "missing call to 'va_end' to match 'va_copy' at \\(1\\)" "final event" { target *-*-* } .-1 } */
|
||||
|
||||
/* double va_end. */
|
||||
|
||||
void test_double_va_end (int placeholder, ...)
|
||||
{
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder);
|
||||
__builtin_sysv_va_end (ap); /* { dg-message "'va_end' called here" } */
|
||||
__builtin_sysv_va_end (ap); /* { dg-warning "'va_end' after 'va_end'" } */
|
||||
}
|
||||
|
||||
/* double va_start. */
|
||||
|
||||
void test_double_va_start (int placeholder, ...)
|
||||
{
|
||||
int i;
|
||||
__builtin_sysv_va_list ap;
|
||||
__builtin_sysv_va_start (ap, placeholder); /* { dg-message "'va_start' called here" } */
|
||||
__builtin_sysv_va_start (ap, placeholder); /* { dg-warning "missing call to 'va_end'" "warning" } */
|
||||
/* { dg-message "missing call to 'va_end' to match 'va_start' at \\(1\\)" "final event" { target *-*-* } .-1 } */
|
||||
__builtin_sysv_va_end (ap);
|
||||
}
|
||||
|
||||
/* va_copy before va_start. */
|
||||
|
||||
void test_va_copy_before_va_start (int placeholder, ...)
|
||||
{
|
||||
__builtin_sysv_va_list ap1; /* { dg-message "region created on stack here" } */
|
||||
__builtin_sysv_va_list ap2;
|
||||
__builtin_sysv_va_copy (ap2, ap1); /* { dg-warning "use of uninitialized value 'ap1'" } */
|
||||
__builtin_sysv_va_end (ap2);
|
||||
}
|
||||
|
||||
/* Verify that we complain about uses of a va_list after the function
|
||||
in which va_start was called has returned. */
|
||||
|
||||
__builtin_sysv_va_list global_ap;
|
||||
|
||||
static void __attribute__((noinline))
|
||||
__analyzer_called_by_test_va_arg_after_return (int placeholder, ...)
|
||||
{
|
||||
__builtin_sysv_va_start (global_ap, placeholder);
|
||||
__builtin_sysv_va_end (global_ap);
|
||||
}
|
||||
|
||||
void test_va_arg_after_return (void)
|
||||
{
|
||||
int i;
|
||||
__analyzer_called_by_test_va_arg_after_return (42, 1066);
|
||||
i = __builtin_va_arg (global_ap, int); /* { dg-warning "dereferencing pointer 'global_ap' to within stale stack frame" } */
|
||||
}
|
Loading…
Add table
Reference in a new issue