public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [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).