From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id JEKxFo/xSmirKgsAWB0awg (envelope-from ) for ; Thu, 12 Jun 2025 11:26:07 -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=e5Lpkk/m; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 40F5B1E102; Thu, 12 Jun 2025 11:26:07 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-10.1 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, RCVD_IN_VALIDITY_RPBL,RCVD_IN_VALIDITY_SAFE autolearn=ham autolearn_force=no version=4.0.1 Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (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 604401E089 for ; Thu, 12 Jun 2025 11:26:03 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id CDDDE3823342 for ; Thu, 12 Jun 2025 15:26:02 +0000 (GMT) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 364FD382490A for ; Thu, 12 Jun 2025 15:25:37 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 364FD382490A 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 364FD382490A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1749741937; cv=none; b=KIxASq/kDdnbpkczIyVnodpgJx4y8E4v1EcfB8NbPfbL8rKjVxffGU5STW1pvEyRd600xdhcf6iZn/7FNhkA1uuoji9o3MEb0DY/0FO3DrQ/FMrXrTCx1ifJ+jNcyJKAMdYcE/wPQra57CHC9tq/wflWd2gayIghvyrx61r2Q+s= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1749741937; c=relaxed/simple; bh=nXiQR97C1JFibbmIFKFuZuVDhDebeJqtweT5/95UykU=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=ozQX3b/wd35s0Rq6GHzmgPPl/TicdNEnLVIiikImrShD+TdXnrfBpEMGoPeii1jhi1ohEUx947EExvmZaoJ73P/3Xk1mZ87ME+aNS2K3eePStgiWKjm+JgUxq6Jc3XYbPvJQjhm6P0b1G/dFeKdlxJ7r+y6tDuFbG8Qmkswl84E= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 364FD382490A 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=e5Lpkk/m DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1749741934; 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=5amiCbp3QYZsJzDWtM5HmsD2tqPuFN8To7Wk37ppyEA=; b=e5Lpkk/mwU8UY2f/BRaaC2LYUpKjLMfLPO8Rolc9u+Uj/2p5iuKj2L6IARlbpMwuf05F7Z W2gY2kl+gT+fF9KiCfHUuukhOPP+WPZAmWYPAImDKcHS5pm4PujiJ1orBqtJiNqr4kwYj4 pG+SLl3nZpdoGr2P0z0oGsYsfC+dP64= 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-421-AbCw22mRPv2XECZyhp6ANQ-1; Thu, 12 Jun 2025 11:25:33 -0400 X-MC-Unique: AbCw22mRPv2XECZyhp6ANQ-1 X-Mimecast-MFC-AGG-ID: AbCw22mRPv2XECZyhp6ANQ_1749741933 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-3a503f28b09so564366f8f.0 for ; Thu, 12 Jun 2025 08:25:33 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749741932; x=1750346732; 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=5amiCbp3QYZsJzDWtM5HmsD2tqPuFN8To7Wk37ppyEA=; b=vKZwKOpvFnDxJe+mTojX6+fLnOYAvbJGoD8PyrTOYN+C1GvwoDalPO489zaWWIxlzS DY5tOV0hySokKg36abtPiZ/sKymlR9bRIDUVL8lc+YxY9PYYPuctVKVTBVmEeYD2dRzj ehEG2mH4XEdshog3G7KaE72DpB/memctbpAaaxSJzCB8k1R8v37bzsGODg+4waDKiWbu dFMJJQj9M8sGpCybUcn/QduwB9aDBZlq8nGVh+plqjI/dY8P2Q7awgtmyqebGE3ywyEr 6lrxlMkYb+v6f6ITO2PuRgyBIlIMdkDB/VMuBMVfmzNFDDaRe3YtpmfDUuTWRMY7cZ1w yciQ== X-Gm-Message-State: AOJu0Yy9v/IMSjFnIOM+JO1DGCfV55FQ70wtZagtDogZnkXWaRPt+zLO s4b4/zIZpT7RCsmmg10oAblhOEbniQOMUfbUIKxp76tADKRnsVY+CjL4812sMrU4E/ldKL74kFO lmAAGBFD8G+3uBD3n/hmmG/LGIDcEDuj6URCFzDNxau7F11xPnLRlQIXufMnUQorkPn5zDCYMdA I7V2KIlVGJ6o17AlaM4iAiclskuVgaZE2fjoGt4tc/OeLtWbQ= X-Gm-Gg: ASbGncsyJA9x6lZlCYgpb0Oez99mEfPkxUvDTvJiKWSoK7Uq9M1QLsLFFJtOs6g2kao /fL7nUnZw58D75RMb4G3rXNwZljlL0KPflzZLr07FtL5zsdiXqFjZ4tThPFLDCwYxbdVu6k2F1p NjpbCJHYK8MXhHKYhiWA5qlJvQK8v+0pz30Xb6YdSJtl+3MKf88vyKgT032woLVlWqwCUQ9V2Ht d1b29VzM8wwLzR+KhAdjlrjtI8fwBPtJS9QyZ1379dYuhVxv86EP6lHeU42eSCBI3TiuJ/3FT7f 6HKKoYSZbzKaTq36XJ/U1V1yOfo2z+/59ur6PAyocA0dG4I= X-Received: by 2002:a5d:64ed:0:b0:3a4:e0f4:e455 with SMTP id ffacd0b85a97d-3a56077c01amr3474122f8f.11.1749741931753; Thu, 12 Jun 2025 08:25:31 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGvTw/GWzpucK6GMc5P5CVt2zKWlsyfDw7X3JrPfaFWu5MSN7xeFxa5tyq+gJECWkTVXJaKAw== X-Received: by 2002:a5d:64ed:0:b0:3a4:e0f4:e455 with SMTP id ffacd0b85a97d-3a56077c01amr3474072f8f.11.1749741931002; Thu, 12 Jun 2025 08:25:31 -0700 (PDT) Received: from localhost (75.226.159.143.dyn.plus.net. [143.159.226.75]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3a5619a84e8sm2244892f8f.42.2025.06.12.08.25.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Jun 2025 08:25:30 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Benjamin Berg , Andrew Burgess Subject: [PATCHv2] gdb: linux-namespaces: enter user namespace when appropriate Date: Thu, 12 Jun 2025 16:25:27 +0100 Message-ID: <824ee908821f07452286730643c1efd5f8b01eb2.1749741769.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: J3RrgdD_WFbfm6PhUkjr2XQ5QoOgtHcCaYYCOPMItsY_1749741933 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 From: Benjamin Berg In v2: - Update the test to ignore a warning seen when running the test on a machine with libc debug information installed, but without the libc source being available, e.g.: warning: 46 ../sysdeps/unix/sysv/linux/arm/libc-do-syscall.S: No such file or directory This was causing some CI failures to be reported from Linaro. - Rebased to current upstream/master. -- The use of user namespaces is required for normal users to use mount namespaces. Consider trying this as an unprivileged user: $ unshare --mount /bin/true unshare: unshare failed: Operation not permitted The problem here is that an unprivileged user doesn't have the required permissions to create a new mount namespace. If, instead, we do this: $ unshare --mount --map-root-user /bin/true then this will succeed. The new option causes unshare to create a user namespace in which the unprivileged user is mapped to UID/GID 0, and so gains all privileges (inside the namespace), the user is then able to create the mount namespace as required. So, how does this relate to GDB? When a user attaches to a process running in a separate mount namespace, GDB makes use of a separate helper process (see linux_mntns_get_helper in nat/linux-namespaces.c), which will then use the `setns` function to enter (or try to enter) the mount namespace of the process GDB is attaching too. The helper process will then handle file I/O requests received from GDB, and return the results back to GDB, this allows GDB to access files within the mount namespace. The problem here is that, switching to a mount namespace requires that a process hold CAP_SYS_CHROOT and CAP_SYS_ADMIN capabilities within its user namespace (actually it's a little more complex, see 'man 2 setns'). Assuming GDB is running as an unprivileged user, then GDB will not have the required permissions. However, if GDB enters the user namespace that the `unshare` process created, then the current user will be mapped to UID/GID 0, and will have the required permissions. And so, this patch extends linux_mntns_access_fs (in nat/linux-namespace.c) to first try and switch to the user namespace of the inferior before trying to switch to the mount namespace. If the inferior does have a user namespace, and does have elevated privileges within that namespace, then this first switch by GDB will mean that the second step, into the mount namespace, will succeed. If there is no user namespace, or the inferior doesn't have elevated privileges within the user namespace, then the switch into the mount namespace will fail, just as it currently does, and the user will need to give elevated privileges to GDB via some other mechanism (e.g. run as root). This work was originally posted here: https://inbox.sourceware.org/gdb-patches/20230321120126.1418012-1-benjamin@sipsolutions.net I (Andrew Burgess) have made some cleanups to the code to comply with GDB's coding standard, and the test is entirely mine. This commit message is also entirely mine -- the original message was very terse and required the reader to understand how the various namespaces work and interact. The above is my attempt to document what I now understand about the problem being fixed. I've left the original author in place as the core of the GDB change itself is largely as originally presented, but any inaccuracies in the commit message, or problems with the test, are all mine. Co-Authored-by: Andrew Burgess --- gdb/nat/linux-namespaces.c | 24 +++- .../gdb.base/user-namespace-attach.c | 35 +++++ .../gdb.base/user-namespace-attach.exp | 136 ++++++++++++++++++ 3 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 gdb/testsuite/gdb.base/user-namespace-attach.c create mode 100644 gdb/testsuite/gdb.base/user-namespace-attach.exp diff --git a/gdb/nat/linux-namespaces.c b/gdb/nat/linux-namespaces.c index a3a38d82e64..4b5cb373e7a 100644 --- a/gdb/nat/linux-namespaces.c +++ b/gdb/nat/linux-namespaces.c @@ -880,7 +880,7 @@ linux_mntns_access_fs (pid_t pid) struct stat sb; struct linux_mnsh *helper; ssize_t size; - int fd; + int fd, fd_user = -1; if (pid == getpid ()) return MNSH_FS_DIRECT; @@ -897,6 +897,8 @@ linux_mntns_access_fs (pid_t pid) { int save_errno = errno; close (fd); + if (fd_user >= 0) + close (fd_user); errno = save_errno; }; @@ -906,6 +908,13 @@ linux_mntns_access_fs (pid_t pid) if (sb.st_ino == ns->id) return MNSH_FS_DIRECT; + struct linux_ns *ns_user = linux_ns_get_namespace (LINUX_NS_USER); + if (ns_user != nullptr) + { + const char *ns_filename = linux_ns_filename (ns_user, pid); + fd_user = gdb_open_cloexec (ns_filename, O_RDONLY, 0).release (); + } + helper = linux_mntns_get_helper (); if (helper == NULL) return MNSH_FS_ERROR; @@ -914,6 +923,19 @@ linux_mntns_access_fs (pid_t pid) { int result, error; + /* Try to enter the user namespace first. The current user might + have elevated privileges within the user namespace, which would + then allow the attempt to enter the mount namespace to succeed. */ + if (fd_user >= 0) + { + size = mnsh_send_setns (helper, fd_user, 0); + if (size < 0) + return MNSH_FS_ERROR; + + if (mnsh_recv_int (helper, &result, &error) != 0) + return MNSH_FS_ERROR; + } + size = mnsh_send_setns (helper, fd, 0); if (size < 0) return MNSH_FS_ERROR; diff --git a/gdb/testsuite/gdb.base/user-namespace-attach.c b/gdb/testsuite/gdb.base/user-namespace-attach.c new file mode 100644 index 00000000000..684ce1cfc66 --- /dev/null +++ b/gdb/testsuite/gdb.base/user-namespace-attach.c @@ -0,0 +1,35 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 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 . */ + +#include +#include +#include + +volatile int spin_p = 1; + +int +main () +{ + alarm (60); + + printf ("pid = %lld\n", ((long long) getpid ())); + + while (spin_p) + sleep (1); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/user-namespace-attach.exp b/gdb/testsuite/gdb.base/user-namespace-attach.exp new file mode 100644 index 00000000000..c01bc5ca583 --- /dev/null +++ b/gdb/testsuite/gdb.base/user-namespace-attach.exp @@ -0,0 +1,136 @@ +# Copyright 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 . + +# Check that GDB can attach to a process started using 'unshare'. The +# inferior is started in a separate mnt namespace. + +require can_spawn_for_attach + +standard_testfile + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile] == -1} { + return +} + +# This test relies (at least in some parts) on the sysroot being +# 'target:'. Grab the current sysroot now so we can skip those tests +# if the board file has changed the sysroot. +set sysroot "" +set test "show sysroot" +gdb_test_multiple $test $test { + -re -wrap "The current system root is \"(.*)\"\\." { + set sysroot $expect_out(1,string) + } +} + +# Start a process using 'unshare FLAGS', then attach to the process +# from GDB. Check that the attach worked as expected. +proc run_test { flags } { + + # If FLAGS contains '--mount' then a separate mnt namespace will + # be created, in which case the executable will have been read + # from the 'target:'. Otherwise, the executable will have been + # read from the local filesystem, and there will be no prefix. + # + # Of course, this only applies if the sysroot is 'target:', some + # boards change this, so skip these tests on those boards. + if { [lsearch -exact [split $flags " "] "--mount"] != -1 } { + if { $::sysroot ne "target:" } { + return + } + + set prefix "target:" + } else { + set prefix "" + } + + + + set inferior_spawn_id \ + [spawn_wait_for_attach [list "unshare $flags $::binfile"]] + if { $inferior_spawn_id == -1 } { + unsupported "failed to spawn for attach" + return + } + + set inferior_pid [spawn_id_get_pid $inferior_spawn_id] + + clean_restart + + set saw_bad_warning false + gdb_test_multiple "attach $inferior_pid" "attach to inferior" { + -re "^attach $::decimal\r\n" { + exp_continue + } + + -re "^warning: \[^\r\n\]+: could not open as an executable file: \[^\r\n\]+\r\n" { + set saw_bad_warning true + exp_continue + } + + -re "^warning: \[^\r\n\]+: can't open to read symbols: \[^\r\n\]+\r\n" { + set saw_bad_warning true + exp_continue + } + + -re "^warning: Could not load vsyscall page because no executable was specified\r\n" { + # This warning is a secondary consequence of the above bad + # warnings, so don't count this as a bad warnings, ignore + # it instead. + exp_continue + } + + -re "^warning:\\s+$::decimal\\s*\[^\r\n\]: No such file or directory\r\n" { + # This unrelated warning is seen when GDB stops in libc, + # and the source code for libc is not available. + exp_continue + } + + -re "^warning: \[^\r\n\]+\r\n" { + # If we ignore "other" warnings then, should the above + # warnings strings change we'll start ignoring the bad + # warnings, and the test will appear to pass. + # + # If you are seeing a warning here that really has nothing + # to do with the test failing, then the correct solution + # is to add a new regexp to specifically match _that_ + # warning, and ignore it. + set saw_bad_warning true + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert { !$saw_bad_warning } $gdb_test_name + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } + + # Ensure GDB could access the executable. + set binfile_re [string_to_regexp $::binfile] + gdb_test "info inferiors" \ + "\r\n\\*\\s+$::decimal\\s+\[^\r\n\]+\\s+${prefix}${binfile_re}\\s*" +} + +set test_flags [list \ + "--mount --map-root-user" \ + "--user" \ + "--user --map-root-user"] + +foreach_with_prefix flags $test_flags { + run_test $flags +} base-commit: add73a101f5e8225d4055563c52adaa64c8fb11d -- 2.47.1