From patchwork Wed Sep 4 14:29:43 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gary Benson X-Patchwork-Id: 272644 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "www.sourceware.org", Issuer "StartCom Class 1 Primary Intermediate Server CA" (not verified)) by ozlabs.org (Postfix) with ESMTPS id 94A022C00CB for ; Thu, 5 Sep 2013 00:29:57 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; q=dns; s=default; b=A0ZWfMDBza/eT+sCPyoOYtH7h+ClOmsVMFwCpkQg+rNcd3cEH/ LxcVIL9NxrvTLyYddRnG8ZEVmAoTcc/G9UF0Q5wyM9simALtmqsOWW0ZZPYF4d+g onRFSzECuC0NIYbrlFAQ7VrHdC3+h3D8JbKVqUxzRWhCzlPldyHpcp9HQ= 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:date :from:to:cc:subject:message-id:mime-version:content-type; s= default; bh=nsMKmFJnDeSGBizHeOKfQcjRR1c=; b=jFK3EjkZc5fSsNYyuYT/ pwL7Yp6GiMpFz7FTSVKarJtDboy5BTtihms0E8DQGhHw95eOO1jbnB2C3SR0NtY2 uMN1pXUPtCYvWP2oMmN5zQveEgUYkxFMNig6uv4AqKM5d18B0c2jU60HowGZIgYX DOaKvvcIepo7BuRbc5uvDl4= Received: (qmail 16434 invoked by alias); 4 Sep 2013 14:29:50 -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 16415 invoked by uid 89); 4 Sep 2013 14:29:49 -0000 Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 04 Sep 2013 14:29:49 +0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.3 required=5.0 tests=AWL, BAYES_00, KAM_STOCKTIP, RP_MATCHES_RCVD autolearn=no version=3.3.2 X-HELO: mx1.redhat.com Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r84ETjax000747 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 4 Sep 2013 10:29:45 -0400 Received: from blade.nx (ovpn-116-77.ams2.redhat.com [10.36.116.77]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id r84ETiZ6020728; Wed, 4 Sep 2013 10:29:44 -0400 Received: by blade.nx (Postfix, from userid 1000) id AECB5264A19; Wed, 4 Sep 2013 15:29:43 +0100 (BST) Date: Wed, 4 Sep 2013 15:29:43 +0100 From: Gary Benson To: gcc-patches@gcc.gnu.org Cc: DJ Delorie , Ian Lance Taylor Subject: C++ demangler fix Message-ID: <20130904142943.GA18972@blade.nx> Mail-Followup-To: gcc-patches@gcc.gnu.org, DJ Delorie , Ian Lance Taylor MIME-Version: 1.0 Content-Disposition: inline X-IsSubscribed: yes Hi all, d_print_comp maintains a certain amount of scope across calls (namely a stack of templates) which is used when evaluating references in template argument lists. If such a reference is later used from a subtitution then the scope in force at the time of the substitution is used. This appears to be wrong (I say appears because I couldn't find anything in http://mentorembedded.github.io/cxx-abi/abi.html#mangling to clarify this). The attached patch causes the demangler to capture the scope the first time such a reference is traversed, and to use that captured scope on subsequent traversals. This fixes http://sourceware.org/bugzilla/show_bug.cgi?id=14963 whereby a reference is resolved against the wrong template, causing an infinite loop and eventual stack overflow and segmentation fault. I've added the result to the demangler test suite, but I know of no way to check the validity of the demangled symbol other than by inspection (and I am no expert here!) If anybody knows a way to check this then please let me know! Otherwise, I hope this not-really-checked demangled version is acceptable. Thanks, Gary --- http://gbenson.net/ diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog index e4ce0b9..a084282 100644 --- a/libiberty/ChangeLog +++ b/libiberty/ChangeLog @@ -1,3 +1,22 @@ +2013-09-04 Gary Benson + + * cp-demangle.c: Include hashtab.h. + (struct d_print_info): New field saved_scopes. + (d_print_init): Initialize the above. + (d_print_free): New function. + (cplus_demangle_print_callback): Call the above. + (struct d_saved_scope): New structure. + (d_store_scope): New function. + (d_free_scope) Likewise. + (d_restore_scope) Likewise. + (d_hash_saved_scope) Likewise. + (d_equal_saved_scope) Likewise. + (d_print_comp): New variable saved_scope. + [DEMANGLE_COMPONENT_REFERENCE, + DEMANGLE_COMPONENT_RVALUE_REFERENCE]: Capture scope the first + time the component is traversed, and use the captured scope for + subsequent traversals. + 2013-08-20 Alan Modra * floatformat.c (floatformat_ibm_long_double): Rename to.. diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c index 70f5438..d44e82a 100644 --- a/libiberty/cp-demangle.c +++ b/libiberty/cp-demangle.c @@ -128,6 +128,7 @@ extern char *alloca (); #include "libiberty.h" #include "demangle.h" #include "cp-demangle.h" +#include "hashtab.h" /* If IN_GLIBCPP_V3 is defined, some functions are made static. We also rename them via #define to avoid compiler errors when the @@ -302,6 +303,10 @@ struct d_print_info int pack_index; /* Number of d_print_flush calls so far. */ unsigned long int flush_count; + /* Table mapping demangle components to scopes saved when first + traversing those components. These are used while evaluating + substitutions. */ + htab_t saved_scopes; }; #ifdef CP_DEMANGLE_DEBUG @@ -3665,6 +3670,17 @@ d_print_init (struct d_print_info *dpi, demangle_callbackref callback, dpi->opaque = opaque; dpi->demangle_failure = 0; + + dpi->saved_scopes = NULL; +} + +/* Free a print information structure. */ + +static void +d_print_free (struct d_print_info *dpi) +{ + if (dpi->saved_scopes != NULL) + htab_delete (dpi->saved_scopes); } /* Indicate that an error occurred during printing, and test for error. */ @@ -3749,6 +3765,7 @@ cplus_demangle_print_callback (int options, demangle_callbackref callback, void *opaque) { struct d_print_info dpi; + int success; d_print_init (&dpi, callback, opaque); @@ -3756,7 +3773,9 @@ cplus_demangle_print_callback (int options, d_print_flush (&dpi); - return ! d_print_saw_error (&dpi); + success = ! d_print_saw_error (&dpi); + d_print_free (&dpi); + return success; } /* Turn components into a human readable string. OPTIONS is the @@ -3913,6 +3932,114 @@ d_print_subexpr (struct d_print_info *dpi, int options, d_append_char (dpi, ')'); } +/* A demangle component and some scope captured when it was first + traversed. */ + +struct d_saved_scope +{ + /* The component whose scope this is. Used as the key for the + saved_scopes hashtable in d_print_info. May be NULL if this + scope will not be inserted into that table. */ + const struct demangle_component *container; + /* Nonzero if the below items are copies and require freeing + when this scope is freed. */ + int is_copy; + /* The list of templates, if any, that was current when this + scope was captured. */ + struct d_print_template *templates; +}; + +/* Allocate a scope and populate it with the current values from DPI. + CONTAINER is the demangle component to which the scope refers, and + is used as the key for the saved_scopes hashtable in d_print_info. + CONTAINER may be NULL if this scope will not be inserted into that + table. If COPY is nonzero then items that may have been allocated + on the stack will be copied before storing. */ + +static struct d_saved_scope * +d_store_scope (const struct d_print_info *dpi, + const struct demangle_component *container, int copy) +{ + struct d_saved_scope *scope = XNEW (struct d_saved_scope); + + scope->container = container; + scope->is_copy = copy; + + if (copy) + { + struct d_print_template *ts, **tl = &scope->templates; + + for (ts = dpi->templates; ts != NULL; ts = ts->next) + { + struct d_print_template *td = XNEW (struct d_print_template); + + *tl = td; + tl = &td->next; + td->template_decl = ts->template_decl; + } + *tl = NULL; + } + else + scope->templates = dpi->templates; + + return scope; +} + +/* Free a scope allocated by d_store_scope. */ + +static void +d_free_scope (void *p) +{ + struct d_saved_scope *scope = (struct d_saved_scope *) p; + + if (scope->is_copy) + { + struct d_print_template *ts, *tn; + + for (ts = scope->templates; ts != NULL; ts = tn) + { + tn = ts->next; + free (ts); + } + } + + free (scope); +} + +/* Restore a stored scope to DPI, optionally freeing it afterwards. */ + +static void +d_restore_scope (struct d_print_info *dpi, struct d_saved_scope *scope, + int free_after) +{ + dpi->templates = scope->templates; + + if (free_after) + d_free_scope (scope); +} + +/* Returns a hash code for the saved scope referenced by p. */ + +static hashval_t +d_hash_saved_scope (const void *p) +{ + const struct d_saved_scope *s = (const struct d_saved_scope *) p; + + return htab_hash_pointer (s->container); +} + +/* Returns non-zero if the saved scopes referenced by p1 and p2 + are equal. */ + +static int +d_equal_saved_scope (const void *p1, const void *p2) +{ + const struct d_saved_scope *s1 = (const struct d_saved_scope *) p1; + const struct d_saved_scope *s2 = (const struct d_saved_scope *) p2; + + return s1->container == s2->container; +} + /* Subroutine to handle components. */ static void @@ -3923,6 +4050,10 @@ d_print_comp (struct d_print_info *dpi, int options, without needing to modify *dc. */ const struct demangle_component *mod_inner = NULL; + /* Variable used to store the current scope while a previously + captured scope is used. */ + struct d_saved_scope *saved_scope = NULL; + if (dc == NULL) { d_print_error (dpi); @@ -4291,12 +4422,43 @@ d_print_comp (struct d_print_info *dpi, int options, const struct demangle_component *sub = d_left (dc); if (sub->type == DEMANGLE_COMPONENT_TEMPLATE_PARAM) { - struct demangle_component *a = d_lookup_template_argument (dpi, sub); + struct demangle_component *a; + struct d_saved_scope lookup; + void **slot; + + if (dpi->saved_scopes == NULL) + dpi->saved_scopes = htab_create_alloc (1, + d_hash_saved_scope, + d_equal_saved_scope, + d_free_scope, + xcalloc, free); + + lookup.container = sub; + slot = htab_find_slot (dpi->saved_scopes, &lookup, INSERT); + if (*slot == HTAB_EMPTY_ENTRY) + { + /* This is the first time SUB has been traversed. + We need to capture some scope so it can be + restored if SUB is reentered as a substitution. */ + *slot = d_store_scope (dpi, sub, 1); + } + else + { + /* This traversal is reentering SUB as a substition. + Restore the original scope temporarily. */ + saved_scope = d_store_scope (dpi, NULL, 0); + d_restore_scope (dpi, (struct d_saved_scope *) *slot, 0); + } + + a = d_lookup_template_argument (dpi, sub); if (a && a->type == DEMANGLE_COMPONENT_TEMPLATE_ARGLIST) a = d_index_template_argument (a, dpi->pack_index); if (a == NULL) { + if (saved_scope != NULL) + d_restore_scope (dpi, saved_scope, 1); + d_print_error (dpi); return; } @@ -4344,6 +4506,9 @@ d_print_comp (struct d_print_info *dpi, int options, dpi->modifiers = dpm.next; + if (saved_scope != NULL) + d_restore_scope (dpi, saved_scope, 1); + return; } diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected index 1259e4a..d3a3603 100644 --- a/libiberty/testsuite/demangle-expected +++ b/libiberty/testsuite/demangle-expected @@ -4291,3 +4291,6 @@ void m(void (A::*)() &&) --format=gnu-v3 _Z1nIM1AKFvvREEvT_ void n(void (A::*)() const &) +--format=gnu-v3 +_ZSt7forwardIRN1x14refobjiteratorINS0_3refINS0_4mime30multipart_section_processorObjIZ15get_body_parserIZZN14mime_processor21make_section_iteratorERKNS2_INS3_10sectionObjENS0_10ptrrefBaseEEEbENKUlvE_clEvEUlSB_bE_ZZNS6_21make_section_iteratorESB_bENKSC_clEvEUlSB_E0_ENS1_INS2_INS0_20outputrefiteratorObjIiEES8_EEEERKSsSB_OT_OT0_EUlmE_NS3_32make_multipart_default_discarderISP_EEEES8_EEEEEOT_RNSt16remove_referenceISW_E4typeE +x::refobjiterator, x::ptrrefBase> > get_body_parser const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&, bool)#1}, mime_processor::make_section_iterator(x::ref const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&)#2}>(std::string const&, x::ref const&, mime_processor::make_section_iterator(x::ref const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&, bool)#1}&&, mime_processor::make_section_iterator(x::ref const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&)#2}&&)::{lambda(unsigned long)#1}, x::mime::make_multipart_default_discarder const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&, bool)#1}&&> >, x::ptrrefBase> >& std::forward, x::ptrrefBase> > get_body_parser const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&, bool)#1}, mime_processor::make_section_iterator(x::ref const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&)#2}>(std::string const&, x::ref const&, mime_processor::make_section_iterator(x::ref const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&, bool)#1}&&, mime_processor::make_section_iterator(x::ref const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&)#2}&&)::{lambda(unsigned long)#1}, x::mime::make_multipart_default_discarder const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&, bool)#1}&&> >, x::ptrrefBase> >&>(std::remove_reference, x::ptrrefBase> > get_body_parser const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&, bool)#1}, mime_processor::make_section_iterator(x::ref const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&)#2}>(std::string const&, x::ref const&, mime_processor::make_section_iterator(x::ref const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&, bool)#1}&&, mime_processor::make_section_iterator(x::ref const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&)#2}&&)::{lambda(unsigned long)#1}, x::mime::make_multipart_default_discarder const&, bool)::{lambda()#1}::operator()() const::{lambda(x::ref const&, bool)#1}&&> > >::type&)