From 430d7d88c1a123d787f529dbc29e6632c6e556fb Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Sat, 18 Mar 2023 12:48:01 -0400 Subject: [PATCH] analyzer: fix ICE on certain longjmp calls [PR109094] 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). 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 --- gcc/analyzer/region-model.cc | 15 ++++++++-- gcc/analyzer/region-model.h | 3 +- gcc/testsuite/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 --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 56beaa8..fb81d43 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 197b535..fe3db0b 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 0000000..10591ce --- /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); +} -- 2.7.4