From patchwork Thu Mar 28 17:17:55 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Keith Seitz X-Patchwork-Id: 32045 Received: (qmail 28750 invoked by alias); 28 Mar 2019 17:18:00 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 28705 invoked by uid 89); 28 Mar 2019 17:18:00 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-14.1 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, SPF_HELO_PASS autolearn=ham version=3.3.1 spammy=cpsupportc, UD:cp-support.c, cp-support.c, prevent X-HELO: mx1.redhat.com 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; Thu, 28 Mar 2019 17:17:59 +0000 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 mx1.redhat.com (Postfix) with ESMTPS id DEEB630992B6 for ; Thu, 28 Mar 2019 17:17:57 +0000 (UTC) Received: from theo.uglyboxes.com.com (ovpn-116-63.phx2.redhat.com [10.3.116.63]) by smtp.corp.redhat.com (Postfix) with ESMTP id AA32A5D71A for ; Thu, 28 Mar 2019 17:17:57 +0000 (UTC) From: Keith Seitz To: gdb-patches@sourceware.org Subject: [RFC PATCH] c++/24367: Infinite recursion of typedef substitution Date: Thu, 28 Mar 2019 10:17:55 -0700 Message-Id: <20190328171755.16464-1-keiths@redhat.com> MIME-Version: 1.0 X-IsSubscribed: yes This bug finds another usage where we end up segfaulting while normalizing user input. inspect_type and replace_type recurse, attempting to substitute the "real" symbol name for the typedef name. However, since the both these names are the same, they keep calling each other until the stack overflows. A simple reproducer for it is given by typedef struct foo foo; int qux (foo *f) { return 0; } (gdb) b qux(foo*) Segmentation fault inspect_type already contains some special handling to prevent a similar situation from occurring with namespaces. I wonder, however, whether we need be so pedantic about the exact nature of the substitution. Shouldn't we rather prevent these substitutions whenever the replacement symbol's name is exactly the same as the one we're trying to substitute? [In the above example, we're trying to substitute the tyepdef named "foo" with the symbol named "foo" (a struct).] Buildbot did not highlight any regrressions from using this more "aggressive" defense. Comments/opinions? Keith --- gdb/cp-support.c | 20 +++++++++++++++----- gdb/testsuite/gdb.cp/meth-typedefs.cc | 13 +++++++++++++ gdb/testsuite/gdb.cp/meth-typedefs.exp | 5 +++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/gdb/cp-support.c b/gdb/cp-support.c index b79dd5c086..b1c27187fe 100644 --- a/gdb/cp-support.c +++ b/gdb/cp-support.c @@ -191,12 +191,22 @@ inspect_type (struct demangle_parse_info *info, /* Get the real type of the typedef. */ type = check_typedef (otype); - /* If the symbol is a namespace and its type name is no different - than the name we looked up, this symbol is not a namespace - alias and does not need to be substituted. */ - if (TYPE_CODE (otype) == TYPE_CODE_NAMESPACE + /* If the symbol name is the same as the original type name, + don't substitute. That would cause infinite recursion in + symbol lookups, as the typedef symbol is often the first + found symbol in the symbol table. + + However, this can happen in a number of situations, such as: + + If the symbol is a namespace and its type name is no different + than the name we looked up, this symbol is not a namespace + alias and does not need to be substituted. + + If the symbol is typedef and its type name is the same + as the symbol's name, e.g., "typedef struct foo foo;". */ + if (TYPE_NAME (type) != nullptr && strcmp (TYPE_NAME (type), name) == 0) - return 0; + return 0; is_anon = (TYPE_NAME (type) == NULL && (TYPE_CODE (type) == TYPE_CODE_ENUM diff --git a/gdb/testsuite/gdb.cp/meth-typedefs.cc b/gdb/testsuite/gdb.cp/meth-typedefs.cc index 0c4d095c7f..f65478e8c0 100644 --- a/gdb/testsuite/gdb.cp/meth-typedefs.cc +++ b/gdb/testsuite/gdb.cp/meth-typedefs.cc @@ -36,6 +36,13 @@ typedef void (*fptr2) (fptr1, my_other_type_2); typedef void (*fptr3) (fptr2, my_other_type); typedef void (*fptr4) (anon_enum a, anon_struct const& b, anon_union const*** c); +// For c++/24367 testing +typedef struct incomplete_struct incomplete_struct; +typedef struct _incomplete_struct another_incomplete_struct; +int test_incomplete (incomplete_struct *p) { return 0; } // test_incomplete(incomplete_struct*) +int test_incomplete (another_incomplete_struct *p) { return 1; } // test_incomplete(another_incomplete_struct*) +int test_incomplete (int *p) { return -1; } // test_incomplete(int*) + namespace A { class foo @@ -147,5 +154,11 @@ main (void) fptr4 f4; + // Tests for c++/24367 + int *i = nullptr; + incomplete_struct *is = nullptr; + another_incomplete_struct *ais = nullptr; + int result = (test_incomplete (i) + test_incomplete (is) + + test_incomplete (ais)); return 0; } diff --git a/gdb/testsuite/gdb.cp/meth-typedefs.exp b/gdb/testsuite/gdb.cp/meth-typedefs.exp index b9c383bb25..76a8fc9780 100644 --- a/gdb/testsuite/gdb.cp/meth-typedefs.exp +++ b/gdb/testsuite/gdb.cp/meth-typedefs.exp @@ -137,6 +137,11 @@ foreach t $typedefs(_BAR_) { add methods "test" "$t&" {_BAR_&} } +# Tests for c++/24367 +foreach t {int incomplete_struct another_incomplete_struct} { + add methods "test_incomplete" "${t}*" [string_to_regexp "${t}*"] +} + gdb_test_no_output "set listsize 1" "" # Finally, for each method in the list METHODS, check whether