From patchwork Thu Jan 23 22:16:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 1228584 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-518186-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.a=rsa-sha1 header.s=default header.b=VBcaVA8A; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=bMxq9RwQ; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 483c893WCyz9s1x for ; Fri, 24 Jan 2020 09:17:11 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; q=dns; s= default; b=R1cGY3mrMh8/BVeeTzJGSQwx/Gcs2vKaaD7crdCJ96uvj7heBQAVZ UjJd3E+L+zGetbQQLcpVbL5DPzGAgKGO6qA8CRppraAxM5hBwytx8CJZ6ENzSMaa HOcyOwCFS6zha1KJVXQTU1bAWO3UmQ2Nrv6Jj4mwirCz/gLIVWOb2w= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; s=default; bh=a7Me5KH1cOfatcZ/onK2fmMyT3U=; b=VBcaVA8AwK2Vudr70dqAk33Aw1oz 1NXW0uHzO0WUsaGAgINtcOif+5Z5eaJPFdfmOfd7Iy+5KjLuNC/cUCRJNafjKpTq POmd8EsT6DZXTiJE+oEL3a+ttJ7OokzDBplyvb7ADqUlArs5K9JRgwaSJaIHZRkg lgxr1pdI/Jym4+M= Received: (qmail 5724 invoked by alias); 23 Jan 2020 22:17:02 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 5714 invoked by uid 89); 23 Jan 2020 22:17:02 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-23.3 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS, T_FILL_THIS_FORM_SHORT autolearn=ham version=3.3.1 spammy= X-HELO: us-smtp-delivery-1.mimecast.com Received: from us-smtp-1.mimecast.com (HELO us-smtp-delivery-1.mimecast.com) (207.211.31.81) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 23 Jan 2020 22:16:59 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1579817817; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=b5KjjIIGHzWKJF/M4oUidhFz/HxfBLOH5f7eAPOzY+Y=; b=bMxq9RwQ6sIuygeKl8QbVTYAY7c4nGCgFVYcTMZagwVJ+b6NaeL/LE48VMlnuAN4MF/JA6 oZyHFUYRpfursDr53m+9akVUhh2WSQo5mg1Ko8KyiFEpEW8KuMuDeHns9ge/kc+hv9GKe/ fLcu7n3olhRuMRgEUcltREQ8rikpPdk= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-99-11Y6WvY3PcOUJNkVaKbIfg-1; Thu, 23 Jan 2020 17:16:51 -0500 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id C03A28010CA for ; Thu, 23 Jan 2020 22:16:50 +0000 (UTC) Received: from t470.redhat.com (ovpn-117-41.phx2.redhat.com [10.3.117.41]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1681E84777; Thu, 23 Jan 2020 22:16:49 +0000 (UTC) From: David Malcolm To: Jakub Jelinek Cc: gcc-patches@gcc.gnu.org, David Malcolm Subject: [PATCH] analyzer: fix setjmp-detection and support sigsetjmp Date: Thu, 23 Jan 2020 17:16:48 -0500 Message-Id: <20200123221648.22050-1-dmalcolm@redhat.com> In-Reply-To: <20200122194002.GQ10088@tucnak> References: <20200122194002.GQ10088@tucnak> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-IsSubscribed: yes On Wed, 2020-01-22 at 20:40 +0100, Jakub Jelinek wrote: > On Wed, Jan 22, 2020 at 02:35:13PM -0500, David Malcolm wrote: > > PR analyzer/93316 reports various testsuite failures where I > > accidentally relied on properties of x86_64-pc-linux-gnu. > > > > The following patch fixes them on sparc-sun-solaris2.11 (gcc211 in > > the > > GCC compile farm), and, I hope, the other configurations showing > > failures. > > > > There may still be other failures for pattern-test-2.c, which I'm > > tracking separately as PR analyzer/93291. > > > > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu; > > tested on stage 1 on sparc-sun-solaris2.11. > > > > gcc/analyzer/ChangeLog: > > PR analyzer/93316 > > * analyzer.cc (is_setjmp_call_p): Check for "setjmp" as well as > > "_setjmp". > > Please see calls.c (special_function_p), you should treat certainly > also sigsetjmp as a setjmp call, and similarly to special_function_p, > skip over _ or __ prefixes before the setjmp or sigsetjmp name. > Similarly for longjmp/siglongjmp. > > Jakub Thanks. 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. Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. OK for master? 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. --- gcc/analyzer/analyzer.cc | 83 +++++++++++++++++---- gcc/analyzer/analyzer.h | 4 +- gcc/analyzer/checker-path.cc | 14 ++-- gcc/analyzer/checker-path.h | 30 +++++--- gcc/analyzer/diagnostic-manager.cc | 6 +- gcc/analyzer/engine.cc | 21 +++--- gcc/analyzer/exploded-graph.h | 14 +++- gcc/analyzer/region-model.cc | 8 +- gcc/analyzer/region-model.h | 9 ++- gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c | 19 +++++ gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c | 35 +++++++++ 11 files changed, 188 insertions(+), 55 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/analyzer/sigsetjmp-5.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/sigsetjmp-6.c 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 e84e6958cec..90ed241c553 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 737ea1dd6e4..1fdedf49224 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/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); +}