diff mbox series

[pushed] analyzer: fix ICE on certain longjmp calls [PR109094]

Message ID 20230318165101.3685516-1-dmalcolm@redhat.com
State New
Headers show
Series [pushed] analyzer: fix ICE on certain longjmp calls [PR109094] | expand

Commit Message

David Malcolm March 18, 2023, 4:51 p.m. UTC
PR analyzer/109094 reports an ICE in the analyzer seen on qemu's
target/i386/tcg/translate.c

The issue turned out to be that when handling a longjmp, the code
to pop the frames was generating an svalue for the result_decl of any
popped frame that had a non-void return type (and discarding it) leading
to "uninit" poisoned_svalue_diagnostic instances being saved since the
result_decl is only set by the greturn stmt.  Later, when checking the
feasibility of the path to these diagnostics, m_check_expr was evaluated
in the context of the frame of the longjmp, leading to an attempt to
evaluate the result_decl of each intervening frames whilst in the
context of the topmost frame, leading to an assertion failure in
frame_region::get_region_for_local here:

919		case RESULT_DECL:
920		  gcc_assert (DECL_CONTEXT (expr) == m_fun->decl);
921		  break;

This patch updates the analyzer's longjmp implementation so that it
doesn't attempt to generate svalues for the result_decls when popping
frames, fixing the assertion failure (and presumably fixing "uninit"
false positives in a release build).

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Analyzer integration tests shows no regressions.
Pushed to trunk as r13-6749-g430d7d88c1a123.

gcc/analyzer/ChangeLog:
	PR analyzer/109094
	* region-model.cc (region_model::on_longjmp): Pass false for
	new "eval_return_svalue" param of pop_frame.
	(region_model::pop_frame): Add new "eval_return_svalue" param and
	use it to suppress the call to get_rvalue on the result when
	needed by on_longjmp.
	* region-model.h (region_model::pop_frame): Add new
	"eval_return_svalue" param.

gcc/testsuite/ChangeLog:
	PR analyzer/109094
	* gcc.dg/analyzer/setjmp-pr109094.c: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/analyzer/region-model.cc                  | 15 ++++++--
 gcc/analyzer/region-model.h                   |  3 +-
 .../gcc.dg/analyzer/setjmp-pr109094.c         | 38 +++++++++++++++++++
 3 files changed, 52 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-pr109094.c
diff mbox series

Patch

diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 56beaa82f95..fb81d43f91b 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1952,7 +1952,7 @@  region_model::on_longjmp (const gcall *longjmp_call, const gcall *setjmp_call,
      setjmp was called.  */
   gcc_assert (get_stack_depth () >= setjmp_stack_depth);
   while (get_stack_depth () > setjmp_stack_depth)
-    pop_frame (NULL, NULL, ctxt);
+    pop_frame (NULL, NULL, ctxt, false);
 
   gcc_assert (get_stack_depth () == setjmp_stack_depth);
 
@@ -4679,6 +4679,10 @@  region_model::get_current_function () const
    If OUT_RESULT is non-null, copy any return value from the frame
    into *OUT_RESULT.
 
+   If EVAL_RETURN_SVALUE is false, then don't evaluate the return value.
+   This is for use when unwinding frames e.g. due to longjmp, to suppress
+   erroneously reporting uninitialized return values.
+
    Purge the frame region and all its descendent regions.
    Convert any pointers that point into such regions into
    POISON_KIND_POPPED_STACK svalues.  */
@@ -4686,7 +4690,8 @@  region_model::get_current_function () const
 void
 region_model::pop_frame (tree result_lvalue,
 			 const svalue **out_result,
-			 region_model_context *ctxt)
+			 region_model_context *ctxt,
+			 bool eval_return_svalue)
 {
   gcc_assert (m_current_frame);
 
@@ -4700,7 +4705,9 @@  region_model::pop_frame (tree result_lvalue,
   tree fndecl = m_current_frame->get_function ()->decl;
   tree result = DECL_RESULT (fndecl);
   const svalue *retval = NULL;
-  if (result && TREE_TYPE (result) != void_type_node)
+  if (result
+      && TREE_TYPE (result) != void_type_node
+      && eval_return_svalue)
     {
       retval = get_rvalue (result, ctxt);
       if (out_result)
@@ -4712,6 +4719,8 @@  region_model::pop_frame (tree result_lvalue,
 
   if (result_lvalue && retval)
     {
+      gcc_assert (eval_return_svalue);
+
       /* Compute result_dst_reg using RESULT_LVALUE *after* popping
 	 the frame, but before poisoning pointers into the old frame.  */
       const region *result_dst_reg = get_lvalue (result_lvalue, ctxt);
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 197b5358678..fe3db0b0c98 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -341,7 +341,8 @@  class region_model
   function * get_current_function () const;
   void pop_frame (tree result_lvalue,
 		  const svalue **out_result,
-		  region_model_context *ctxt);
+		  region_model_context *ctxt,
+		  bool eval_return_svalue = true);
   int get_stack_depth () const;
   const frame_region *get_frame_at_index (int index) const;
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-pr109094.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-pr109094.c
new file mode 100644
index 00000000000..10591ce60a7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-pr109094.c
@@ -0,0 +1,38 @@ 
+/* Reduced from an ICE seen in qemu's target/i386/tcg/translate.c  */
+
+typedef long int __jmp_buf[8];
+struct __jmp_buf_tag {
+  __jmp_buf __jmpbuf;
+};
+typedef struct __jmp_buf_tag sigjmp_buf[1];
+
+extern int __sigsetjmp(sigjmp_buf env, int savesigs);
+extern void siglongjmp(sigjmp_buf env, int val);
+
+typedef struct DisasContextBase {
+  int num_insns;
+} DisasContextBase;
+
+typedef struct DisasContext {
+  DisasContextBase base;
+  sigjmp_buf jmpbuf;
+} DisasContext;
+
+extern int translator_ldub(DisasContextBase *base, int);
+
+int advance_pc(DisasContext *s, int num_bytes) {
+  if (s->base.num_insns > 1) {
+    siglongjmp(s->jmpbuf, 2);
+  }
+  return 0;
+}
+
+static inline int x86_ldub_code(DisasContext *s) {
+  return translator_ldub(&s->base, advance_pc(s, 1));
+}
+
+static void disas_insn(DisasContext *s) {
+  int b;
+  __sigsetjmp(s->jmpbuf, 0);
+  b = x86_ldub_code(s);
+}