]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Do not crash on recursive pointer types
authorTom Tromey <tromey@adacore.com>
Fri, 14 Nov 2025 14:49:50 +0000 (07:49 -0700)
committerTom Tromey <tromey@adacore.com>
Mon, 17 Nov 2025 15:03:47 +0000 (08:03 -0700)
In Ada, it's possible to create a recursive pointer type, or two
mutually recursive pointer types.  gdb will crash when reading the
DWARF for these.

These types aren't actually useful, and so are unlikely to appear in
real code.  Still, gdb shouldn't crash.

This patch handles this case by noticing recursive pointer types and
representing them as pointer-to-void.

Approved-by: Kevin Buettner <kevinb@redhat.com>
gdb/dwarf2/read.c
gdb/testsuite/gdb.ada/recursive-access.exp [new file with mode: 0644]
gdb/testsuite/gdb.ada/recursive-access/pack.ads [new file with mode: 0644]
gdb/testsuite/gdb.ada/recursive-access/prog.adb [new file with mode: 0644]

index 961cf430e0bdce347115f7f4409ed1e65878013f..8bc98342db8fa49d819405728e7c2c679c3ef7ed 100644 (file)
@@ -916,6 +916,9 @@ static const char *dwarf2_physname (const char *name, struct die_info *die,
 static struct die_info *dwarf2_extension (struct die_info *die,
                                          struct dwarf2_cu **);
 
+static die_info *follow_die_offset (const section_and_offset &target,
+                                   dwarf2_cu **ref_cu);
+
 static struct die_info *follow_die_ref_or_sig (struct die_info *,
                                               const struct attribute *,
                                               struct dwarf2_cu **);
@@ -12738,6 +12741,75 @@ namespace_name (struct die_info *die, int *is_anonymous, struct dwarf2_cu *cu)
   return name;
 }
 
+/* A convenience structure that holds a CU and a DIE.  */
+
+struct cu_die_pair
+{
+  die_info *die;
+  dwarf2_cu *cu;
+
+  /* Return true if this object holds a valid DIE.  */
+  bool valid () const
+  {
+    return die != nullptr;
+  }
+
+  /* Compare two cu_die_pair objects.  */
+  bool operator!= (const cu_die_pair &other) const
+  {
+    return die != other.die || cu != other.cu;
+  }
+};
+
+/* Follow the DW_AT_type reference, if any, from ORIG.  The return
+   value will be valid only when DW_AT_type exists.  In other cases,
+   including when the incoming ORIG is not valid, an invalid
+   cu_die_pair is returned.  */
+
+static cu_die_pair
+follow_type (cu_die_pair orig)
+{
+  /* This simplifies the caller a little.  */
+  if (!orig.valid ())
+    return {};
+
+  attribute *type_attr = dwarf2_attr (orig.die, DW_AT_type, orig.cu);
+  if (type_attr == nullptr || !type_attr->form_is_ref ())
+    return {};
+
+  cu_die_pair result = orig;
+  sect_offset sect_off = type_attr->get_ref_die_offset ();
+  const dwarf2_section_info &section = get_section_for_ref (*type_attr,
+                                                           orig.cu);
+  result.die = follow_die_offset ({ &section, sect_off }, &result.cu);
+
+  /* Note that we do not check for DW_TAG_pointer_type here.  GNAT, at
+     least, will emit recursive pointers that "indirect" via typedefs,
+     so checkpoint purely for pointer types would not find these.  */
+  return result;
+}
+
+/* Return true if DIE is a self-referential pointer type; false
+   otherwise.  CU is the origin of DIE.  */
+
+static bool
+is_recursive_pointer (die_info *die, dwarf2_cu *cu)
+{
+  cu_die_pair tortoise { die, cu };
+  gdb_assert (tortoise.valid ());
+  cu_die_pair hare = follow_type (tortoise);
+
+  while (tortoise != hare)
+    {
+      tortoise = follow_type (tortoise);
+      hare = follow_type (follow_type (hare));
+      if (!tortoise.valid () || !hare.valid ())
+       return false;
+    }
+
+  return true;
+}
+
 /* Extract all information from a DW_TAG_pointer_type DIE and add to
    the user defined type vector.  */
 
@@ -12752,7 +12824,17 @@ read_tag_pointer_type (struct die_info *die, struct dwarf2_cu *cu)
   int byte_size;
   struct type *target_type;
 
-  target_type = die_type (die, cu);
+  /* In Ada, it's possible to create a self-referential pointer type.
+     These aren't useful, but nevertheless we take care to avoid a gdb
+     crash in this situation.  Instead just turn these into a
+     pointer-to-void.  */
+  if (is_recursive_pointer (die, cu))
+    {
+      type_allocator alloc (cu->per_objfile->objfile, cu->lang ());
+      target_type = alloc.new_type (TYPE_CODE_VOID, 0, nullptr);
+    }
+  else
+    target_type = die_type (die, cu);
 
   /* The die_type call above may have already set the type for this DIE.  */
   type = get_die_type (die, cu);
diff --git a/gdb/testsuite/gdb.ada/recursive-access.exp b/gdb/testsuite/gdb.ada/recursive-access.exp
new file mode 100644 (file)
index 0000000..833be93
--- /dev/null
@@ -0,0 +1,31 @@
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test that recursive access types do not cause a crash.
+
+load_lib "ada.exp"
+
+require allow_ada_tests
+
+standard_ada_testfile prog
+
+if {[gdb_compile_ada "${srcfile}" "${binfile}" executable {debug}] != ""} {
+    return
+}
+
+clean_restart ${testfile}
+
+# The bug was that even reading these types caused a crash.
+gdb_test_no_output "maint expand-symtabs"
diff --git a/gdb/testsuite/gdb.ada/recursive-access/pack.ads b/gdb/testsuite/gdb.ada/recursive-access/pack.ads
new file mode 100644 (file)
index 0000000..fe06edc
--- /dev/null
@@ -0,0 +1,26 @@
+--  Copyright 2025 Free Software Foundation, Inc.
+--
+--  This program is free software; you can redistribute it and/or modify
+--  it under the terms of the GNU General Public License as published by
+--  the Free Software Foundation; either version 3 of the License, or
+--  (at your option) any later version.
+--
+--  This program is distributed in the hope that it will be useful,
+--  but WITHOUT ANY WARRANTY; without even the implied warranty of
+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--  GNU General Public License for more details.
+--
+--  You should have received a copy of the GNU General Public License
+--  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package Pack is
+   --  With GCC, this makes a directly self-referential pointer type.
+   type Direct;
+   subtype Sub is Direct;
+   type Direct is access Sub;
+
+   --  With GCC, this makes two mutually recursive pointer types.
+   type Second;
+   type First is access Second;
+   type Second is access First;
+end Pack;
diff --git a/gdb/testsuite/gdb.ada/recursive-access/prog.adb b/gdb/testsuite/gdb.ada/recursive-access/prog.adb
new file mode 100644 (file)
index 0000000..f740343
--- /dev/null
@@ -0,0 +1,23 @@
+--  Copyright 2025 Free Software Foundation, Inc.
+--
+--  This program is free software; you can redistribute it and/or modify
+--  it under the terms of the GNU General Public License as published by
+--  the Free Software Foundation; either version 3 of the License, or
+--  (at your option) any later version.
+--
+--  This program is distributed in the hope that it will be useful,
+--  but WITHOUT ANY WARRANTY; without even the implied warranty of
+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--  GNU General Public License for more details.
+--
+--  You should have received a copy of the GNU General Public License
+--  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+with Pack; use Pack;
+
+procedure Prog is
+   X : Direct := null;
+   Y : Second := new First;
+begin
+   null;
+end Prog;