From patchwork Wed Mar 12 10:52:45 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 107761 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A79A93858294 for ; Wed, 12 Mar 2025 10:54:35 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A79A93858294 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=haS5Oite X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org 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 B9D823858429 for ; Wed, 12 Mar 2025 10:53:00 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B9D823858429 Authentication-Results: sourceware.org; dmarc=pass (p=none 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 B9D823858429 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741776780; cv=none; b=x1CTLQdOfjrr7EqaMxDcB/GFPQRGmPR9i6BGUhW96QWJZQr2H+PGJZlz7vyzLDRuCn8fkjNnA2Tk2RDhqonTALewCJ0yKhUp/fxBY8d1EEa3vDvsRd2qJHR0AyoY+3VkmT515C0ErPcP/FZXD/3pJq+ahrrnC5xQQR7G8ropZFs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741776780; c=relaxed/simple; bh=souptsdKQgDTS1zvD5v02VK8SZBtZRGiktuZWlNqQqU=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=UmktbbpuFj8+17vTVeNEuwxAkIXv6JBKvKiA7oedkE6LipEGy0imG7CoLFC9t6qv6QMpy2rN8xUKPtblmmXWmdHbs4zF1XyixPLeYg5p0GXcgHM+bel0dwMEC9rh0fMA1EJ6F/SIEOYWjvdzAt/ghUsshBxvLqAVoszh7dinr5U= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B9D823858429 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1741776780; 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=G5ee/GZdqUfbQSb6jugbhajh1EjLpv66I85H3WMxi2M=; b=haS5OiteWzW9GCXw3yb0WEXHMm8U3qVxywyzEOPYCxHofE90xX3PvH++WBvvKxYAXbUW0x BabUkvChWQMvutowIomFhc2efa1XX7OKF/1dVl2AnnT8NUNlEXZtwUe9KN0RSJ7wd8Zk8Q uwBY6tyZn3MUhw06/+jYAO6euxU3USM= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-620-MNCkmNsTOYmJ126VyIwn1A-1; Wed, 12 Mar 2025 06:52:53 -0400 X-MC-Unique: MNCkmNsTOYmJ126VyIwn1A-1 X-Mimecast-MFC-AGG-ID: MNCkmNsTOYmJ126VyIwn1A_1741776773 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-43cf5196c25so18203455e9.0 for ; Wed, 12 Mar 2025 03:52:53 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741776772; x=1742381572; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=G5ee/GZdqUfbQSb6jugbhajh1EjLpv66I85H3WMxi2M=; b=HWq0wIsH/d5x13ezD2Rjxtn1GEueZLkE9vxODbYvMeY+pLSTmObQJyAJK00uKnzbub 055qU0h8widVMAeGtLNSsbRhm/t4oTNlZjSNJym1KTgL5fxnw2CBGm6NGojrRH0vjPYY zzNJ4yejhGbm4c3fV3+mpfIfWquwqHn92XTPSI24jwAF/cNjWEG2MZdrvFsK01YUL2N8 pe6jpc6sVijNb1SbVVWZOY0sgfyBVbFFdAi0/hGv7GxruqL5u6cU4rfpRkJTEqwR/qAh 6euhhk5XzJYlrqUnVLp2s70eHlaRKWGkvYRULPR1qqz2/5SPaM5vu/n6BDW2gBANRwZ3 kURQ== X-Gm-Message-State: AOJu0Yz3FVoqHTji7aTxjvNRkkhpEZfaP2VgH6KxiUBo43zdbDUeAao1 qDEgVQfCpcSkh11aS77LbVNghut4qfZgZWU4Vx1QRMA8wjUDwdJ6ihaM79oTLt0MCZP8xn3So6n 98DXB9QQe12Fzt5YcJBEBgn+fE6lxGkOu3g62Li5rn/lIdifu0bd6xGvVRFY0BDLWED81MYEFmm kfq1NvqvJD8GkunI3ye25EqKRNc3tAVD6SR+YHfEf6hUw= X-Gm-Gg: ASbGncs9tS3nmPxiaiwBqV+iUPkJ/Us3LbVLAUnn6hDGQZVing/LQhnIGQKA27ZgIz/ clgG6gpxfraqM4w5ISCVZiBOXpoWuLnyRN+C+DyCC4eppIU6WXd6KQpqr5RxaQo5Zf/elJCn23H 4GrE5NSDpYOzGTOM+wQ00AgG9s2bzY0UUxIAKiaeIASJxP7p92qkerayxRMogeWhPC7/L/WREHk Dbm0SuWKos3K7HylHKBqbH/urKTwoWfUszoLTITLnmiwnsJZTGn1JxOVcuf9uXDUSRIUUusysk3 vbnn6u4asLvZWpYLiSTl3GKKpj2lyjSwbUA= X-Received: by 2002:a05:600c:1c9d:b0:439:86fb:7340 with SMTP id 5b1f17b1804b1-43c601d19cbmr179299895e9.30.1741776772240; Wed, 12 Mar 2025 03:52:52 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFgS3AX5Diz1BS+PqsLWW4cbnUhgwZlllKJHvx4su4edQIgAOz9CPK5wydxUXVzxKo+aKkufA== X-Received: by 2002:a05:600c:1c9d:b0:439:86fb:7340 with SMTP id 5b1f17b1804b1-43c601d19cbmr179299665e9.30.1741776771713; Wed, 12 Mar 2025 03:52:51 -0700 (PDT) Received: from localhost (52.72.115.87.dyn.plus.net. [87.115.72.52]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-43d0a74c6a1sm17153035e9.13.2025.03.12.03.52.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 12 Mar 2025 03:52:50 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 2/2] gdb: support zero inode in generate-core-file command Date: Wed, 12 Mar 2025 10:52:45 +0000 Message-ID: <0d389b435cbb0924335adbc9eba6cf30b4a2c4ee.1741776651.git.aburgess@redhat.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: dapInyHHGkXE7X40U_eqTUZiyoadIObjwdiu0bhthxg_1741776773 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-10.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_BARRACUDACENTRAL, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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~patchwork=sourceware.org@sourceware.org It is possible, when creating a shared memory segment (i.e. with shmget), that the id of the segment will be zero. When looking at the segment in /proc/PID/smaps, the inode field of the entry holds the shared memory segment id. And so, it can be the case that an entry (in the smaps file) will have an inode of zero. When GDB generates a core file, with the generate-core-file (or its gcore alias) command, the shared memory segment should be written into the core file. Fedora GDB has, since 2008, carried a patch that tests this case. There is no fix for GDB associated with the test, and unfortunately, the motivation for the test has been lost to the mists of time. This likely means that a fix was merged upstream without a suitable test, but I've not been able to find and relevant commit. The test seems to be checking that the shared memory segment with id zero, is being written to the core file. While looking at this test and trying to work out if it should be posted upstream, I saw that GDB does appear to write the shared memory segment into the core file (as expected), which is good. However, GDB still isn't getting this case exactly right. In gcore_memory_sections (gcore.c) we call back into linux-tdep.c (via the gdbarch_find_memory_regions call) to correctly write the shared memory segment into the core file, however, in linux_make_mappings_corefile_notes, when we use linux_find_memory_regions_full to create the NT_FILE note, we call back into linux_make_mappings_callback for each mapping, and in here we reject any mapping with a zero inode. The result of this, is that, for a shared memory segment with a non-zero id, after loading the core file, the shared memory segment will appear in the 'proc info mappings' output. But, for a shared memory segment with a zero id, the segment will not appear in the 'proc info mappings' output. I propose fixing this by not checking the inode in linux_make_mappings_callback. The inode check was in place since the code was originally added in commit 451b7c33cb3c9ec6272c36870 (in 2012). The test for this bug (a modified version of the original Fedora test) is a little sketchy. User space doesn't have any control over the shared memory id, so all we can do is spam out requests for new shared memory segments and hope that we eventually get the zero id. Obviously, this can fail; the zero id might already be in use by some long running process, or the kernel, for whatever reason, might choose to never allocate the zero id. But, for me, the test actually turns out to be pretty reliable. But that's just my machine, running my work load. I believe the fix in linux-tdep.c is sane, and should be merged. But we might decide that the test is just too unstable. If this is the case, then I propose that we merge the test, along with the fix, but then immediately follow up with a commit to delete the test. This way, if, in the future, someone looks back through the git history at this commit, they will be able to recover the test. You never know when information like this could be useful. I've listed the author of the original test as co-author below. But I have made significant changes to the test, any mistakes should be considered mine. Co-Author: Jan Kratochvil --- gdb/linux-tdep.c | 2 +- gdb/testsuite/gdb.base/gcore-shmid0.c | 183 ++++++++++++++++++++++++ gdb/testsuite/gdb.base/gcore-shmid0.exp | 101 +++++++++++++ 3 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 gdb/testsuite/gdb.base/gcore-shmid0.c create mode 100644 gdb/testsuite/gdb.base/gcore-shmid0.exp diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index d3ab02d03e0..e2d41e7964e 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -1712,7 +1712,7 @@ linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size, = (struct linux_make_mappings_data *) data; gdb_byte buf[sizeof (ULONGEST)]; - if (*filename == '\0' || inode == 0) + if (*filename == '\0') return 0; ++map_data->file_count; diff --git a/gdb/testsuite/gdb.base/gcore-shmid0.c b/gdb/testsuite/gdb.base/gcore-shmid0.c new file mode 100644 index 00000000000..01ebc46499c --- /dev/null +++ b/gdb/testsuite/gdb.base/gcore-shmid0.c @@ -0,0 +1,183 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2007-2025 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 . */ + +/* Test GDB's core file creation for a shared memory mapping that has a + name, but a zero id. The shared memory id is represented as the inode + in the file mappings list, and at one point GDB would ignore any mapping + with a zero inode. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This test relies on acquiring a shared memory mapping with the zero id. + Which id is assigned to a mapping is not under our control, so we just + need to keep asking the kernel for new mappings until we get a mapping + with the desired (zero) id. + + Even then, if some other process, e.g. another instance of this test in + a parallel testsuite run, has already acquired the zero id, then this + test will never get the zero id. + + All we can do is run for some period of time and hope that we eventually + get the desired id. If we don't then we'll report this test as + unsupported. */ + +#define TIMEOUT_SEC 15 + +/* Use multiple threads to increase our chance of creating the shared + memory mapping with the zero id. */ + +#define N_THREADS 4 + +/* A global used to create filler work. */ +static volatile int global_var; + +/* Setting this true causes all threads to terminate. Shouldn't really use + volatile for thread synchronisation, but the only other state that + propagates from the worker threads to the main is the shared memory + mapping, so we're probably OK with volatile. */ +static volatile int stop_all_threads_p = 0; + +static void +initialized (void) +{ + /* Just some filler work. */ + global_var++; +} + +static void +unresolved (void) +{ + /* Just some filler work. */ + global_var++; +} + +/* Thread worker function. Spin creating and destroying shared memory + mappings, trying to acquire a mapping with id 0. When the mapping with + id 0 is created, map the shared memory region into the process, and set + the global STOP_ALL_THREADS_P, then terminate this thread. + + If some other thread sets STOP_ALL_THREADS_P then just terminate this + thread. */ + +static void * +worker_function (void *arg) +{ + unsigned int *addr = (void *) -1L; + + /* The generated SID will cycle with an increment of 32768, attempt until + it wraps to 0. */ + for (int attempt = 0; addr == (void *) -1L && !stop_all_threads_p; attempt++) + { + int sid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | IPC_EXCL | 0777); + if (sid == -1) + { + perror ("shmget"); + exit (1); + } + + /* Use SID only if it is 0, retry it otherwise. */ + if (sid == 0) + { + /* Attach the shared memory mapping. */ + addr = shmat (sid, NULL, SHM_RND); + if (addr == (void *) -1L) + { + perror ("shmat"); + exit (1); + } + + /* Tell all the other threads to exit. */ + stop_all_threads_p = 1; + } + + /* Mark the shared memory mapping as deleted -- once the last user + has finished with it. */ + if (shmctl (sid, IPC_RMID, NULL) != 0) + { + perror ("shmctl"); + exit (1); + } + } +} + +int +main (void) +{ + pthread_t thr[N_THREADS]; + + /* Start all threads. */ + for (int i = 0; i < N_THREADS; ++i) + pthread_create (&thr[i], NULL, worker_function, NULL); + + /* This is set to non-zero if we timeout. */ + int timeout_p = 0; + + /* Used to track how long we've been waiting for the worker threads. */ + time_t ts_start, ts; + + if (time (&ts_start) == (time_t) -1) + { + perror ("time"); + exit (1); + } + + /* Wait for at least TIMEOUT_SEC seconds, or until the global + STOP_ALL_THREADS_P is set to true. If we timeout then set + STOP_ALL_THREADS_P, which will cause all the worker threads to stop + themselves. */ + while (!stop_all_threads_p) + { + usleep (1000); + + if (time (&ts) == (time_t) -1) + { + perror ("time"); + exit (1); + } + + if (ts >= ts_start + TIMEOUT_SEC) + { + stop_all_threads_p = 1; + timeout_p = 1; + break; + } + } + + /* Join all threads. */ + for (int i = 0; i < N_THREADS; ++i) + pthread_join (thr[i], NULL); + + /* If we timed-out then there's nothing to be done. */ + if (timeout_p) + { + unresolved (); + exit (1); + } + + /* Success! We managed to create a shared memory mapping with id 0. */ + initialized (); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/gcore-shmid0.exp b/gdb/testsuite/gdb.base/gcore-shmid0.exp new file mode 100644 index 00000000000..932717e86bb --- /dev/null +++ b/gdb/testsuite/gdb.base/gcore-shmid0.exp @@ -0,0 +1,101 @@ +# Copyright 2007-2025 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 . + +# Test GDB's handling of gcore for shared memory mappings with a name +# but zero inode. The inode of a shared memory mapping is actually +# its shared memory id, which can be zero. + +require gcore_cmd_available +require {istarget *-linux*} +require {!is_remote host} + +standard_testfile + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ + {debug pthreads}] == -1 } { + return +} + +if { ![ runto_main ] } { + return +} + +gdb_breakpoint "initialized" +gdb_breakpoint "unresolved" + +with_timeout_factor 3 { + gdb_test_multiple "continue" "setup shmid 0 test" { + -re -wrap "Breakpoint .*, initialized .* at .*" { + pass $gdb_test_name + } + -re -wrap "Breakpoint .*, unresolved .* at .*" { + unsupported $gdb_test_name + return + } + } +} + +# Ensure that the shared memory region shows up in 'proc mappings'. +set shm_start "" +set shm_end "" +gdb_test_multiple "info proc mappings" "" { + -re "^($hex)\\s+($hex)\\s+\[^\r\n\]+/SYSV0+ \\(deleted\\)\\s*\r\n" { + set shm_start $expect_out(1,string) + set shm_end $expect_out(2,string) + exp_continue + } + + -re "^$gdb_prompt $" { + gdb_assert { $shm_start ne "" && $shm_end ne "" } $gdb_test_name + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } +} + +# Create the core file. +set corefile_name [standard_output_file gcore-shmid0.core] +if { ![gdb_gcore_cmd $corefile_name "create core file"] } { + return +} + +# Allow the inferior to finish. This ensures that the shared memory +# region is cleaned up correctly. That said, the shared memory region +# should already be marked as deleted by this point, so it should be +# OK to just kill the inferior. But letting the inferior finish +# doesn't hurt. +gdb_continue_to_end "finish" + +# Restart GDB. +clean_restart $binfile + +# Load the core file. +if { [gdb_core_cmd $corefile_name "load core file"] != 1 } { + return +} + +gdb_test "bt" \ + [multi_line \ + "#0\\s+initialized \\(\\) at \[^\r\n\]+" \ + "#1\\s+$hex in main \\(\\) at \[^\r\n\]+"] + +gdb_test "info proc mappings" "/SYSV0+ \\(deleted\\).*" \ + "shared mapping shows after core file load" + +gdb_test "x/8w $shm_start" \ + [multi_line \ + "$hex:\\s+0\\s+0\\s+0\\s+0\\s*" \ + "$hex:\\s+0\\s+0\\s+0\\s+0\\s*"]