From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id ITvgModdk2kSzT0AWB0awg (envelope-from ) for ; Mon, 16 Feb 2026 13:10:15 -0500 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=suse.de header.i=@suse.de header.a=rsa-sha256 header.s=susede2_rsa header.b=GyK7fpTr; dkim=pass header.d=suse.de header.i=@suse.de header.a=ed25519-sha256 header.s=susede2_ed25519 header.b=XQklyLXR; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.a=rsa-sha256 header.s=susede2_rsa header.b=GyK7fpTr; dkim=neutral header.d=suse.de header.i=@suse.de header.a=ed25519-sha256 header.s=susede2_ed25519 header.b=XQklyLXR; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id C9C9B1E0BA; Mon, 16 Feb 2026 13:10:15 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, 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 E5AE01E08D for ; Mon, 16 Feb 2026 13:10:13 -0500 (EST) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 822EC4BAD17E for ; Mon, 16 Feb 2026 18:10:12 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 822EC4BAD17E Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=suse.de header.i=@suse.de header.a=rsa-sha256 header.s=susede2_rsa header.b=GyK7fpTr; dkim=pass header.d=suse.de header.i=@suse.de header.a=ed25519-sha256 header.s=susede2_ed25519 header.b=XQklyLXR; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.a=rsa-sha256 header.s=susede2_rsa header.b=GyK7fpTr; dkim=neutral header.d=suse.de header.i=@suse.de header.a=ed25519-sha256 header.s=susede2_ed25519 header.b=XQklyLXR Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) by sourceware.org (Postfix) with ESMTPS id 5EF634BAD168 for ; Mon, 16 Feb 2026 18:09:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5EF634BAD168 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=suse.de Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=suse.de ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 5EF634BAD168 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1771265372; cv=none; b=v2Ml8nWV8j4kkH57WFT25tq/nPkGzuGvaGSlzzoKsXqKf3b5dOPn999KjrcHxRL+nqibnIYrCVRmlO2Wwjani3mascFL8k9ruP0+J1DfruDElqXRK1uJNPo7WNu7FG4g8eatGknOj/YtzxMqnd2qPN+px6qZdW/T8YCKSW0cSOM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1771265372; c=relaxed/simple; bh=N/LL1GCbQuNjFTenXRthWWrD8SPsSJZh+TnS7W7IgrU=; h=DKIM-Signature:DKIM-Signature:DKIM-Signature:DKIM-Signature:From: To:Subject:Date:Message-ID:MIME-Version; b=FgsJf8MQ6PP6Ns7pzcOhwqiIy2MGaR506AlMoLcFG3xjgtao3CgkAVxpdlUcpp/lfzza7p3BadXB3dd6Naa2GCmhCph5U438TzBm64zU49wR3oLaxqLRmh3egxW4+LFVqE3+jg1kOo0zAamXPcCj3C5C37YCM26CEQia1eHI4Gs= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5EF634BAD168 Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 6B9B65BD39 for ; Mon, 16 Feb 2026 18:09:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1771265371; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=cGCXQP2znaJ8YkhiboF/03uClyJmB5MhZCe/4brb7VI=; b=GyK7fpTrRNAlZoLNwWjD+9JBtz9xbvD5aJUBcTFBJ9ji/gVHdj32nnem9QGJ07ZSAd8j20 rM44qDWrhKJs31mkZmOUFCXXe93xIxVTqStihjj00D7hKZBXmbW8KP8nwuko51ZSzrjDRD k7FBxGvkERy4HqXK3v4Ee8X5smw3tc4= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1771265371; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=cGCXQP2znaJ8YkhiboF/03uClyJmB5MhZCe/4brb7VI=; b=XQklyLXRDM1ZB8uvoOsWOjZ7pTq05+LnyNYT3mvcpDdHJIdksrp2w4M/CVk4YghJzngB37 Uk1ku6YnmUxAqvAg== Authentication-Results: smtp-out2.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1771265371; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=cGCXQP2znaJ8YkhiboF/03uClyJmB5MhZCe/4brb7VI=; b=GyK7fpTrRNAlZoLNwWjD+9JBtz9xbvD5aJUBcTFBJ9ji/gVHdj32nnem9QGJ07ZSAd8j20 rM44qDWrhKJs31mkZmOUFCXXe93xIxVTqStihjj00D7hKZBXmbW8KP8nwuko51ZSzrjDRD k7FBxGvkERy4HqXK3v4Ee8X5smw3tc4= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1771265371; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=cGCXQP2znaJ8YkhiboF/03uClyJmB5MhZCe/4brb7VI=; b=XQklyLXRDM1ZB8uvoOsWOjZ7pTq05+LnyNYT3mvcpDdHJIdksrp2w4M/CVk4YghJzngB37 Uk1ku6YnmUxAqvAg== Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 82AB73EA64 for ; Mon, 16 Feb 2026 18:09:29 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id iG+KHlldk2moCwAAD6G6ig (envelope-from ) for ; Mon, 16 Feb 2026 18:09:29 +0000 From: Tom de Vries To: gdb-patches@sourceware.org Subject: [PATCH v2 1/2] [gdb/symtab] Replace per-BFD lock with global BFD lock Date: Mon, 16 Feb 2026 19:09:24 +0100 Message-ID: <20260216180925.3174052-2-tdevries@suse.de> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260216180925.3174052-1-tdevries@suse.de> References: <20260216180925.3174052-1-tdevries@suse.de> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spamd-Result: default: False [-2.80 / 50.00]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-0.994]; MIME_GOOD(-0.10)[text/plain]; FUZZY_RATELIMITED(0.00)[rspamd.com]; RCVD_VIA_SMTP_AUTH(0.00)[]; RCPT_COUNT_ONE(0.00)[1]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; DKIM_SIGNED(0.00)[suse.de:s=susede2_rsa,suse.de:s=susede2_ed25519]; URIBL_BLOCKED(0.00)[sourceware.org:url,suse.de:mid,imap1.dmz-prg2.suse.org:helo]; FROM_EQ_ENVFROM(0.00)[]; FROM_HAS_DN(0.00)[]; DBL_BLOCKED_OPENRESOLVER(0.00)[imap1.dmz-prg2.suse.org:helo,suse.de:mid]; RCVD_COUNT_TWO(0.00)[2]; TO_MATCH_ENVRCPT_ALL(0.00)[]; TO_DN_NONE(0.00)[]; PREVIOUSLY_DELIVERED(0.00)[gdb-patches@sourceware.org]; RCVD_TLS_ALL(0.00)[] 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 Our current BFD locking scheme is as follows [1]: ... There is one global mutex, gdb_bfd_mutex, which BFD can lock and unlock via the callbacks we pass it. This appears to lock the internal global data structures of BFD (like its global cache or some global counter), but not data in individual `bfd *`instances. If the user of BFD wishes to call functions on a given `bfd *` from multiple threads, it must provide the synchronization itself. For this, we have gdb_bfd_data::per_bfd_mutex. ... PR33811 reports the following data race: ... Read of size 1 at 0x72440010c608 by thread T5 (mutexes: write M0): #0 bfd_get_section_limit_octets bfd.h:2433 #1 bfd_get_section_contents bfd/section.c:1612 #2 bfd_is_section_compressed_info bfd/compress.c:901 #3 bfd_is_section_compressed bfd/compress.c:959 #4 gdb_bfd_map_section(bfd_section*, unsigned long*) gdb/gdb_bfd.c:779 ... vs: ... Previous write of size 4 at 0x72440010c608 by main thread (mutexes: write M1): #0 bfd_cache_delete bfd/cache.c:180 #1 _bfd_cache_close_unlocked bfd/cache.c:607 #2 bfd_cache_close_all bfd/cache.c:664 #3 notify_before_prompt gdb/event-top.c:524 ... In more detail, this read in bfd_get_section_limit_octets in bfd/bfd-in2.h: ... if (abfd->direction != write_direction && sec->rawsize != 0) ... vs. this write in bfd_cache_delete in bfd/cache.c: ... abfd->last_io = bfd_io_force; ... There is already locking used for both the read and write. In gdb_bfd_map_section, we use the per-BFD lock: ... gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); gdb::lock_guard guard (gdata->per_bfd_mutex); ... And in bfd_cache_close_all, we use the global BFD lock: ... bool bfd_cache_close_all (void) { ... if (!bfd_lock ()) return false; ... if (!bfd_unlock ()) return false; return ret; } ... The problem is that the locking is not sufficient. Since bfd_cache_close_all accesses individual BFDs, it needs to lock the corresponding per-BFD locks as well. A naive way to implement this using the existing scheme of wrappers, would be to add a gdb_bfd_cache_close_all that locks all per-BFD locks, calls bfd_cache_close_all, and unlocks all per-BFD locks, like this: ... bool gdb_bfd_cache_close_all () { bool res; for (auto abfd : all_bfds) { auto gdata = static_cast (bfd_usrdata (abfd)); gdata->per_bfd_mutex.lock (); } res = bfd_cache_close_all (); for (auto abfd : all_bfds) { auto gdata = static_cast (bfd_usrdata (abfd)); gdata->per_bfd_mutex.unlock (); } return res; } ... Apart from the fact that trying to hold all those locks at the same time increases the changes of deadlock, it also accesses all_bfds without locking the required global BFD lock (reported by TSAN). It's easy enough to fix that by adding: ... gdb_bfd_cache_close_all () { + gdb::lock_guard guard (gdb_bfd_mutex); ... but that brings us to the problem of lock-order-inversion (also reported by TSAN), and indeed timeouts do occur. I came up with a complicated scheme [2] that: - doesn't try to lock all the per-BFD locks at the same time, and - addresses the lock-order-inversion problem by releasing the global BFD lock before acquiring the per-BFD lock and then re-acquiring the global BFD lock The scheme is implemented in bfd_cache_close_all: ... bool bfd_cache_close_all (per_bfd_lock_unlock_fn_type per_bfd_lock, per_bfd_lock_unlock_fn_type per_bfd_unlock) { bool ret = true; if (!bfd_lock ()) return false; while (true) { bfd *abfd = bfd_last_cache; if (abfd == nullptr) break; /* At this point we'd like to lock the per-bfd lock for abfd, but as it happens we run into lock order inversion problems. So instead, we unlock the global BFD lock, and then lock the two in the opposite order. */ if (!bfd_unlock ()) return false; if (!per_bfd_lock (abfd)) return false; if (!bfd_lock ()) { per_bfd_unlock (abfd); return false; } if (abfd != bfd_last_cache) { /* While the global BFD lock was briefly unlocked, bfd_last_cache changed, so try again. */ if (!per_bfd_unlock (abfd)) { ret = false; break; } continue; } ret &= _bfd_cache_close_unlocked (abfd); if (!per_bfd_unlock (abfd)) { ret = false; break; } /* Stop a potential infinite loop should bfd_cache_close() not update bfd_last_cache. */ if (bfd_last_cache == abfd) break; } if (!bfd_unlock ()) return false; return ret; } ... However, this approach was seen as too convoluted. So instead, revert to a simple locking scheme with only the global BFD lock, dropping the per-BFD lock. This changes the per-BFD locking in gdb_bfd_map_section to global BFD locking, which means that the read in bfd_get_section_limit_octets is now guarded by the global BFD lock, which is the same lock guarding the write in bfd_cache_delete. So, the race is fixed. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33811 [1] https://sourceware.org/pipermail/gdb-patches/2026-January/224291.html [2] https://sourceware.org/pipermail/gdb-patches/2026-January/224426.html --- gdb/gdb_bfd.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/gdb/gdb_bfd.c b/gdb/gdb_bfd.c index 1933166b5fb..712cea494fa 100644 --- a/gdb/gdb_bfd.c +++ b/gdb/gdb_bfd.c @@ -147,17 +147,6 @@ struct gdb_bfd_data /* The registry. */ registry registry_fields; - - /* Most of the locking needed for multi-threaded operation is - handled by BFD itself. However, the current BFD model is that - locking is only needed for global operations -- but it turned out - that the background DWARF reader could race with the auto-load - code reading the .debug_gdb_scripts section from the same BFD. - - This lock is the fix: wrappers for important BFD functions will - acquire this lock before performing operations that might modify - the state of this BFD. */ - gdb::mutex per_bfd_mutex; }; registry * @@ -766,8 +755,7 @@ gdb_bfd_map_section (asection *sectp, bfd_size_type *size) abfd = sectp->owner; - gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); - gdb::lock_guard guard (gdata->per_bfd_mutex); + gdb::lock_guard guard (gdb_bfd_mutex); descriptor = get_section_descriptor (sectp); @@ -1100,8 +1088,7 @@ bool gdb_bfd_get_full_section_contents (bfd *abfd, asection *section, gdb::byte_vector *contents) { - gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); - gdb::lock_guard guard (gdata->per_bfd_mutex); + gdb::lock_guard guard (gdb_bfd_mutex); bfd_size_type section_size = bfd_get_section_alloc_size (abfd, section); @@ -1116,8 +1103,7 @@ gdb_bfd_get_full_section_contents (bfd *abfd, asection *section, int gdb_bfd_stat (bfd *abfd, struct stat *sbuf) { - gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); - gdb::lock_guard guard (gdata->per_bfd_mutex); + gdb::lock_guard guard (gdb_bfd_mutex); return bfd_stat (abfd, sbuf); } @@ -1127,8 +1113,7 @@ gdb_bfd_stat (bfd *abfd, struct stat *sbuf) long gdb_bfd_get_mtime (bfd *abfd) { - gdb_bfd_data *gdata = (gdb_bfd_data *) bfd_usrdata (abfd); - gdb::lock_guard guard (gdata->per_bfd_mutex); + gdb::lock_guard guard (gdb_bfd_mutex); return bfd_get_mtime (abfd); } -- 2.51.0