[PATCH] gdb: Reject non-base type operands in typed DWARF expressions

Jielun Wu <[email protected]> Wed, 5 Aug 2026 15:59:33 +0800
Newsgroups gmane.comp.gdb.patches
Message-ID <[email protected]>
DWARF typed expression operators require a type operand that names a
DW_TAG_base_type DIE.  This applies to DW_OP_const_type,
DW_OP_regval_type, DW_OP_deref_type, and the nonzero type operands of
DW_OP_convert and DW_OP_reinterpret.

dwarf_expr_context::get_base_type documents that contract, but its
implementation accepted any DIE that resolved to a GDB type.  As a
result, typed expression operators could accept non-base DIEs such as
DW_TAG_structure_type.

Add dwarf2_get_base_type to resolve the referenced DIE, reject invalid or
non-base DIEs, and return the corresponding base type.  Use it from the
DWARF expression evaluator.

Add a DWARF assembler test covering DW_OP_const_type with a
DW_TAG_structure_type type operand.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34375
Signed-off-by: Jielun Wu <[email protected]>
---
 gdb/dwarf2/expr.c                             |  9 +-
 gdb/dwarf2/read.c                             | 38 ++++++++
 gdb/dwarf2/read.h                             |  8 ++
 .../gdb.dwarf2/dw2-typed-op-invalid-type.exp  | 89 +++++++++++++++++++
 4 files changed, 137 insertions(+), 7 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-typed-op-invalid-type.exp

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 3a6b8f58199..3fd7672bb2f 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -857,13 +857,8 @@ dwarf_expr_context::get_base_type (cu_offset die_cu_off)
   if (this->m_per_cu == nullptr)
     return builtin_type (this->m_per_objfile->objfile->arch ())->builtin_int;
 
-  struct type *result = dwarf2_get_die_type (die_cu_off, this->m_per_cu,
-					     this->m_per_objfile);
-
-  if (result == nullptr)
-    error (_("Could not find type for operation"));
-
-  return result;
+  return dwarf2_get_base_type (die_cu_off, this->m_per_cu,
+			       this->m_per_objfile);
 }
 
 /* See expr.h.  */
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index ca475f53745..3571f407a53 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -17146,6 +17146,44 @@ dwarf2_get_die_type (cu_offset die_offset, dwarf2_per_cu *per_cu,
   return get_die_type_at_offset (die_offset_sect, per_cu, per_objfile);
 }
 
+/* See read.h.  */
+
+struct type *
+dwarf2_get_base_type (cu_offset die_offset, dwarf2_per_cu *per_cu,
+		      dwarf2_per_objfile *per_objfile)
+{
+  dwarf2_cu *cu = per_objfile->get_cu (per_cu);
+  if (cu == nullptr)
+    cu = load_cu (per_cu, per_objfile, false);
+
+  /* A dummy CU has no DIE tree to inspect.  This helper needs a real CU
+     to validate the referenced DIE's tag.  */
+  gdb_assert (cu != nullptr);
+
+  sect_offset sect_off = cu->section_offset () + to_underlying (die_offset);
+  if (!cu->header.offset_in_unit_p (sect_off))
+    error (_(DWARF_ERROR_PREFIX
+	     "DIE at %s referenced by typed DWARF expression operation is "
+	     "outside the current CU in module %s"),
+	   sect_offset_str (sect_off), objfile_name (per_objfile->objfile));
+
+  die_info *die = follow_die_offset ({ &cu->section (), sect_off }, &cu);
+  if (die == nullptr)
+    error (_(DWARF_ERROR_PREFIX
+	     "Cannot find DIE at %s referenced by typed DWARF expression "
+	     "operation in module %s"),
+	   sect_offset_str (sect_off), objfile_name (per_objfile->objfile));
+
+  if (die->tag != DW_TAG_base_type)
+    error (_(DWARF_ERROR_PREFIX
+	     "DIE at %s referenced by typed DWARF expression operation has "
+	     "tag '%s', not DW_TAG_base_type in module %s"),
+	   sect_offset_str (sect_off), dwarf_tag_name (die->tag),
+	   objfile_name (per_objfile->objfile));
+
+  return read_type_die (die, cu);
+}
+
 /* Fill in the missing details in SIG_TYPE from DWO_FILE.
 
    Error out if there isn't a type unit with the appropriate signature in
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index 15dd2abf3a1..ea43ada9d2f 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -1202,6 +1202,14 @@ dwarf2_per_objfile *get_dwarf2_per_objfile (struct objfile *objfile);
 struct type *dwarf2_get_die_type (cu_offset die_offset, dwarf2_per_cu *per_cu,
 				  dwarf2_per_objfile *per_objfile);
 
+/* Return the type of the DW_TAG_base_type DIE at DIE_OFFSET in the CU
+   named by PER_CU.  Throw an exception if the DIE is invalid or does
+   not represent a base type.  */
+
+struct type *dwarf2_get_base_type (cu_offset die_offset,
+				   dwarf2_per_cu *per_cu,
+				   dwarf2_per_objfile *per_objfile);
+
 /* Given an index in .debug_addr, fetch the value.
    NOTE: This can be called during dwarf expression evaluation,
    long after the debug information has been read, and thus per_cu->cu
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-typed-op-invalid-type.exp b/gdb/testsuite/gdb.dwarf2/dw2-typed-op-invalid-type.exp
new file mode 100644
index 00000000000..cc147b98eca
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dw2-typed-op-invalid-type.exp
@@ -0,0 +1,89 @@
+# 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 that typed DWARF expression operations reject type operands that
+# do not reference a DW_TAG_base_type DIE.
+
+load_lib dwarf.exp
+
+require dwarf2_support
+
+standard_testfile main.c -dw.S
+
+set asm_file [standard_output_file $srcfile2]
+
+Dwarf::assemble $asm_file {
+    cu {} {
+	compile_unit {} {
+	    declare_labels int_label struct_label
+
+	    int_label: base_type {
+		DW_AT_name "int"
+		DW_AT_encoding @DW_ATE_signed
+		DW_AT_byte_size 4 DW_FORM_sdata
+	    }
+
+	    struct_label: structure_type {
+		DW_AT_name "not_a_base_type"
+		DW_AT_byte_size 4 DW_FORM_sdata
+	    } {
+		member {
+		    DW_AT_name "field"
+		    DW_AT_type :$int_label
+		    DW_AT_data_member_location 0 DW_FORM_sdata
+		}
+	    }
+
+	    DW_TAG_variable {
+		DW_AT_name "bad_struct_const"
+		DW_AT_type :$struct_label
+		DW_AT_external 1 DW_FORM_flag
+		DW_AT_location {
+		    variable _constants
+		    variable _cu_label
+
+		    _op .byte $_constants(DW_OP_const_type)
+		    _op .uleb128 "$struct_label - $_cu_label"
+		    _op .byte 4
+		    _op .byte 1
+		    _op .byte 2
+		    _op .byte 3
+		    _op .byte 4
+		    DW_OP_stack_value
+		} SPECIAL_expr
+	    }
+	}
+    }
+}
+
+if {[build_executable ${testfile}.exp ${testfile} \
+	 [list $srcfile $asm_file] {nodebug}]} {
+    return
+}
+
+clean_restart ${testfile}
+
+if {![runto_main]} {
+    return
+}
+
+set base_type_error \
+    "DWARF Error: DIE at $hex referenced by typed DWARF "
+append base_type_error \
+    "expression operation has tag 'DW_TAG_structure_type', not "
+append base_type_error \
+    "DW_TAG_base_type .*"
+
+gdb_test "print bad_struct_const" $base_type_error
-- 
2.34.1