From: Tom de Vries Date: Tue, 24 Sep 2024 08:50:44 +0000 (+0200) Subject: [gdb/symtab] Fix segfault on invalid debug info X-Git-Tag: gdb-16-branchpoint~838 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6848938272157eb6532c189d6fcebec9d2dc33e8;p=thirdparty%2Fbinutils-gdb.git [gdb/symtab] Fix segfault on invalid debug info While looking at PR symtab/31478 (a problem in the cooked indexer with invalid dwarf) it occurred to me that I could trigger a similar problem using: ... Compilation Unit @ offset 0xb2: Length: 0x1f (32-bit) Version: 4 Abbrev Offset: 0x6c Pointer Size: 8 <0>: Abbrev Number: 1 (DW_TAG_compile_unit) DW_AT_language : 2 (non-ANSI C) <1>: Abbrev Number: 2 (DW_TAG_subprogram) DW_AT_low_pc : 0x4004a7 DW_AT_high_pc : 0x4004b2 DW_AT_specification: <0xd5> <1>: Abbrev Number: 0 Compilation Unit @ offset 0xd5: Length: 0x7 (32-bit) Version: 4 Abbrev Offset: 0x7f Pointer Size: 8 ... and indeed I get: ... $ gdb -q -batch outputs/gdb.dwarf2/dw2-inter-cu-error-2/dw2-inter-cu-error-2 Fatal signal: Segmentation fault ... The problem is that we're calling prepare_one_comp_unit with cu == nullptr and comp_unit_die == nullptr here in cooked_indexer::ensure_cu_exists: ... cutu_reader new_reader (per_cu, per_objfile, nullptr, nullptr, false, m_index_storage->get_abbrev_cache ()); prepare_one_comp_unit (new_reader.cu, new_reader.comp_unit_die, language_minimal); ... Fix this by bailing out for various types of dummy CUs: ... if (new_reader.dummy_p || new_reader.comp_unit_die == nullptr || !new_reader.comp_unit_die->has_children) return nullptr; ... Also make sure in scan_attributes that this triggers a dwarf error: ... $ gdb -q -batch dw2-inter-cu-error-2 DWARF Error: cannot follow reference to DIE at 0xd5 \ [in module dw2-inter-cu-error-2] ... With target board readnow, the test-case triggers an assertion failure in follow_die_offset, so fix this by throwing the same dwarf error. While we're at it, make the other check for dummy CUs in cooked_indexer::ensure_cu_exists more robust by adding an intermediate test for comp_unit_die: ... - if (result->dummy_p || !result->comp_unit_die->has_children) + if (result->dummy_p || result->comp_unit_die == nullptr + || !result->comp_unit_die->has_children) return nullptr; ... Tested on x86_64-linux. Approved-By: Tom Tromey --- diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index ca27391a6f5..193a52ef6e0 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -16078,6 +16078,10 @@ cooked_indexer::ensure_cu_exists (cutu_reader *reader, cutu_reader new_reader (per_cu, per_objfile, nullptr, nullptr, false, m_index_storage->get_abbrev_cache ()); + if (new_reader.dummy_p || new_reader.comp_unit_die == nullptr + || !new_reader.comp_unit_die->has_children) + return nullptr; + prepare_one_comp_unit (new_reader.cu, new_reader.comp_unit_die, language_minimal); std::unique_ptr copy @@ -16085,7 +16089,8 @@ cooked_indexer::ensure_cu_exists (cutu_reader *reader, result = m_index_storage->preserve (std::move (copy)); } - if (result->dummy_p || !result->comp_unit_die->has_children) + if (result->dummy_p || result->comp_unit_die == nullptr + || !result->comp_unit_die->has_children) return nullptr; if (for_scanning) @@ -16271,49 +16276,53 @@ cooked_indexer::scan_attributes (dwarf2_per_cu_data *scanning_per_cu, cutu_reader *new_reader = ensure_cu_exists (reader, reader->cu->per_objfile, origin_offset, origin_is_dwz, false); - if (new_reader != nullptr) - { - const gdb_byte *new_info_ptr = (new_reader->buffer - + to_underlying (origin_offset)); + if (new_reader == nullptr) + error (_(DWARF_ERROR_PREFIX + "cannot follow reference to DIE at %s" + " [in module %s]"), + sect_offset_str (origin_offset), + bfd_get_filename (reader->abfd)); - if (*parent_entry == nullptr) - { - /* We only perform immediate lookups of parents for DIEs - from earlier in this CU. This avoids any problem - with a NULL result when when we see a reference to a - DIE in another CU that we may or may not have - imported locally. */ - parent_map::addr_type addr - = parent_map::form_addr (origin_offset, origin_is_dwz); - if (new_reader->cu != reader->cu || new_info_ptr > watermark_ptr) - *maybe_defer = addr; - else - *parent_entry = m_die_range_map->find (addr); - } + const gdb_byte *new_info_ptr = (new_reader->buffer + + to_underlying (origin_offset)); - unsigned int bytes_read; - const abbrev_info *new_abbrev = peek_die_abbrev (*new_reader, - new_info_ptr, - &bytes_read); + if (*parent_entry == nullptr) + { + /* We only perform immediate lookups of parents for DIEs + from earlier in this CU. This avoids any problem + with a NULL result when when we see a reference to a + DIE in another CU that we may or may not have + imported locally. */ + parent_map::addr_type addr + = parent_map::form_addr (origin_offset, origin_is_dwz); + if (new_reader->cu != reader->cu || new_info_ptr > watermark_ptr) + *maybe_defer = addr; + else + *parent_entry = m_die_range_map->find (addr); + } - if (new_abbrev == nullptr) - error (_(DWARF_ERROR_PREFIX - "Unexpected null DIE at offset %s [in module %s]"), - sect_offset_str (origin_offset), - bfd_get_filename (new_reader->abfd)); + unsigned int bytes_read; + const abbrev_info *new_abbrev = peek_die_abbrev (*new_reader, + new_info_ptr, + &bytes_read); - new_info_ptr += bytes_read; + if (new_abbrev == nullptr) + error (_(DWARF_ERROR_PREFIX + "Unexpected null DIE at offset %s [in module %s]"), + sect_offset_str (origin_offset), + bfd_get_filename (new_reader->abfd)); - if (new_reader->cu == reader->cu && new_info_ptr == watermark_ptr) - { - /* Self-reference, we're done. */ - } - else - scan_attributes (scanning_per_cu, new_reader, new_info_ptr, - new_info_ptr, new_abbrev, name, linkage_name, - flags, nullptr, parent_entry, maybe_defer, - is_enum_class, true); + new_info_ptr += bytes_read; + + if (new_reader->cu == reader->cu && new_info_ptr == watermark_ptr) + { + /* Self-reference, we're done. */ } + else + scan_attributes (scanning_per_cu, new_reader, new_info_ptr, + new_info_ptr, new_abbrev, name, linkage_name, + flags, nullptr, parent_entry, maybe_defer, + is_enum_class, true); } if (!for_specification) @@ -20415,7 +20424,12 @@ follow_die_offset (sect_offset sect_off, int offset_in_dwz, false, cu->lang ()); target_cu = per_objfile->get_cu (per_cu); - gdb_assert (target_cu != nullptr); + if (target_cu == nullptr) + error (_(DWARF_ERROR_PREFIX + "cannot follow reference to DIE at %s" + " [in module %s]"), + sect_offset_str (sect_off), + objfile_name (per_objfile->objfile)); } else if (cu->dies == NULL) { diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inter-cu-error-2.exp b/gdb/testsuite/gdb.dwarf2/dw2-inter-cu-error-2.exp new file mode 100644 index 00000000000..585fd54ed15 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-inter-cu-error-2.exp @@ -0,0 +1,51 @@ +# Copyright 2024 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 . + +# Check that GDB doesn't crash on invalid dwarf, specifically an inter-CU +# reference pointing to a dummy CU. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +standard_testfile main.c .S + +# Create the DWARF. +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + declare_labels label1 + + cu {} { + compile_unit {{language @DW_LANG_C}} { + subprogram { + {MACRO_AT_range { main }} + {DW_AT_specification %$label1} + } + } + } + + label1: cu {} { + } +} + +if [prepare_for_testing "failed to prepare" $testfile \ + [list $asm_file $srcfile] {nodebug}] { + return -1 +} + +gdb_assert \ + { [regexp "DWARF Error: cannot follow reference" $gdb_file_cmd_msg] } \ + "Error message"