[binutils-gdb] gdb: fix crash when pretty printing anonymous struct with base class

Simon Marchi via Gdb-cvs <[email protected]>
Newsgroups gmane.comp.gdb.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=42cac857bbd7e63776c025af7267ad78db187f8c

commit 42cac857bbd7e63776c025af7267ad78db187f8c
Author: Simon Marchi <[email protected]>
Date:   Mon Aug 17 10:59:01 2026 -0400

    gdb: fix crash when pretty printing anonymous struct with base class
    
    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
    Approved-By: Kevin Buettner <[email protected]>

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

diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c
index 349a0d6bf9e..d450e90e1e1 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 f64c1069b53..2f913a3da15 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 00000000000..474473abb66
--- /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 00000000000..2ff7036198a
--- /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>\}"]
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.