* [committed] analyzer: fix ICE on __builtin_ms_va_copy [PR105765]
@ 2022-10-19 20:52 David Malcolm
0 siblings, 0 replies; only message in thread
From: David Malcolm @ 2022-10-19 20:52 UTC (permalink / raw)
To: gcc-patches; +Cc: David Malcolm
Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r13-3390-gebe87edadc4a3f.
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>
---
gcc/analyzer/varargs.cc | 39 +-
.../gcc.dg/analyzer/stdarg-1-ms_abi.c | 437 ++++++++++++++++++
.../gcc.dg/analyzer/stdarg-1-sysv_abi.c | 437 ++++++++++++++++++
3 files changed, 897 insertions(+), 16 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c
create mode 100644 gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c
diff --git a/gcc/analyzer/varargs.cc b/gcc/analyzer/varargs.cc
index b2e6cd53c06..20c83dbbadc 100644
--- a/gcc/analyzer/varargs.cc
+++ b/gcc/analyzer/varargs.cc
@@ -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 ());
diff --git a/gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c b/gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c
new file mode 100644
index 00000000000..b0143a7d3e3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/stdarg-1-ms_abi.c
@@ -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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c b/gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c
new file mode 100644
index 00000000000..1dc97ea3a44
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/stdarg-1-sysv_abi.c
@@ -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" } */
+}
--
2.26.3
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2022-10-19 20:52 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-19 20:52 [committed] analyzer: fix ICE on __builtin_ms_va_copy [PR105765] David Malcolm
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).