From: David Malcolm Date: Thu, 23 Jan 2020 17:19:20 +0000 (-0500) Subject: analyzer: fix setjmp-detection and support sigsetjmp X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=342e14ffa30e9163a1a75e0a4fb21b6883d58dbe;p=gcc.git analyzer: fix setjmp-detection and support sigsetjmp This patch removes the hack in is_setjmp_call_p of looking for "setjmp" and "_setjmp", replacing it with some logic adapted from special_function_p in calls.c, ignoring up to 2 leading underscores from the fndecl's name when checking for a function by name. It also requires that such functions are "extern" and at file scope for them to be matched. The patch also generalizes the setjmp/longjmp handling in the analyzer to also work with sigsetjmp/siglongjmp. Doing so requires generalizing some hardcoded functions in diagnostics (which were hardcoded to avoid user-facing messages referring to "_setjmp", which is an implementation detail) - the patch adds a new function, get_user_facing_name for this, for use on calls that matched is_named_call_p and is_specical_named_call_p. gcc/analyzer/ChangeLog: * analyzer.cc (is_named_call_p): Check that fndecl is "extern" and at file scope. Potentially disregard prefix _ or __ in fndecl's name. Bail if the identifier is NULL. (is_setjmp_call_p): Expect a gcall rather than plain gimple. Remove special-case check for leading prefix, and also check for sigsetjmp. (is_longjmp_call_p): Also check for siglongjmp. (get_user_facing_name): New function. * analyzer.h (is_setjmp_call_p): Expect a gcall rather than plain gimple. (get_user_facing_name): New decl. * checker-path.cc (setjmp_event::get_desc): Use get_user_facing_name to avoid hardcoding the function name. (rewind_event::rewind_event): Add rewind_info param, using it to initialize new m_rewind_info field, and strengthen the assertion. (rewind_from_longjmp_event::get_desc): Use get_user_facing_name to avoid hardcoding the function name. (rewind_to_setjmp_event::get_desc): Likewise. * checker-path.h (setjmp_event::setjmp_event): Add setjmp_call param and use it to initialize... (setjmp_event::m_setjmp_call): New field. (rewind_event::rewind_event): Add rewind_info param. (rewind_event::m_rewind_info): New protected field. (rewind_from_longjmp_event::rewind_from_longjmp_event): Add rewind_info param. (class rewind_to_setjmp_event): Move rewind_info field to parent class. * diagnostic-manager.cc (diagnostic_manager::add_events_for_eedge): Update setjmp-handling for is_setjmp_call_p requiring a gcall; pass the call to the new setjmp_event. * engine.cc (exploded_node::on_stmt): Update for is_setjmp_call_p requiring a gcall. (stale_jmp_buf::emit): Use get_user_facing_name to avoid hardcoding the function names. (exploded_node::on_longjmp): Pass the longjmp_call when constructing rewind_info. (rewind_info_t::add_events_to_path): Pass the rewind_info_t to the rewind_from_longjmp_event's ctor. * exploded-graph.h (rewind_info_t::rewind_info_t): Add longjmp_call param. (rewind_info_t::get_longjmp_call): New. (rewind_info_t::m_longjmp_call): New. * region-model.cc (region_model::on_setjmp): Update comment to indicate this is also for sigsetjmp. * region-model.h (struct setjmp_record): Likewise. (class setjmp_svalue): Likewise. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/sigsetjmp-5.c: New test. * gcc.dg/analyzer/sigsetjmp-6.c: New test. --- diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index 359ea14a748..345d40f3faf 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,52 @@ +2020-01-27 David Malcolm + + * analyzer.cc (is_named_call_p): Check that fndecl is "extern" + and at file scope. Potentially disregard prefix _ or __ in + fndecl's name. Bail if the identifier is NULL. + (is_setjmp_call_p): Expect a gcall rather than plain gimple. + Remove special-case check for leading prefix, and also check for + sigsetjmp. + (is_longjmp_call_p): Also check for siglongjmp. + (get_user_facing_name): New function. + * analyzer.h (is_setjmp_call_p): Expect a gcall rather than plain + gimple. + (get_user_facing_name): New decl. + * checker-path.cc (setjmp_event::get_desc): Use + get_user_facing_name to avoid hardcoding the function name. + (rewind_event::rewind_event): Add rewind_info param, using it to + initialize new m_rewind_info field, and strengthen the assertion. + (rewind_from_longjmp_event::get_desc): Use get_user_facing_name to + avoid hardcoding the function name. + (rewind_to_setjmp_event::get_desc): Likewise. + * checker-path.h (setjmp_event::setjmp_event): Add setjmp_call + param and use it to initialize... + (setjmp_event::m_setjmp_call): New field. + (rewind_event::rewind_event): Add rewind_info param. + (rewind_event::m_rewind_info): New protected field. + (rewind_from_longjmp_event::rewind_from_longjmp_event): Add + rewind_info param. + (class rewind_to_setjmp_event): Move rewind_info field to parent + class. + * diagnostic-manager.cc (diagnostic_manager::add_events_for_eedge): + Update setjmp-handling for is_setjmp_call_p requiring a gcall; + pass the call to the new setjmp_event. + * engine.cc (exploded_node::on_stmt): Update for is_setjmp_call_p + requiring a gcall. + (stale_jmp_buf::emit): Use get_user_facing_name to avoid + hardcoding the function names. + (exploded_node::on_longjmp): Pass the longjmp_call when + constructing rewind_info. + (rewind_info_t::add_events_to_path): Pass the rewind_info_t to the + rewind_from_longjmp_event's ctor. + * exploded-graph.h (rewind_info_t::rewind_info_t): Add + longjmp_call param. + (rewind_info_t::get_longjmp_call): New. + (rewind_info_t::m_longjmp_call): New. + * region-model.cc (region_model::on_setjmp): Update comment to + indicate this is also for sigsetjmp. + * region-model.h (struct setjmp_record): Likewise. + (class setjmp_svalue): Likewise. + 2020-01-27 David Malcolm PR analyzer/93276 diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc index 3884788ee9e..1b5e4c9ecf8 100644 --- a/gcc/analyzer/analyzer.cc +++ b/gcc/analyzer/analyzer.cc @@ -54,7 +54,10 @@ is_special_named_call_p (const gcall *call, const char *funcname, return is_named_call_p (fndecl, funcname, call, num_args); } -/* Helper function for checkers. Does FNDECL have the given FUNCNAME? */ +/* Helper function for checkers. Is FNDECL an extern fndecl at file scope + that has the given FUNCNAME? + + Compare with special_function_p in calls.c. */ bool is_named_call_p (tree fndecl, const char *funcname) @@ -62,11 +65,38 @@ is_named_call_p (tree fndecl, const char *funcname) gcc_assert (fndecl); gcc_assert (funcname); - return 0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname); + /* Exclude functions not at the file scope, or not `extern', + since they are not the magic functions we would otherwise + think they are. */ + if (!((DECL_CONTEXT (fndecl) == NULL_TREE + || TREE_CODE (DECL_CONTEXT (fndecl)) == TRANSLATION_UNIT_DECL) + && TREE_PUBLIC (fndecl))) + return false; + + tree identifier = DECL_NAME (fndecl); + if (identifier == NULL) + return false; + + const char *name = IDENTIFIER_POINTER (identifier); + const char *tname = name; + + /* Potentially disregard prefix _ or __ in FNDECL's name, but not if + FUNCNAME itself has leading underscores (e.g. when looking for + "__analyzer_eval"). */ + if (funcname[0] != '_' && name[0] == '_') + { + if (name[1] == '_') + tname += 2; + else + tname += 1; + } + + return 0 == strcmp (tname, funcname); } -/* Helper function for checkers. Does FNDECL have the given FUNCNAME, and - does CALL have the given number of arguments? */ +/* Helper function for checkers. Is FNDECL an extern fndecl at file scope + that has the given FUNCNAME, and does CALL have the given number of + arguments? */ bool is_named_call_p (tree fndecl, const char *funcname, @@ -84,32 +114,57 @@ is_named_call_p (tree fndecl, const char *funcname, return true; } -/* Return true if stmt is a setjmp call. */ +/* Return true if stmt is a setjmp or sigsetjmp call. */ bool -is_setjmp_call_p (const gimple *stmt) +is_setjmp_call_p (const gcall *call) { - /* TODO: is there a less hacky way to check for "setjmp"? */ - if (const gcall *call = dyn_cast (stmt)) - if (is_special_named_call_p (call, "setjmp", 1) - || is_special_named_call_p (call, "_setjmp", 1)) - return true; + if (is_special_named_call_p (call, "setjmp", 1) + || is_special_named_call_p (call, "sigsetjmp", 2)) + return true; return false; } -/* Return true if stmt is a longjmp call. */ +/* Return true if stmt is a longjmp or siglongjmp call. */ bool is_longjmp_call_p (const gcall *call) { - /* TODO: is there a less hacky way to check for "longjmp"? */ - if (is_special_named_call_p (call, "longjmp", 2)) + if (is_special_named_call_p (call, "longjmp", 2) + || is_special_named_call_p (call, "siglongjmp", 2)) return true; return false; } +/* For a CALL that matched is_special_named_call_p or is_named_call_p for + some name, return a name for the called function suitable for use in + diagnostics (stripping the leading underscores). */ + +const char * +get_user_facing_name (const gcall *call) +{ + tree fndecl = gimple_call_fndecl (call); + gcc_assert (fndecl); + + tree identifier = DECL_NAME (fndecl); + gcc_assert (identifier); + + const char *name = IDENTIFIER_POINTER (identifier); + + /* Strip prefix _ or __ in FNDECL's name. */ + if (name[0] == '_') + { + if (name[1] == '_') + return name + 2; + else + return name + 1; + } + + return name; +} + /* Generate a label_text instance by formatting FMT, using a temporary clone of the global_dc's printer (thus using its formatting callbacks). diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 9746c9e0123..f1602e38b36 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -78,9 +78,11 @@ extern bool is_special_named_call_p (const gcall *call, const char *funcname, extern bool is_named_call_p (tree fndecl, const char *funcname); extern bool is_named_call_p (tree fndecl, const char *funcname, const gcall *call, unsigned int num_args); -extern bool is_setjmp_call_p (const gimple *stmt); +extern bool is_setjmp_call_p (const gcall *call); extern bool is_longjmp_call_p (const gcall *call); +extern const char *get_user_facing_name (const gcall *call); + extern void register_analyzer_pass (); extern label_text make_label_text (bool can_colorize, const char *fmt, ...); diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc index f7455ba1245..7f6cdf599cf 100644 --- a/gcc/analyzer/checker-path.cc +++ b/gcc/analyzer/checker-path.cc @@ -709,7 +709,7 @@ setjmp_event::get_desc (bool can_colorize) const { return make_label_text (can_colorize, "%qs called here", - "setjmp"); + get_user_facing_name (m_setjmp_call)); } /* Implementation of checker_event::prepare_for_emission vfunc for setjmp_event. @@ -748,11 +748,13 @@ rewind_event::get_setjmp_caller () const rewind_event::rewind_event (const exploded_edge *eedge, enum event_kind kind, - location_t loc, tree fndecl, int depth) + location_t loc, tree fndecl, int depth, + const rewind_info_t *rewind_info) : checker_event (kind, loc, fndecl, depth), + m_rewind_info (rewind_info), m_eedge (eedge) { - gcc_assert (m_eedge->m_custom_info); // a rewind_info_t + gcc_assert (m_eedge->m_custom_info == m_rewind_info); } /* class rewind_from_longjmp_event : public rewind_event. */ @@ -763,7 +765,8 @@ rewind_event::rewind_event (const exploded_edge *eedge, label_text rewind_from_longjmp_event::get_desc (bool can_colorize) const { - const char *src_name = "longjmp"; + const char *src_name + = get_user_facing_name (m_rewind_info->get_longjmp_call ()); if (get_longjmp_caller () == get_setjmp_caller ()) /* Special-case: purely intraprocedural rewind. */ @@ -786,7 +789,8 @@ rewind_from_longjmp_event::get_desc (bool can_colorize) const label_text rewind_to_setjmp_event::get_desc (bool can_colorize) const { - const char *dst_name = "setjmp"; + const char *dst_name + = get_user_facing_name (m_rewind_info->get_setjmp_call ()); /* If we can, identify the ID of the setjmp_event. */ if (m_original_setjmp_event_id.known_p ()) diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h index cceffe079fa..30cb43c13ba 100644 --- a/gcc/analyzer/checker-path.h +++ b/gcc/analyzer/checker-path.h @@ -329,15 +329,15 @@ public: bool is_return_p () const FINAL OVERRIDE; }; -/* A concrete event subclass for a setjmp call. */ +/* A concrete event subclass for a setjmp or sigsetjmp call. */ class setjmp_event : public checker_event { public: setjmp_event (location_t loc, const exploded_node *enode, - tree fndecl, int depth) + tree fndecl, int depth, const gcall *setjmp_call) : checker_event (EK_SETJMP, loc, fndecl, depth), - m_enode (enode) + m_enode (enode), m_setjmp_call (setjmp_call) { } @@ -349,9 +349,12 @@ public: private: const exploded_node *m_enode; + const gcall *m_setjmp_call; }; -/* An abstract event subclass for rewinding from a longjmp to a setjmp. +/* An abstract event subclass for rewinding from a longjmp to a setjmp + (or siglongjmp to sigsetjmp). + Base class for two from/to subclasses, showing the two halves of the rewind. */ @@ -365,21 +368,25 @@ public: protected: rewind_event (const exploded_edge *eedge, enum event_kind kind, - location_t loc, tree fndecl, int depth); + location_t loc, tree fndecl, int depth, + const rewind_info_t *rewind_info); + const rewind_info_t *m_rewind_info; private: const exploded_edge *m_eedge; }; /* A concrete event subclass for rewinding from a longjmp to a setjmp, - showing the longjmp. */ + showing the longjmp (or siglongjmp). */ class rewind_from_longjmp_event : public rewind_event { public: rewind_from_longjmp_event (const exploded_edge *eedge, - location_t loc, tree fndecl, int depth) - : rewind_event (eedge, EK_REWIND_FROM_LONGJMP, loc, fndecl, depth) + location_t loc, tree fndecl, int depth, + const rewind_info_t *rewind_info) + : rewind_event (eedge, EK_REWIND_FROM_LONGJMP, loc, fndecl, depth, + rewind_info) { } @@ -387,7 +394,7 @@ public: }; /* A concrete event subclass for rewinding from a longjmp to a setjmp, - showing the setjmp. */ + showing the setjmp (or sigsetjmp). */ class rewind_to_setjmp_event : public rewind_event { @@ -395,8 +402,8 @@ public: rewind_to_setjmp_event (const exploded_edge *eedge, location_t loc, tree fndecl, int depth, const rewind_info_t *rewind_info) - : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth), - m_rewind_info (rewind_info) + : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth, + rewind_info) { } @@ -408,7 +415,6 @@ public: private: diagnostic_event_id_t m_original_setjmp_event_id; - const rewind_info_t *m_rewind_info; }; /* Concrete subclass of checker_event for use at the end of a path: diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc index 02bc4a61e60..eb1fa05533e 100644 --- a/gcc/analyzer/diagnostic-manager.cc +++ b/gcc/analyzer/diagnostic-manager.cc @@ -818,12 +818,14 @@ diagnostic_manager::add_events_for_eedge (const exploded_edge &eedge, case PK_BEFORE_STMT: { const gimple *stmt = dst_point.get_stmt (); - if (is_setjmp_call_p (stmt)) + const gcall *call = dyn_cast (stmt); + if (call && is_setjmp_call_p (call)) emission_path->add_event (new setjmp_event (stmt->location, dst_node, dst_point.get_fndecl (), - dst_stack_depth)); + dst_stack_depth, + call)); else emission_path->add_event (new statement_event (stmt, diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index a2587a33a66..b39058f9c10 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -1001,7 +1001,7 @@ exploded_node::on_stmt (exploded_graph &eg, { /* This is handled elsewhere. */ } - else if (is_setjmp_call_p (stmt)) + else if (is_setjmp_call_p (call)) state->m_region_model->on_setjmp (call, this, &ctxt); else if (is_longjmp_call_p (call)) { @@ -1126,7 +1126,8 @@ public: return warning_at (richloc, OPT_Wanalyzer_stale_setjmp_buffer, "%qs called after enclosing function of %qs has returned", - "longjmp", "setjmp"); + get_user_facing_name (m_longjmp_call), + get_user_facing_name (m_setjmp_call)); } const char *get_kind () const FINAL OVERRIDE @@ -1143,10 +1144,10 @@ private: const gcall *m_longjmp_call; }; -/* Handle LONGJMP_CALL, a call to "longjmp". +/* Handle LONGJMP_CALL, a call to longjmp or siglongjmp. - Attempt to locate where "setjmp" was called on the jmp_buf and build an - exploded_node and exploded_edge to it representing a rewind to that frame, + Attempt to locate where setjmp/sigsetjmp was called on the jmp_buf and build + an exploded_node and exploded_edge to it representing a rewind to that frame, handling the various kinds of failure that can occur. */ void @@ -1174,9 +1175,9 @@ exploded_node::on_longjmp (exploded_graph &eg, const setjmp_record tmp_setjmp_record = setjmp_sval->get_setjmp_record (); - /* Build a custom enode and eedge for rewinding from the longjmp - call back to the setjmp. */ - rewind_info_t rewind_info (tmp_setjmp_record); + /* Build a custom enode and eedge for rewinding from the longjmp/siglongjmp + call back to the setjmp/sigsetjmp. */ + rewind_info_t rewind_info (tmp_setjmp_record, longjmp_call); const gcall *setjmp_call = rewind_info.get_setjmp_call (); const program_point &setjmp_point = rewind_info.get_setjmp_point (); @@ -1217,7 +1218,7 @@ exploded_node::on_longjmp (exploded_graph &eg, exploded_edge *eedge = eg.add_edge (const_cast (this), next, NULL, change, - new rewind_info_t (tmp_setjmp_record)); + new rewind_info_t (tmp_setjmp_record, longjmp_call)); /* For any diagnostics that were queued here (such as leaks) we want the checker_path to show the rewinding events after the "final event" @@ -1369,7 +1370,7 @@ rewind_info_t::add_events_to_path (checker_path *emission_path, (new rewind_from_longjmp_event (&eedge, src_point.get_supernode ()->get_end_location (), src_point.get_fndecl (), - src_stack_depth)); + src_stack_depth, this)); emission_path->add_event (new rewind_to_setjmp_event (&eedge, get_setjmp_call ()->location, diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h index 3d1445c87ad..a3e758ed751 100644 --- a/gcc/analyzer/exploded-graph.h +++ b/gcc/analyzer/exploded-graph.h @@ -302,13 +302,15 @@ private: }; /* Extra data for an exploded_edge that represents a rewind from a - longjmp to a setjmp. */ + longjmp to a setjmp (or from a siglongjmp to a sigsetjmp). */ class rewind_info_t : public exploded_edge::custom_info_t { public: - rewind_info_t (const setjmp_record &setjmp_record) - : m_setjmp_record (setjmp_record) + rewind_info_t (const setjmp_record &setjmp_record, + const gcall *longjmp_call) + : m_setjmp_record (setjmp_record), + m_longjmp_call (longjmp_call) {} void print (pretty_printer *pp) FINAL OVERRIDE @@ -339,6 +341,11 @@ public: return m_setjmp_record.m_setjmp_call; } + const gcall *get_longjmp_call () const + { + return m_longjmp_call; + } + const exploded_node *get_enode_origin () const { return m_setjmp_record.m_enode; @@ -346,6 +353,7 @@ public: private: setjmp_record m_setjmp_record; + const gcall *m_longjmp_call; }; /* Statistics about aspects of an exploded_graph. */ diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 25a22f8fc65..985f1bd56ac 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -4480,11 +4480,11 @@ region_model::on_return (const greturn *return_stmt, region_model_context *ctxt) set_value (get_lvalue (lhs, ctxt), get_rvalue (rhs, ctxt), ctxt); } -/* Update this model for a call and return of "setjmp" at CALL within ENODE, - using CTXT to report any diagnostics. +/* Update this model for a call and return of setjmp/sigsetjmp at CALL within + ENODE, using CTXT to report any diagnostics. - This is for the initial direct invocation of setjmp (which returns 0), - as opposed to any second return due to longjmp. */ + This is for the initial direct invocation of setjmp/sigsetjmp (which returns + 0), as opposed to any second return due to longjmp/sigsetjmp. */ void region_model::on_setjmp (const gcall *call, const exploded_node *enode, diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index f7fb7b0b6d0..70e3eb4c716 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -718,8 +718,8 @@ is_a_helper ::test (svalue *sval) namespace ana { -/* A bundle of information recording a setjmp call, corresponding roughly - to a jmp_buf. */ +/* A bundle of information recording a setjmp/sigsetjmp call, corresponding + roughly to a jmp_buf. */ struct setjmp_record { @@ -739,8 +739,9 @@ struct setjmp_record const gcall *m_setjmp_call; }; -/* Concrete subclass of svalue representing setjmp buffers, so that - longjmp can potentially "return" to an entirely different function. */ +/* Concrete subclass of svalue representing buffers for setjmp/sigsetjmp, + so that longjmp/siglongjmp can potentially "return" to an entirely + different function. */ class setjmp_svalue : public svalue { diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index bd76fa78e91..e8928777fd9 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2020-01-27 David Malcolm + + * gcc.dg/analyzer/sigsetjmp-5.c: New test. + * gcc.dg/analyzer/sigsetjmp-6.c: New test. + 2020-01-27 Richard Biener PR testsuite/91171 diff --git a/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c b/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c new file mode 100644 index 00000000000..68afe9d1c97 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c @@ -0,0 +1,19 @@ +#include +#include +#include "analyzer-decls.h" + +static jmp_buf env; + +static void inner (void) +{ + sigsetjmp (env, 0); /* { dg-message "'sigsetjmp' called here" } */ +} + +void outer (void) +{ + int i; + + inner (); + + siglongjmp (env, 42); /* { dg-warning "'siglongjmp' called after enclosing function of 'sigsetjmp' has returned" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c b/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c new file mode 100644 index 00000000000..fcd9d0bbb47 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c @@ -0,0 +1,35 @@ +#include +#include +#include + +extern int foo (int) __attribute__ ((__pure__)); + +static jmp_buf env; + +static void inner (void) +{ + void *ptr = malloc (1024); /* { dg-message "allocated here" } */ + + siglongjmp (env, 1); /* { dg-warning "leak of 'ptr'" "warning" } */ + /* { dg-message "rewinding from 'siglongjmp' in 'inner'" " event: rewind from" { target *-*-* } .-1 } */ + + free (ptr); +} + +void outer (void) +{ + int i; + + foo (0); + + i = sigsetjmp(env, 0); /* { dg-message "'sigsetjmp' called here" "event: sigsetjmp call" } */ + /* { dg-message "to 'sigsetjmp' in 'outer'" "event: rewind to" { target *-*-* } .-1 } */ + + if (i == 0) + { + foo (1); + inner (); + } + + foo (3); +}