From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id +X19FkUx1WoDYCwAWB0awg (envelope-from ) for ; Sun, 18 Oct 2026 16:51:17 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=a5j4M1Wh; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 58ACC1E050; Sun, 18 Oct 2026 16:51:17 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 752661E04E for ; Sun, 18 Oct 2026 16:51:16 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id EA1B14BA2E14 for ; Sun, 18 Oct 2026 20:51:15 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org EA1B14BA2E14 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=a5j4M1Wh Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id BB1684BA23D6 for ; Sun, 18 Oct 2026 20:50:48 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org BB1684BA23D6 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org BB1684BA23D6 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1792356648; cv=none; b=jtBpjdD5lkLiuGwjpaZ4AnEZZUyQhKhsd35d7uTC2OZ1KvKbAlgUUv2YvwuF9i0jBCdND2PZKfFfyxSbgP1Hsxk7P4y6NBpJY7CFmzrzvhLZIo7CcC4BVeROyCeD0Ihpx8jBfSUhjBn8eGaa3DRQPLATLkxiK5CaFg2b40r+HlI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1792356648; c=relaxed/simple; bh=NZLJ/vqhIn/ln6a4zLw9ebSC90NRsHqsFzkgzLJsZBg=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=Y1OX9u+a23ddWvuCOPfEDZ6prxRF1PEdW5cnllOlgObKFFZ4oBlxGreYlTGNlflflI4X4x+v0kNIvBW6WBssP5Jq/zJRYpMPAOfGgf9h1xLmU6QPFkDeEEZ9wasjniRoAYBUvNrEov1YdZBy+jdFH/Mk/Gcx14k6utCGHgQb3EY= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=a5j4M1Wh DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BB1684BA23D6 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1792356648; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=EpwtBxzh7Cz9aonCDm5YqIN89n41ne2JpVlff2cWwJI=; b=a5j4M1WhKlctcCKlWhf1disq/E8IuH6sxpvQUxK/TwfuwLNBnpFeyLPEMy9F9LQqwaF2et uaek3GUYtF7P9z53q9kdd9vbq4DoAlhfzW7O1WbI1nAGo0J7uK97lWXNuxy8wBwuQR3xBU 9JVtasPIZQx91o00t0PDgwBeOIxZCwk= Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-648-m1wlvVhKPE6gV7dxLX07OA-1; Sun, 18 Oct 2026 20:50:46 +0000 X-MC-Unique: m1wlvVhKPE6gV7dxLX07OA-1 X-Mimecast-MFC-AGG-ID: m1wlvVhKPE6gV7dxLX07OA_1792356645 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-48af4a0bd0fso673486f8f.2 for ; Sun, 18 Oct 2026 13:50:46 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792356645; x=1792961445; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=EpwtBxzh7Cz9aonCDm5YqIN89n41ne2JpVlff2cWwJI=; b=iPVLf24OPDXJQuAucZ7yUIYQkd+DHyVTm99hsDYdXszYeRoIdy8s9iGoBJeZNX5NPJ 5ZdP9aRZnfrbjQAa4KE3IHXRvBFW0frYo5kIqRBfTXYmGq8p5yCtJybVstz+nija+Hxn qyH3tZFQHAqVoIVk60o22B86Nkjt+/V97ntYUudmdxGm33pEntWNilUgL+1EW84Hgh0K H9hNumApyk40542EWFDTaridTXXZFfgDfxEbRW96SHzXNyCJHhkz5a1AWl/XJgs5SDiC K2gfM9VPFz9ChEjlVgThCiR6Y7HIqGQGRB3N9RPvj8FdqfyrZ24/b5h99ddHx1ii8sVz +EOA== X-Gm-Message-State: AFq9FYLGS9iM/6ZXZItnx/vRvtcwfvYJKqkiXA4h2nwzBwHDtW/r3Awb /n/oHy7YIJd+RQcnZfR90vYatQTht06JoJCVsHw3bG4k4f0n3l/KDSQGEhEUoGQEWCZahuhJaPV rdQQdOdOfLYstVRoVLJler49lQ92wSIs4Q3MiMIeEXZX5FcO/MCNFJFzlH/ef8AK9M3BFCoGPf9 WvMyvLDXAYIvuPLwyugH398g9h/ziIs9XB8QB7woJ3GdrfAec= X-Gm-Gg: AYBFou25FtOwmnXiipk/bbzuGgPrTlceYhohtbhVI+3GPOn9U2s/TSWE4Sv3ixya43Q ZMmh43LKiZkvPLlJh976iOj11iwGy5IQt+fHd8uzat3JQ57m1UMueuPeyITXvDbskm3VMLEWhrB 9D0q4NuaBkiHMuxKJlv7uWeoEGAnt19cMl7LOG4Y4i12GNItJdEYca53mg2EmgxSRtkPbssfEc5 f6v2yR/vmj59Koty9cZVHMH5tKcqHp+LLztid5vCGOS+mzL/YzxYhuMAdRXE9ePk1n1yI1a9zeV kPGtsr/XRjW3aLb6lT9E9LTYen2p4qgH5ihw7/tcT5h+qDU3GxgqLB2b/3bntuG2l86D X-Received: by 2002:a05:6000:2409:b0:48d:fd4b:cdd6 with SMTP id ffacd0b85a97d-48e07f9852cmr11175809f8f.25.1792356645117; Sun, 18 Oct 2026 13:50:45 -0700 (PDT) X-Received: by 2002:a05:6000:2409:b0:48d:fd4b:cdd6 with SMTP id ffacd0b85a97d-48e07f9852cmr11175783f8f.25.1792356644545; Sun, 18 Oct 2026 13:50:44 -0700 (PDT) Received: from localhost ([213.31.44.29]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-48dfd5ad06bsm19410226f8f.14.2026.10.18.13.50.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 18 Oct 2026 13:50:43 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 3/6] gdb: validate the CU vector offset when loading the .gdb_index Date: Sun, 18 Oct 2026 21:50:32 +0100 Message-Id: <17ad2d9bf083c17ea90f2e8ba417b2d0231f66fa.1792356284.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 3zKHhrYUpv1x_30FLM7yodX4wwnKFK8btTRX-gyPJBQ_1792356645 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org Within the .gdb_index, the symbol table contains indexes that point to CU vectors within the constant pool. Unfortunately, these indexes are not validated against the size of the constant pool, so a malformed .gdb_index can cause GDB to read outside the bounds of the .gdb_index. This commit extends the dwarf.exp gdb_index proc to allow for the CU offset to be overridden, this is then used to add tests that expose this bug. Finally, mapped_gdb_index::build_name_components is updated to sanity check the CU offset before it is used. If we find an invalid offset then that symbol will be ignored. To improve GDB's ability to catch errors I also added an assert to offset_view::operator[] that the whole offset_type is within the view. Without the build_name_components fix this assert will be triggered by one of the new tests. --- gdb/dwarf2/read-gdb-index.c | 22 +++++++- .../gdb.dwarf2/gdb-index-corrupted.exp | 54 +++++++++++++++++++ gdb/testsuite/lib/dwarf.exp | 47 ++++++++++++---- 3 files changed, 111 insertions(+), 12 deletions(-) diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c index e0895fb6aa8..343ad90847f 100644 --- a/gdb/dwarf2/read-gdb-index.c +++ b/gdb/dwarf2/read-gdb-index.c @@ -102,6 +102,11 @@ class offset_view /* Extract the INDEXth offset_type from the array. */ offset_type operator[] (size_t index) const { + /* Ensure the whole offset_type is within the underlying bytes. If GDB + is compiled with libc++ assert then the access of M_BYTES will be + checked, but this only guarantees that the first byte is within the + array_view as it's a gdb_byte view. */ + gdb_assert (index < size ()); const gdb_byte *bytes = &m_bytes[index * sizeof (offset_type)]; return (offset_type) extract_unsigned_integer (bytes, sizeof (offset_type), @@ -273,7 +278,22 @@ mapped_gdb_index::build_name_components (dwarf2_per_objfile *per_objfile) gdb_assert (components.size () > 0); std::vector these_entries; - offset_view vec (constant_pool.slice (symbol_vec_index (idx))); + + offset_type vec_index = symbol_vec_index (idx); + /* The CU vector starts with an offset_type length word. Check that + VEC_INDEX lands inside CONSTANT_POOL, and that there is at least an + offset_type worth of bytes beyond this in CONSTANT_POOL to be + read. */ + if (vec_index >= constant_pool.size () + || constant_pool.size () - vec_index < sizeof (offset_type)) + { + complaint (_(".gdb_index entry %s has bad CU vector offset %s " + "[in module %s]"), + pulongest (idx), pulongest (vec_index), + objfile_name (per_objfile->objfile)); + continue; + } + offset_view vec (constant_pool.slice (vec_index)); offset_type vec_len = vec[0]; /* Bounds check VEC_LEN against the CONSTANT_POOL. */ diff --git a/gdb/testsuite/gdb.dwarf2/gdb-index-corrupted.exp b/gdb/testsuite/gdb.dwarf2/gdb-index-corrupted.exp index 356b12f97ce..33c00874002 100644 --- a/gdb/testsuite/gdb.dwarf2/gdb-index-corrupted.exp +++ b/gdb/testsuite/gdb.dwarf2/gdb-index-corrupted.exp @@ -173,6 +173,50 @@ proc run_test { testname } { name _start {{cu_label function global}} name foo {{cu_label function global}} 0xffffff00 } + } elseif { $testname eq "cu-vector-offset-1" } { + # Add a symbol whose CU vector offset in the symbol table is out + # of range. The slot has a valid name offset, so GDB won't + # treat it as an empty slot and will try to read the CU vector. + # Ensure GDB doesn't crash and instead just ignores this one + # symbol. + # + # The 'end' offset is exactly the size of the constant pool, + # that is, it points at the first byte after the pool. This is + # the off-by-one boundary case. + gdb_index {} { + cu cu_label + name _start {{cu_label function global}} + name foo {{cu_label function global}} "" end + } + } elseif { $testname eq "cu-vector-offset-2" } { + # Just like 'cu-vector-offset-1', but here the CU vector offset + # is huge, far past the end of the constant pool. This is far + # more likely to cause GDB to crash. + gdb_index {} { + cu cu_label + name _start {{cu_label function global}} + name foo {{cu_label function global}} "" 0xffffff00 + } + } elseif { $testname eq "cu-vector-offset-3" } { + # Like cu-vector-offset-1/-2, but aim the CU vector offset at + # the last byte of the constant pool. That byte offset is in + # range, but there is no room there for the 4-byte CU vector + # length word that GDB reads. This ensures that GDB is + # correctly validating the entire length of its reads. + # + # Work out the constant pool size from its contents. There are + # two symbols that need adding to the constant pool, '_start' + # and 'foo'. For each symbol we have a CU vector (a 4-byte + # count and a 4-byte offset), then we have both symbol strings + # plus their null terminating character. + set pool_size \ + [expr {2 * (4 + 4) + [string length "_start"] + 1 \ + + [string length "foo"] + 1}] + gdb_index {} { + cu cu_label + name _start {{cu_label function global}} + name foo {{cu_label function global}} "" [expr {$pool_size - 1}] + } } elseif { $testname eq "bad-cu-length" } { # Create an otherwise valid index, but record a bogus length # for the CU in the index's CU list; one that doesn't match the @@ -247,6 +291,9 @@ proc run_test { testname } { || $testname eq "long-cu-vector" \ || $testname eq "name-offset-1" \ || $testname eq "name-offset-2" \ + || $testname eq "cu-vector-offset-1" \ + || $testname eq "cu-vector-offset-2" \ + || $testname eq "cu-vector-offset-3" \ || $testname eq "bad-cu-length" } { set expect_gdb_index_ignored false set expect_start true @@ -262,6 +309,10 @@ proc run_test { testname } { } elseif { $testname eq "name-offset-1" \ || $testname eq "name-offset-2" } { set expect_warning_re "\r\nDuring symbol reading: \\.gdb_index entry $::decimal has bad name offset $::decimal \\\[in module \[^\r\n\]+\\\]\r\n" + } elseif { $testname eq "cu-vector-offset-1" \ + || $testname eq "cu-vector-offset-2" \ + || $testname eq "cu-vector-offset-3" } { + set expect_warning_re "\r\nDuring symbol reading: \\.gdb_index entry $::decimal has bad CU vector offset $::decimal \\\[in module \[^\r\n\]+\\\]\r\n" } else { set expect_warning_re "" } @@ -371,6 +422,9 @@ set tests { empty-component \ long-cu-vector \ name-offset-1 \ name-offset-2 \ + cu-vector-offset-1 \ + cu-vector-offset-2 \ + cu-vector-offset-3 \ bad-cu-length \ valid-index } diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp index 7a82f3915d3..b59e598a163 100644 --- a/gdb/testsuite/lib/dwarf.exp +++ b/gdb/testsuite/lib/dwarf.exp @@ -3835,7 +3835,7 @@ namespace eval Dwarf { # .debug_info; providing a (bogus) override allows GDB's # handling of an inconsistent CU length to be tested. # - # name [] + # name [ []] # -- add symbol with its CU vector to the constant pool: # # The is a list of { } tuples @@ -3862,7 +3862,17 @@ namespace eval Dwarf { # encoded for testing GDB's error handling. The value can be a # raw number, or the keyword 'end' which resolves to the exact # size of the constant pool (i.e. one byte past its end). The - # string is still emitted into the constant pool. + # string is still emitted into the constant pool. Pass + # an empty string to keep the automatically computed offset. + # + # Similarly, the optional overrides the CU vector + # offset written into this symbol's slot in the symbol table. + # Normally this offset is computed to point at this symbol's CU + # vector in the constant pool but an override allows an out of + # range offset to be encoded. The value can be a raw number or + # the keyword 'end' which has the same meaning as for + # above. The CU vector itself is still emitted + # into the constant pool. # # main # -- add symbol of to the index as the entry point @@ -3923,10 +3933,9 @@ namespace eval Dwarf { set _gdb_index_cus [] # A list of lists. Each sub-list has the form: {name entries - # offset}, where is a string, entries is a list of CU - # descriptors, and is usually the empty string, but can be - # a number which overrides the offset from the CU vector to the - # string. See _gdb_index_name for more details. + # name_offset cuvec_offset}. See the 'name' command (proc + # _gdb_index_name) in the gdb_index documentation for details of + # what each field means. variable _gdb_index_names set _gdb_index_names [] @@ -3969,9 +3978,11 @@ namespace eval Dwarf { # offset, or it can be the string 'end', which resolves to the exact # size of the constant pool (i.e. one byte past its end). This is # useful for testing for off-by-one errors in GDB. - proc _gdb_index_name { name entries_list {name_offset ""} } { + proc _gdb_index_name { name entries_list {name_offset ""} \ + {cuvec_offset ""} } { variable _gdb_index_names - lappend _gdb_index_names [list $name $entries_list $name_offset] + lappend _gdb_index_names \ + [list $name $entries_list $name_offset $cuvec_offset] } # Setup the name and language of the main function, as recorded in @@ -4184,9 +4195,23 @@ namespace eval Dwarf { } else { _op .4byte "$name_offset" "name offset" } - _op .4byte \ - "$vec_label - $gdb_index_constant_pool" \ - "CU vector offset" + + # The CU vector offset normally points at this symbol's CU + # vector in the constant pool, but it too can be overridden + # (e.g. to an out of range value) for testing. See the + # 'name' command in the gdb_index documentation for details. + set cuvec_offset [lindex [lindex $_gdb_index_names $idx] 3] + if { $cuvec_offset eq "" } { + _op .4byte \ + "$vec_label - $gdb_index_constant_pool" \ + "CU vector offset" + } elseif { $cuvec_offset eq "end" } { + _op .4byte \ + "$gdb_index_constant_pool_end - $gdb_index_constant_pool" \ + "CU vector offset" + } else { + _op .4byte "$cuvec_offset" "CU vector offset" + } } else { _op .4byte 0 "empty slot" _op .4byte 0 "empty slot" -- 2.25.4