From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pf1-f178.google.com (mail-pf1-f178.google.com [209.85.210.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6F4A926FA4E for ; Sat, 29 Nov 2025 09:15:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.178 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764407729; cv=none; b=av+CMc5KcZ9pWzfaZ0/8b2zYvAZWXPdo9tb+8qBMTHGNyE/CJcAVY1VtEnIQZpnoDCeji2Piw/wyOy0lGwsAY5ng0Mm5etS0u73+PRoQ2UGwjUU3xVDWxynZEkAjDXhXUOYfGRo3+GEAr3Ad+co+VzulgqXmdruIGIOPK9bjCZ0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764407729; c=relaxed/simple; bh=IhlLSpZF72FSrdV1+YHnN2WHoXOri79HtwWzTG5DQ+A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jYavYDHtFP6NNtdHQnEIKUZz6WknII7eNbOaQmmbk+nimYFJirfsV3oZKnZ03x3GQxWbaqKpRj2ezCWGD1hdLr7rLy+qOhl3qzLnl2EHJxlWXeYM5kPi248nj8/Ww6eM3/8dA/LQU8lIBOcg8F0YtlQZR/dza7tMM5T9gBtNqLU= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=H1dHyNAs; arc=none smtp.client-ip=209.85.210.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="H1dHyNAs" Received: by mail-pf1-f178.google.com with SMTP id d2e1a72fcca58-7d26a7e5639so2357403b3a.1 for ; Sat, 29 Nov 2025 01:15:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1764407727; x=1765012527; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=pVcxx22uwcyWv5Zel36iqnOvGPO1H7KVbIWMJe+WJP8=; b=H1dHyNAsa9Aly4A02qUmfNJdAr0Lw3ih8Fn9bkqmD6XR49Bmzh77h4DyPOPjWqKoaT FXU02MUX0XkFaWVRh4EUAUCsbGSCAwxHBhjIKOwf851vr+uwKAORCqqkG3nE+Mo88pyO xB0tsFHph7J5qH3uU17OXTaVAT+V2BDN7Uo6qcY0MYMesTydielVsdJFaQoWlEoyPmvC nUP2i/r+K8CF5SVyMWGQ+GfEHRaOMGFYWDOQaKbNctiokI3o9fc3vjp2e7cxaBLx6fCp M+WwjkzZcS0oxifVuZzNvJkd1EeDPBlwd4YNuMvQuby+Cgf/41PS5Y2ZpeKb3pX9Hreo qezw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764407727; x=1765012527; 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; bh=pVcxx22uwcyWv5Zel36iqnOvGPO1H7KVbIWMJe+WJP8=; b=c9sK3aNhLQ4mqbJ5bynFFJ1+rSHbVkybetD3cLrdV/yp0RjAto4gM/i2EyirZcsdgT dzPlcsayHRPjm2vdA7SrRTQZHWqXbkMSRh/lBrOLBh1kqxYFwWbThhVspKfNGhYYLGvJ BilNqkhBB7Jxr51UPGX4W7U5yJLUva96OBiyPuTVBSkHXCydcIFzIz0+76UHj8ut+dn0 3gdyymV4MtUGb5xAMiFTEtRUk9MH7NQHqCPEbGoUIkxCcq46dg72nQps5cJ4Fir5z56U X+8wj9DWUt0JRergXWsImVjNRipcD+FDodhqgwm5b1oqj0H+IUTrjYhGJW2bla/RrRSh 6nhw== X-Forwarded-Encrypted: i=1; AJvYcCXabf13Gkxk726ZnwEbMbiqVpWcb1NmmXODQOlpDWi2Dw4B7PAQVhTnmsmR13RCRV1PZfdN@lists.linux.dev X-Gm-Message-State: AOJu0Ywhvmrrt+iy7m7DiM/Nvq4AYq00F1sJE76yLZFsFf8xpe/FUqmc YTDXvzN26plT+jmKRtvgfc/nO13eNyRmFqK82fUohYpAy9JUaGyc2/TL X-Gm-Gg: ASbGncuCktMUugMPrLlwyhP6noe3F2SbDpbV5EEqM7L33QJiFhQBu6IsMaCHMqrxYW2 RkTEC5W5aMM7VqhYeUTPT8vrdFYTVtOAB76a2PvYQNaPLtmFhs7hY6xd8R1NwEiEyAqYg4b5klS 8C8p6AubS/KhXKsZcE+v1Hyk+iMUD6izRjI6w1aM1HJT5bbkqPFf+DALkCQhU4l2h+urgDFd8uI HH0ueKU86+oLRHxiPevXoz3qx4Leow/6iurCltJoP885lZ+om8Z5KY2oGP8unvpc/0fGgUgQLN1 Y8JNdvciZvXlVbH0TS9lpaCybB/HUm+vhKu1w2bOj+j8eqJ7r0W4fVEY6kFLRihOVBG0nz0Y/ll ykmY4qycowxtXcHqCx1cDVNsKnjfPUdzUd2Jp+kJUdS1DUO2B4N3zUhJPX64b8VB7OVh0YtatNN e2V0Z5sBdp45o= X-Google-Smtp-Source: AGHT+IFy53cLbESiXML1MJ4hrBJswXxa/BoGl5z6KQzHvzuNLRuKNd9j4dRr8DLhGkrFztEpABmyuA== X-Received: by 2002:a05:6a21:999a:b0:361:2fff:7b19 with SMTP id adf61e73a8af0-3614ee0a140mr34179763637.52.1764407726448; Sat, 29 Nov 2025 01:15:26 -0800 (PST) Received: from fedora ([2405:201:3017:184:2d1c:8c4c:2945:3f7c]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7d15e7db416sm7300563b3a.41.2025.11.29.01.15.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Nov 2025 01:15:25 -0800 (PST) From: Bhavik Sachdev To: Alexander Viro , Christian Brauner , Shuah Khan Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, criu@lists.linux.dev, Jan Kara , Jeff Layton , Aleksa Sarai , Miklos Szeredi , Bhavik Sachdev , Pavel Tikhomirov , Andrei Vagin , Alexander Mikhalitsyn , John Hubbard , Amir Goldstein , "Martin K . Petersen" , Andrew Donnellan Subject: [PATCH v7 2/3] statmount: accept fd as a parameter Date: Sat, 29 Nov 2025 14:41:21 +0530 Message-ID: <20251129091455.757724-3-b.sachdev1904@gmail.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251129091455.757724-1-b.sachdev1904@gmail.com> References: <20251129091455.757724-1-b.sachdev1904@gmail.com> Precedence: bulk X-Mailing-List: criu@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Extend `struct mnt_id_req` to take in a fd and introduce STATMOUNT_BY_FD flag. When a valid fd is provided and STATMOUNT_BY_FD is set, statmount will return mountinfo about the mount the fd is on. This even works for "unmounted" mounts (mounts that have been umounted using umount2(mnt, MNT_DETACH)), if you have access to a file descriptor on that mount. These "umounted" mounts will have no mountpoint and no valid mount namespace. Hence, we unset the STATMOUNT_MNT_POINT and STATMOUNT_MNT_NS_ID in statmount.mask for "unmounted" mounts. In case of STATMOUNT_BY_FD, given that we already have access to an fd on the mount, accessing mount information without a capability check seems fine because of the following reasons: - All fs related information is available via fstatfs() without any capability check. - Mount information is also available via /proc/pid/mountinfo (without any capability check). - Given that we have access to a fd on the mount which tells us that we had access to the mount at some point (or someone that had access gave us the fd). So, we should be able to access mount info. Co-developed-by: Pavel Tikhomirov Signed-off-by: Pavel Tikhomirov Signed-off-by: Bhavik Sachdev --- fs/namespace.c | 102 ++++++++++++++++++++++++------------- include/uapi/linux/mount.h | 10 +++- 2 files changed, 76 insertions(+), 36 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index ee36d67f1ac2..73ffa1fbdad7 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -5563,31 +5563,49 @@ static int grab_requested_root(struct mnt_namespace *ns, struct path *root) /* locks: namespace_shared */ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id, - struct mnt_namespace *ns) + struct file *mnt_file, struct mnt_namespace *ns) { - struct mount *m; int err; - /* Has the namespace already been emptied? */ - if (mnt_ns_id && mnt_ns_empty(ns)) - return -ENOENT; + if (mnt_file) { + WARN_ON_ONCE(ns != NULL); - s->mnt = lookup_mnt_in_ns(mnt_id, ns); - if (!s->mnt) - return -ENOENT; + s->mnt = mnt_file->f_path.mnt; + ns = real_mount(s->mnt)->mnt_ns; + if (!ns) + /* + * We can't set mount point and mnt_ns_id since we don't have a + * ns for the mount. This can happen if the mount is unmounted + * with MNT_DETACH. + */ + s->mask &= ~(STATMOUNT_MNT_POINT | STATMOUNT_MNT_NS_ID); + } else { + /* Has the namespace already been emptied? */ + if (mnt_ns_id && mnt_ns_empty(ns)) + return -ENOENT; - err = grab_requested_root(ns, &s->root); - if (err) - return err; + s->mnt = lookup_mnt_in_ns(mnt_id, ns); + if (!s->mnt) + return -ENOENT; + } - /* - * Don't trigger audit denials. We just want to determine what - * mounts to show users. - */ - m = real_mount(s->mnt); - if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) && - !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) - return -EPERM; + if (ns) { + err = grab_requested_root(ns, &s->root); + if (err) + return err; + + if (!mnt_file) { + struct mount *m; + /* + * Don't trigger audit denials. We just want to determine what + * mounts to show users. + */ + m = real_mount(s->mnt); + if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) && + !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + } + } err = security_sb_statfs(s->mnt->mnt_root); if (err) @@ -5709,7 +5727,7 @@ static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq, } static int copy_mnt_id_req(const struct mnt_id_req __user *req, - struct mnt_id_req *kreq) + struct mnt_id_req *kreq, unsigned int flags) { int ret; size_t usize; @@ -5727,11 +5745,17 @@ static int copy_mnt_id_req(const struct mnt_id_req __user *req, ret = copy_struct_from_user(kreq, sizeof(*kreq), req, usize); if (ret) return ret; - if (kreq->mnt_ns_fd != 0 && kreq->mnt_ns_id) - return -EINVAL; - /* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */ - if (kreq->mnt_id <= MNT_UNIQUE_ID_OFFSET) - return -EINVAL; + + if (flags & STATMOUNT_BY_FD) { + if (kreq->mnt_id || kreq->mnt_ns_id) + return -EINVAL; + } else { + if (kreq->mnt_ns_fd != 0 && kreq->mnt_ns_id) + return -EINVAL; + /* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */ + if (kreq->mnt_id <= MNT_UNIQUE_ID_OFFSET) + return -EINVAL; + } return 0; } @@ -5777,25 +5801,33 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req, { struct mnt_namespace *ns __free(mnt_ns_release) = NULL; struct kstatmount *ks __free(kfree) = NULL; + struct file *mnt_file __free(fput) = NULL; struct mnt_id_req kreq; /* We currently support retrieval of 3 strings. */ size_t seq_size = 3 * PATH_MAX; int ret; - if (flags) + if (flags & ~STATMOUNT_BY_FD) return -EINVAL; - ret = copy_mnt_id_req(req, &kreq); + ret = copy_mnt_id_req(req, &kreq, flags); if (ret) return ret; - ns = grab_requested_mnt_ns(&kreq); - if (IS_ERR(ns)) - return PTR_ERR(ns); + if (flags & STATMOUNT_BY_FD) { + mnt_file = fget_raw(kreq.mnt_fd); + if (!mnt_file) + return -EBADF; + /* do_statmount sets ns in case of STATMOUNT_BY_FD */ + } else { + ns = grab_requested_mnt_ns(&kreq); + if (IS_ERR(ns)) + return PTR_ERR(ns); - if (kreq.mnt_ns_id && (ns != current->nsproxy->mnt_ns) && - !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) - return -EPERM; + if (kreq.mnt_ns_id && (ns != current->nsproxy->mnt_ns) && + !ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + } ks = kmalloc(sizeof(*ks), GFP_KERNEL_ACCOUNT); if (!ks) @@ -5807,7 +5839,7 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req, return ret; scoped_guard(namespace_shared) - ret = do_statmount(ks, kreq.mnt_id, kreq.mnt_ns_id, ns); + ret = do_statmount(ks, kreq.mnt_id, kreq.mnt_ns_id, mnt_file, ns); if (!ret) ret = copy_statmount_to_user(ks); @@ -5947,7 +5979,7 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req, if (!access_ok(mnt_ids, nr_mnt_ids * sizeof(*mnt_ids))) return -EFAULT; - ret = copy_mnt_id_req(req, &kreq); + ret = copy_mnt_id_req(req, &kreq, 0); if (ret) return ret; diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h index 5d3f8c9e3a62..18c624405268 100644 --- a/include/uapi/linux/mount.h +++ b/include/uapi/linux/mount.h @@ -197,7 +197,10 @@ struct statmount { */ struct mnt_id_req { __u32 size; - __u32 mnt_ns_fd; + union { + __u32 mnt_ns_fd; + __u32 mnt_fd; + }; __u64 mnt_id; __u64 param; __u64 mnt_ns_id; @@ -232,4 +235,9 @@ struct mnt_id_req { #define LSMT_ROOT 0xffffffffffffffff /* root mount */ #define LISTMOUNT_REVERSE (1 << 0) /* List later mounts first */ +/* + * @flag bits for statmount(2) + */ +#define STATMOUNT_BY_FD 0x00000001U /* want mountinfo for given fd */ + #endif /* _UAPI_LINUX_MOUNT_H */ -- 2.52.0