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:
David Malcolm 2022-10-19 16:49:38 -04:00
parent fb4db9bc1c
commit ebe87edadc
3 changed files with 897 additions and 16 deletions

View file

@ -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 ());

View 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" } */
}

View 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" } */
}