[PATCH 1/2] gdb: fix crash when pretty printing anonymous struct with base class

Simon Marchi <[email protected]>
Newsgroups gmane.comp.gdb.patches
Message-ID <[email protected]>
As I was reviewing another patch, I was wondering if we allowed
type->name() to be nullptr.  The answer is yes, but there are some spots
that don't check for nullptr, when they should.  Claude came up with a
reproducer that inspired the included test case.  It is arguably a
corner case, but it makes GDB crash.

The crash happens when trying to pretty-print (with "set print pretty
on") a value of an anonymous struct type that has a base.  Given this:

    struct base
    {
      int a;
    };

    struct : base
    {
      int b;
    } v4 = { { 1 }, 2 };

We get:

    $ ./gdb -nx -q --data-directory=data-directory testsuite/outputs/gdb.cp/anon-struct/anon-struct -ex "with print pretty -- p v4"
    Reading symbols from testsuite/outputs/gdb.cp/anon-struct/anon-struct...
    $1 = {
      <base> = {
        a = 1
      },
    /home/smarchi/src/binutils-gdb/gdb/ui-file.h:77:30: runtime error: null pointer passed as argument 1, which is declared to never be null

The crash happens here:

    #4  0x00007ffff600dec8 in __ubsan_handle_nonnull_arg_abort () from /usr/lib/libubsan.so.1
    #5  0x00005555637daa35 in ui_file::puts (this=0x7c1ff1c022d0, str=0x0) at /home/smarchi/src/binutils-gdb/gdb/ui-file.h:77
    #6  0x0000555566f2b0ab in gdb_puts (linebuffer=0x0, stream=0x7c1ff1c022d0) at /home/smarchi/src/binutils-gdb/gdb/utils.c:1912
    #7  0x00005555644e5cd4 in cp_print_value_fields (val=0x7d0ff1c2aa00, stream=0x7c1ff1c022d0, recurse=0, options=0x7bfff094acb0, dont_print_vb=0x0, dont_print_statmem=0) at /home/smarchi/src/binutils-gdb/gdb/cp-valprint.c:213
    #8  0x00005555640de708 in c_value_print_struct (val=0x7d0ff1c2aa00, stream=0x7c1ff1c022d0, recurse=0, options=0x7bfff094acb0) at /home/smarchi/src/binutils-gdb/gdb/c-valprint.c:385
    #9  0x00005555640df215 in c_value_print_inner (val=0x7d0ff1c2aa00, stream=0x7c1ff1c022d0, recurse=0, options=0x7bfff094acb0) at /home/smarchi/src/binutils-gdb/gdb/c-valprint.c:441
    #10 0x000055556563828c in language_defn::value_print_inner (this=0x5555734a0700 <cplus_language_defn>, val=0x7d0ff1c2aa00, stream=0x7c1ff1c022d0, recurse=0, options=0x7bfff094acb0) at /home/smarchi/src/binutils-gdb/gdb/language.c:658
    #11 0x0000555566fb7749 in common_val_print (value=0x7d0ff1c2aa00, stream=0x7c1ff1c022d0, recurse=0, options=0x7bfff094aa70, language=0x5555734a0700 <cplus_language_defn>) at /home/smarchi/src/binutils-gdb/gdb/valprint.c:1120

when we try to print the nullptr type name:

    if (options->prettyformat)
      {
	gdb_printf (stream, "\n");
	print_spaces (2 + 2 * recurse, stream);
	gdb_puts ("members of ", stream);
	gdb_puts (type->name (), stream);
	gdb_puts (":", stream);
      }

Fix it by using type->safe_name() instead of type->name().  This results
in:

    $1 = {
      <base> = {
        a = 1
      },
      members of <unnamed type>:
      b = 2
    }

Claude pointed out that p-valprint.c has more or less the same code, so
I changed it there too, but I did not write a test for that one.

Change-Id: Ibc4541bf04b7239e31b8401aa86466a6807e89d5
---
 gdb/cp-valprint.c                             |  2 +-
 gdb/p-valprint.c                              |  2 +-
 gdb/testsuite/gdb.cp/anon-struct-with-base.cc | 49 ++++++++++++++
 .../gdb.cp/anon-struct-with-base.exp          | 65 +++++++++++++++++++
 4 files changed, 116 insertions(+), 2 deletions(-)
 create mode 100644 gdb/testsuite/gdb.cp/anon-struct-with-base.cc
 create mode 100644 gdb/testsuite/gdb.cp/anon-struct-with-base.exp

diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c
index 349a0d6bf9e9..d450e90e1e1d 100644
--- a/gdb/cp-valprint.c
+++ b/gdb/cp-valprint.c
@@ -210,7 +210,7 @@ cp_print_value_fields (struct value *val, struct ui_file *stream,
 		  gdb_printf (stream, "\n");
 		  print_spaces (2 + 2 * recurse, stream);
 		  gdb_puts ("members of ", stream);
-		  gdb_puts (type->name (), stream);
+		  gdb_puts (type->safe_name (), stream);
 		  gdb_puts (":", stream);
 		}
 	    }
diff --git a/gdb/p-valprint.c b/gdb/p-valprint.c
index f64c1069b531..2f913a3da150 100644
--- a/gdb/p-valprint.c
+++ b/gdb/p-valprint.c
@@ -564,7 +564,7 @@ pascal_object_print_value_fields (struct value *val, struct ui_file *stream,
 		  gdb_printf (stream, "\n");
 		  print_spaces (2 + 2 * recurse, stream);
 		  gdb_puts ("members of ", stream);
-		  gdb_puts (type->name (), stream);
+		  gdb_puts (type->safe_name (), stream);
 		  gdb_puts (": ", stream);
 		}
 	    }
diff --git a/gdb/testsuite/gdb.cp/anon-struct-with-base.cc b/gdb/testsuite/gdb.cp/anon-struct-with-base.cc
new file mode 100644
index 000000000000..474473abb666
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/anon-struct-with-base.cc
@@ -0,0 +1,49 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2026 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/>.  */
+
+struct base_data
+{
+  int a;
+};
+
+struct base_no_data
+{
+};
+
+struct : base_data
+{
+  int b;
+} v_data_base_data = { { 1 }, 2 };
+
+struct : base_data
+{
+} v_no_data_base_data = { { 3 } };
+
+struct : base_no_data
+{
+  int c;
+} v_data_base_no_data = { {}, 4 };
+
+struct : base_no_data
+{
+} v_no_data_base_no_data = {};
+
+int
+main ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.cp/anon-struct-with-base.exp b/gdb/testsuite/gdb.cp/anon-struct-with-base.exp
new file mode 100644
index 000000000000..2ff7036198a2
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/anon-struct-with-base.exp
@@ -0,0 +1,65 @@
+# Copyright 2026 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 printing a value whose type is an unnamed struct with a base class.
+
+standard_testfile .cc
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile \
+	 {debug c++}]} {
+    return
+}
+
+gdb_test "with print pretty off -- print v_data_base_data" \
+    "$::valnum_re = \{<base_data> = \{a = 1\}, b = 2\}"
+
+gdb_test "with print pretty off -- print v_no_data_base_data" \
+    "$::valnum_re = \{<base_data> = \{a = 3\}, <No data fields>\}"
+
+gdb_test "with print pretty off -- print v_data_base_no_data" \
+    "$::valnum_re = \{<base_no_data> = \{<No data fields>\}, c = 4\}"
+
+gdb_test "with print pretty off -- print v_no_data_base_no_data" \
+    "$::valnum_re = \{<base_no_data> = \{<No data fields>\}, <No data fields>\}"
+
+gdb_test "with print pretty on -- print v_data_base_data" \
+    [multi_line \
+	 "$::valnum_re = \{" \
+	 "  <base_data> = \{" \
+	 "    a = 1" \
+	 "  \}, " \
+	 "  members of <unnamed type>:" \
+	 "  b = 2" \
+	 "\}"]
+
+gdb_test "with print pretty on -- print v_no_data_base_data" \
+    [multi_line \
+	 "$::valnum_re = \{" \
+	 "  <base_data> = \{" \
+	 "    a = 3" \
+	 "  \}, <No data fields>\}"]
+
+gdb_test "with print pretty on -- print v_data_base_no_data" \
+    [multi_line \
+	 "$::valnum_re = \{" \
+	 "  <base_no_data> = \{<No data fields>\}, " \
+	 "  members of <unnamed type>:" \
+	 "  c = 4" \
+	 "\}"]
+
+gdb_test "with print pretty on -- print v_no_data_base_no_data" \
+    [multi_line \
+	 "$::valnum_re = \{" \
+	 "  <base_no_data> = \{<No data fields>\}, <No data fields>\}"]

base-commit: 0f9faaebc91bc1886a563bde6c178601b4be743b
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.