From nobody Fri May 17 01:44:16 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=google.com ARC-Seal: i=1; a=rsa-sha256; t=1681176623; cv=none; d=zohomail.com; s=zohoarc; b=fsNKptxX7nRTO7f6i6Zq6qOc0OcqiI8v5WJQfnmsjdfLzMMsyvXXIapoVC+bo1IIZgt+v5aXzsM+VzVmb7FF8ywXuXsLCkPmqikUmhIGZyjtoFxPSLEzt2nMMtysNn0NG0yFuvH7C7ecBLKke57hYTFqwY8xISMY8QveP3PQt60= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1681176623; h=Content-Type:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=jBCdWjTPDV7u7tywdkOEhqA30FFTo51cVFS76tLVdIk=; b=hcXGeGiFRYmMDMb5WIDvSK23hGOTfB+z67+f3x84/eYuThj/P+6UC1doifoWsZNAxGyim8bNA7KwwN81nhitQR6LLDCzArt14pP4/YeuLN57Rw2EOnLsC9DPsTHw9Ls5gv7MycAqmi1gZ+g3qZPPkATNLc3Q/kaq7dgu2dVd0YM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1681176623210730.508661574572; Mon, 10 Apr 2023 18:30:23 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pm2pM-0005fs-6C; Mon, 10 Apr 2023 21:29:44 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3Arg0ZAsKCm0LNVPcWPjeYRRZZRWP.NZXbPXf-OPgPWYZYRYf.ZcR@flex--ackerleytng.bounces.google.com>) id 1pm2pK-0005eK-7g for qemu-devel@nongnu.org; Mon, 10 Apr 2023 21:29:42 -0400 Received: from mail-pj1-x104a.google.com ([2607:f8b0:4864:20::104a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3Arg0ZAsKCm0LNVPcWPjeYRRZZRWP.NZXbPXf-OPgPWYZYRYf.ZcR@flex--ackerleytng.bounces.google.com>) id 1pm2pI-0006kB-BJ for qemu-devel@nongnu.org; Mon, 10 Apr 2023 21:29:42 -0400 Received: by mail-pj1-x104a.google.com with SMTP id 98e67ed59e1d1-24677634ef3so226080a91.2 for ; Mon, 10 Apr 2023 18:29:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1681176578; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=jBCdWjTPDV7u7tywdkOEhqA30FFTo51cVFS76tLVdIk=; b=XXnjZgXzwtwzC9wPP7936MOH1Bh6PiD2hqfXsgLbzkeGH7jGOqDvWazEmiyAzxPEXN HOXn2bdQRnFFlnJUMCqgw4px9COwSYMyn3YVtfmzMWvKtYBIfUSMShHDLtLIFs/2YTUR ZIlb/4bJS9MEL7swsNJzi4YAZKTAsbO10KC0Ds2cNLC22Pg9HmCMPpjEwtsK0gp8xNc3 mZO89Ov6XW97TbXx3Qad8uCoGve6ai+6KRHCABfhvY1sO8p+sNfAEeAPFhpBXQq5q71+ Ao9iCqyyDHst/xqAqkZjIbwaUW4f94Yk0iTSASX0LtcD2fSwJLyo8HMt+YM3EG3m5HrO XxOw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1681176578; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=jBCdWjTPDV7u7tywdkOEhqA30FFTo51cVFS76tLVdIk=; b=BP/GUnbRA9NvuyFfOatNkCxGmHwUfwrH1P5h8jR3+ykxN1KlIaRBmXxty2XhfI38yb XD9uy4qGdXaNeeGkRYE8ETkQKbndqoAAKAFaRU6/oR2wz08OrcTUeA9/+k9CDb8+drjD 1cl6F0tachkcCmP/gZFEKAR4ArLTx698GG1uLQSr5LJKpSGNa3cmPKBJBS0mileTn4bQ kU94q2GWT448AzQQx2o3QT4G3lFkATOVWmFKGkJqTQv1eFtGEE2GtMb/pT/TEuJe34IB lp6DqyFoKokcsbd6+L8GydZbqxH7rIT5lAhdr4q/M0rxuIhBBlOU6FLhFNLs3NqAGgzg J9sw== X-Gm-Message-State: AAQBX9fxzqoB7gvIBMhmL12XV8ouxkEXcOFSfwKKtyJ+2y3PLdxRFPcx +EfFQL9M+mU9LkE2Ar0yfp1Bx0sSWVodiKxGvg== X-Google-Smtp-Source: AKy350aUc53Va1zkms/tZeMPTaKwPOXFpbnBKZFuGedztjHPQD03PotiB50/sPjZNhzfTmekJxf45Wmqylq6+Y7Cgw== X-Received: from ackerleytng-cloudtop.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:1f5f]) (user=ackerleytng job=sendgmr) by 2002:a05:6a00:a1b:b0:62d:dade:825 with SMTP id p27-20020a056a000a1b00b0062ddade0825mr6152408pfh.3.1681176578151; Mon, 10 Apr 2023 18:29:38 -0700 (PDT) Date: Tue, 11 Apr 2023 01:29:32 +0000 In-Reply-To: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.40.0.577.gac1e443424-goog Message-ID: Subject: [RFC PATCH v4 1/2] mm: restrictedmem: Allow userspace to specify mount for memfd_restricted From: Ackerley Tng To: kvm@vger.kernel.org, linux-api@vger.kernel.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, qemu-devel@nongnu.org Cc: aarcange@redhat.com, ak@linux.intel.com, akpm@linux-foundation.org, arnd@arndb.de, bfields@fieldses.org, bp@alien8.de, chao.p.peng@linux.intel.com, corbet@lwn.net, dave.hansen@intel.com, david@redhat.com, ddutile@redhat.com, dhildenb@redhat.com, hpa@zytor.com, hughd@google.com, jlayton@kernel.org, jmattson@google.com, joro@8bytes.org, jun.nakajima@intel.com, kirill.shutemov@linux.intel.com, linmiaohe@huawei.com, luto@kernel.org, mail@maciej.szmigiero.name, mhocko@suse.com, michael.roth@amd.com, mingo@redhat.com, naoya.horiguchi@nec.com, pbonzini@redhat.com, qperret@google.com, rppt@kernel.org, seanjc@google.com, shuah@kernel.org, steven.price@arm.com, tabba@google.com, tglx@linutronix.de, vannapurve@google.com, vbabka@suse.cz, vkuznets@redhat.com, wanpengli@tencent.com, wei.w.wang@intel.com, x86@kernel.org, yu.c.zhang@linux.intel.com, Ackerley Tng Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::104a; envelope-from=3Arg0ZAsKCm0LNVPcWPjeYRRZZRWP.NZXbPXf-OPgPWYZYRYf.ZcR@flex--ackerleytng.bounces.google.com; helo=mail-pj1-x104a.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @google.com) X-ZM-MESSAGEID: 1681176623987100003 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" By default, the backing shmem file for a restrictedmem fd is created on shmem's kernel space mount. With this patch, an optional tmpfs mount can be specified via an fd, which will be used as the mountpoint for backing the shmem file associated with a restrictedmem fd. This will help restrictedmem fds inherit the properties of the provided tmpfs mounts, for example, hugepage (THP) allocation hints, NUMA binding hints, etc. Permissions for the fd passed to memfd_restricted() is modeled after the openat() syscall, since both of these allow creation of a file upon a mount/directory. Permission to reference the mount the fd represents is checked upon fd creation by other syscalls (e.g. fsmount(), open(), or open_tree(), etc) and any process that can present memfd_restricted() with a valid fd is expected to have obtained permission to use the mount represented by the fd. This behavior is intended to parallel that of the openat() syscall. memfd_restricted() will check that the tmpfs superblock is writable, and that the mount is also writable, before attempting to create a restrictedmem file on the mount. Signed-off-by: Ackerley Tng --- include/linux/syscalls.h | 2 +- include/uapi/linux/restrictedmem.h | 8 ++++ mm/restrictedmem.c | 73 ++++++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 include/uapi/linux/restrictedmem.h diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 660be0bf89d5..90c73b9e14e5 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1058,7 +1058,7 @@ asmlinkage long sys_memfd_secret(unsigned int flags); asmlinkage long sys_set_mempolicy_home_node(unsigned long start, unsigned = long len, unsigned long home_node, unsigned long flags); -asmlinkage long sys_memfd_restricted(unsigned int flags); +asmlinkage long sys_memfd_restricted(unsigned int flags, int mount_fd); =20 /* * Architecture-specific system calls diff --git a/include/uapi/linux/restrictedmem.h b/include/uapi/linux/restri= ctedmem.h new file mode 100644 index 000000000000..73e31bce73dc --- /dev/null +++ b/include/uapi/linux/restrictedmem.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_RESTRICTEDMEM_H +#define _UAPI_LINUX_RESTRICTEDMEM_H + +/* flags for memfd_restricted */ +#define MEMFD_RSTD_USERMNT 0x0001U + +#endif /* _UAPI_LINUX_RESTRICTEDMEM_H */ diff --git a/mm/restrictedmem.c b/mm/restrictedmem.c index 55e99e6c09a1..032ad1f15138 100644 --- a/mm/restrictedmem.c +++ b/mm/restrictedmem.c @@ -6,6 +6,7 @@ #include #include #include +#include #include =20 struct restrictedmem { @@ -250,19 +251,20 @@ static struct address_space_operations restricted_aop= s =3D { #endif }; =20 -SYSCALL_DEFINE1(memfd_restricted, unsigned int, flags) +static int restrictedmem_create(struct vfsmount *mount) { struct file *file, *restricted_file; int fd, err; =20 - if (flags) - return -EINVAL; - fd =3D get_unused_fd_flags(0); if (fd < 0) return fd; =20 - file =3D shmem_file_setup("memfd:restrictedmem", 0, VM_NORESERVE); + if (mount) + file =3D shmem_file_setup_with_mnt(mount, "memfd:restrictedmem", 0, VM_N= ORESERVE); + else + file =3D shmem_file_setup("memfd:restrictedmem", 0, VM_NORESERVE); + if (IS_ERR(file)) { err =3D PTR_ERR(file); goto err_fd; @@ -286,6 +288,67 @@ SYSCALL_DEFINE1(memfd_restricted, unsigned int, flags) return err; } =20 +static struct vfsmount *restrictedmem_get_user_mount(struct file *file) +{ + int ret; + struct vfsmount *mnt; + struct path *path; + + path =3D &file->f_path; + if (path->dentry !=3D path->mnt->mnt_root) + return ERR_PTR(-EINVAL); + + /* + * Disallow bind-mounts that aren't bind-mounts of the whole + * filesystem + */ + mnt =3D path->mnt; + if (mnt->mnt_root !=3D mnt->mnt_sb->s_root) + return ERR_PTR(-EINVAL); + + if (mnt->mnt_sb->s_magic !=3D TMPFS_MAGIC) + return ERR_PTR(-EINVAL); + + ret =3D mnt_want_write(mnt); + if (ret) + return ERR_PTR(ret); + + return mnt; +} + +SYSCALL_DEFINE2(memfd_restricted, unsigned int, flags, int, mount_fd) +{ + int ret; + struct fd f =3D {}; + struct vfsmount *mnt =3D NULL; + + if (flags & ~MEMFD_RSTD_USERMNT) + return -EINVAL; + + if (flags & MEMFD_RSTD_USERMNT) { + f =3D fdget_raw(mount_fd); + if (!f.file) + return -EBADF; + + mnt =3D restrictedmem_get_user_mount(f.file); + if (IS_ERR(mnt)) { + ret =3D PTR_ERR(mnt); + goto out; + } + } + + ret =3D restrictedmem_create(mnt); + + if (mnt) + mnt_drop_write(mnt); + +out: + if (f.file) + fdput(f); + + return ret; +} + int restrictedmem_bind(struct file *file, pgoff_t start, pgoff_t end, struct restrictedmem_notifier *notifier, bool exclusive) { --=20 2.40.0.577.gac1e443424-goog From nobody Fri May 17 01:44:16 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=google.com ARC-Seal: i=1; a=rsa-sha256; t=1681176605; cv=none; d=zohomail.com; s=zohoarc; b=Cc8+e0h46SHTPRbq+kVABTNOU15x08O1AvW0m4oAdpAoAxGPfca5ow7Y7qL01Oqi2KFiYTDMX/hhuC6Thg7BO1Ir5+r/tSesLkq7GSbLv9HjO6PTs7NUr+AnYEFfvYj8+Gad+mEcBcVTy1I0qUqLMIP/B8rOoNcomGPemdZP9vY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1681176605; h=Content-Type:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=SUMdFa7+PPMn99YcGlnAfohLLYbWjlEJWXPRhdQ3G0w=; b=etOw/a8IK07LBrVW96P9Ta82D7s9cAXWcsZFm6hixrxFn4AGOd9+DAXgkNJEUaA0sKI+2Krt8/yZRuR1nQmXNXGM8U0ygRspJgz5XnL9XeQ9Qm1EZBySajnBVArWKvS5aBHTt96b6evy5vSFtNj8nRN/+GG/49c+tR7/kXflNlw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1681176605957168.1279799205073; Mon, 10 Apr 2023 18:30:05 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pm2pM-0005gF-RC; Mon, 10 Apr 2023 21:29:44 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3A7g0ZAsKCm4MOWQdXQkfZSSaaSXQ.OaYcQYg-PQhQXZaZSZg.adS@flex--ackerleytng.bounces.google.com>) id 1pm2pL-0005fl-SJ for qemu-devel@nongnu.org; Mon, 10 Apr 2023 21:29:43 -0400 Received: from mail-yw1-x114a.google.com ([2607:f8b0:4864:20::114a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3A7g0ZAsKCm4MOWQdXQkfZSSaaSXQ.OaYcQYg-PQhQXZaZSZg.adS@flex--ackerleytng.bounces.google.com>) id 1pm2pJ-0006kq-G7 for qemu-devel@nongnu.org; Mon, 10 Apr 2023 21:29:43 -0400 Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54ee397553eso70505587b3.19 for ; Mon, 10 Apr 2023 18:29:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; t=1681176580; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=SUMdFa7+PPMn99YcGlnAfohLLYbWjlEJWXPRhdQ3G0w=; b=O5jPzmxvpGvZW7A15nBNKVb9+WZg0AxtgKYK/czsKaQti101N5gb12ZgJwZ79KOqCf /7nLUbSt/ecPSttKIZWx6hQc09RPO2HLTAVpaRmG4dxWAxUD1XW4CPBxQwX91bGClxCM n9SwpkbUTkagt1qddJ5g+Zlprvg9Qx83RwrnKQyRHDiuW/iPXyr64Ji+vFI/hXtaItGe bKQE6xEnoGEp/6S1qqA8RVOleosKH8GoiYUjrxCrkffvfodFdsATKBc1Df5vA9mW7O0G wEsFs+d31y4Hn8W1fVVU7JSdUXy353YUcUPYXkJGfyphlSLdBo0eGJEp/ecFM45BsrMI v+pQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1681176580; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=SUMdFa7+PPMn99YcGlnAfohLLYbWjlEJWXPRhdQ3G0w=; b=KsoqzrFjyAXRai0ymuvdbJJCG7+w5khB/BXtRXerB/5x260qIk5hdDxWfsRCsRHEve ToxpyIBlp4ifH7mt1Hr0kPKRstqXAabb5fUPuhptPZWGMFGqHgig8PtCiB5C/kI1P6rm jXnDfcMtjw8OSIMbwN69tjRz4UqfbSJGVfgW2d8LhxWrQWsWDKbO1LRjr53DtMIHFJTg 30kWaTEmdJr6oQ8z3j6fe9imZfuth+upBSwBWfDZOBajYX+EhvbNRdXhLXgMd4rwfKxS UolwDKsbZPUzrBkHgQyc44iMdWPjOBvEXZX1E81UbcYwnhI5Vtz/eTqNUiDcKYwdggST egRw== X-Gm-Message-State: AAQBX9drBHp2R9jXWcVIoI/Ti92t6lEoDZ+kv9DstR7rXOR8fDcPzeGv LVfkjEeos0/ZSCNQ/80ZBJSykALIwtrW2cn3bQ== X-Google-Smtp-Source: AKy350aWx1LEWUOf1FrnrMDEm7YGoW5Jf5E4LFqJ8ZnxfEUyPVRZ2iMsx9R9r5E7bVHQTgok7giMc8dxwwldHsZMKw== X-Received: from ackerleytng-cloudtop.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:1f5f]) (user=ackerleytng job=sendgmr) by 2002:a05:6902:909:b0:a27:3ecc:ffe7 with SMTP id bu9-20020a056902090900b00a273eccffe7mr10712313ybb.3.1681176579771; Mon, 10 Apr 2023 18:29:39 -0700 (PDT) Date: Tue, 11 Apr 2023 01:29:33 +0000 In-Reply-To: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.40.0.577.gac1e443424-goog Message-ID: <2a733e3ed673c3b9d6b1a5fcb6625953da042f42.1681176340.git.ackerleytng@google.com> Subject: [RFC PATCH v4 2/2] selftests: restrictedmem: Check memfd_restricted()'s handling of provided userspace mount From: Ackerley Tng To: kvm@vger.kernel.org, linux-api@vger.kernel.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, qemu-devel@nongnu.org Cc: aarcange@redhat.com, ak@linux.intel.com, akpm@linux-foundation.org, arnd@arndb.de, bfields@fieldses.org, bp@alien8.de, chao.p.peng@linux.intel.com, corbet@lwn.net, dave.hansen@intel.com, david@redhat.com, ddutile@redhat.com, dhildenb@redhat.com, hpa@zytor.com, hughd@google.com, jlayton@kernel.org, jmattson@google.com, joro@8bytes.org, jun.nakajima@intel.com, kirill.shutemov@linux.intel.com, linmiaohe@huawei.com, luto@kernel.org, mail@maciej.szmigiero.name, mhocko@suse.com, michael.roth@amd.com, mingo@redhat.com, naoya.horiguchi@nec.com, pbonzini@redhat.com, qperret@google.com, rppt@kernel.org, seanjc@google.com, shuah@kernel.org, steven.price@arm.com, tabba@google.com, tglx@linutronix.de, vannapurve@google.com, vbabka@suse.cz, vkuznets@redhat.com, wanpengli@tencent.com, wei.w.wang@intel.com, x86@kernel.org, yu.c.zhang@linux.intel.com, Ackerley Tng Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::114a; envelope-from=3A7g0ZAsKCm4MOWQdXQkfZSSaaSXQ.OaYcQYg-PQhQXZaZSZg.adS@flex--ackerleytng.bounces.google.com; helo=mail-yw1-x114a.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @google.com) X-ZM-MESSAGEID: 1681176607211100005 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" For memfd_restricted() calls without a userspace mount, the backing file should be the shmem mount in the kernel, and the size of backing pages should be as defined by system-wide shmem configuration. If a userspace mount is provided, the size of backing pages should be as defined in the mount. Also includes negative tests for invalid inputs, including fds representing read-only superblocks/mounts. Signed-off-by: Ackerley Tng --- tools/testing/selftests/mm/.gitignore | 1 + tools/testing/selftests/mm/Makefile | 1 + .../selftests/mm/memfd_restricted_usermnt.c | 529 ++++++++++++++++++ tools/testing/selftests/mm/run_vmtests.sh | 3 + 4 files changed, 534 insertions(+) create mode 100644 tools/testing/selftests/mm/memfd_restricted_usermnt.c diff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftest= s/mm/.gitignore index fb6e4233374d..dba320c8151a 100644 --- a/tools/testing/selftests/mm/.gitignore +++ b/tools/testing/selftests/mm/.gitignore @@ -31,6 +31,7 @@ map_fixed_noreplace write_to_hugetlbfs hmm-tests memfd_restricted +memfd_restricted_usermnt memfd_secret soft-dirty split_huge_page_test diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/= mm/Makefile index 5ec338ea1fed..2f5df7a12ea5 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -46,6 +46,7 @@ TEST_GEN_FILES +=3D map_fixed_noreplace TEST_GEN_FILES +=3D map_hugetlb TEST_GEN_FILES +=3D map_populate TEST_GEN_FILES +=3D memfd_restricted +TEST_GEN_FILES +=3D memfd_restricted_usermnt TEST_GEN_FILES +=3D memfd_secret TEST_GEN_FILES +=3D migration TEST_GEN_FILES +=3D mlock-random-test diff --git a/tools/testing/selftests/mm/memfd_restricted_usermnt.c b/tools/= testing/selftests/mm/memfd_restricted_usermnt.c new file mode 100644 index 000000000000..0be04e3d714d --- /dev/null +++ b/tools/testing/selftests/mm/memfd_restricted_usermnt.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define _GNU_SOURCE /* for O_PATH */ +#define _POSIX_C_SOURCE /* for PATH_MAX */ +#include +#include +#include +#include + +#include "linux/restrictedmem.h" + +#include "../kselftest_harness.h" + +static int memfd_restricted(unsigned int flags, int fd) +{ + return syscall(__NR_memfd_restricted, flags, fd); +} + +static int get_hpage_pmd_size(void) +{ + FILE *fp; + char buf[100]; + char *ret; + int size; + + fp =3D fopen("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", "r"); + if (!fp) + return -1; + + ret =3D fgets(buf, 100, fp); + if (ret !=3D buf) { + size =3D -1; + goto out; + } + + if (sscanf(buf, "%d\n", &size) !=3D 1) + size =3D -1; + +out: + fclose(fp); + + return size; +} + +static int write_string_to_file(const char *path, const char *string) +{ + FILE *fp; + size_t len =3D strlen(string); + int ret =3D -1; + + fp =3D fopen(path, "w"); + if (!fp) + return ret; + + if (fwrite(string, 1, len, fp) !=3D len) + goto out; + + ret =3D 0; + +out: + fclose(fp); + return ret; +} + +/* + * Expect shmem thp policy to be one of always, within_size, advise, never, + * deny, force + */ +#define POLICY_BUF_SIZE 12 + +static bool is_valid_shmem_thp_policy(char *policy) +{ + if (strcmp(policy, "always") =3D=3D 0) + return true; + if (strcmp(policy, "within_size") =3D=3D 0) + return true; + if (strcmp(policy, "advise") =3D=3D 0) + return true; + if (strcmp(policy, "never") =3D=3D 0) + return true; + if (strcmp(policy, "deny") =3D=3D 0) + return true; + if (strcmp(policy, "force") =3D=3D 0) + return true; + + return false; +} + +static int get_shmem_thp_policy(char *policy) +{ + FILE *fp; + char buf[100]; + char *left =3D NULL; + char *right =3D NULL; + int ret =3D -1; + + fp =3D fopen("/sys/kernel/mm/transparent_hugepage/shmem_enabled", "r"); + if (!fp) + return -1; + + if (fgets(buf, 100, fp) !=3D buf) + goto out; + + /* + * Expect shmem_enabled to be of format like "always within_size advise + * [never] deny force" + */ + left =3D memchr(buf, '[', 100); + if (!left) + goto out; + + right =3D memchr(buf, ']', 100); + if (!right) + goto out; + + memcpy(policy, left + 1, right - left - 1); + + ret =3D !is_valid_shmem_thp_policy(policy); + +out: + fclose(fp); + return ret; +} + +static int set_shmem_thp_policy(char *policy) +{ + int ret =3D -1; + /* +1 for newline */ + char to_write[POLICY_BUF_SIZE + 1] =3D { 0 }; + + if (!is_valid_shmem_thp_policy(policy)) + return ret; + + ret =3D snprintf(to_write, POLICY_BUF_SIZE + 1, "%s\n", policy); + if (ret !=3D strlen(policy) + 1) + return -1; + + ret =3D write_string_to_file( + "/sys/kernel/mm/transparent_hugepage/shmem_enabled", to_write); + + return ret; +} + +FIXTURE(reset_shmem_enabled) +{ + char shmem_enabled[POLICY_BUF_SIZE]; +}; + +FIXTURE_SETUP(reset_shmem_enabled) +{ + memset(self->shmem_enabled, 0, POLICY_BUF_SIZE); + ASSERT_EQ(get_shmem_thp_policy(self->shmem_enabled), 0); +} + +FIXTURE_TEARDOWN(reset_shmem_enabled) +{ + ASSERT_EQ(set_shmem_thp_policy(self->shmem_enabled), 0); +} + +TEST_F(reset_shmem_enabled, restrictedmem_fstat_shmem_enabled_never) +{ + int fd =3D -1; + struct stat stat; + + ASSERT_EQ(set_shmem_thp_policy("never"), 0); + + fd =3D memfd_restricted(0, -1); + ASSERT_GT(fd, 0); + + ASSERT_EQ(fstat(fd, &stat), 0); + + /* + * st_blksize is set based on the superblock's s_blocksize_bits. For + * shmem, this is set to PAGE_SHIFT + */ + ASSERT_EQ(stat.st_blksize, getpagesize()); + + close(fd); +} + +TEST_F(reset_shmem_enabled, restrictedmem_fstat_shmem_enabled_always) +{ + int fd =3D -1; + struct stat stat; + + ASSERT_EQ(set_shmem_thp_policy("always"), 0); + + fd =3D memfd_restricted(0, -1); + ASSERT_GT(fd, 0); + + ASSERT_EQ(fstat(fd, &stat), 0); + + ASSERT_EQ(stat.st_blksize, get_hpage_pmd_size()); + + close(fd); +} + +TEST(restrictedmem_tmpfile_invalid_fd) +{ + int fd =3D memfd_restricted(MEMFD_RSTD_USERMNT, -2); + + ASSERT_EQ(fd, -1); + ASSERT_EQ(errno, EBADF); +} + +TEST(restrictedmem_tmpfile_fd_not_a_mount) +{ + int fd =3D memfd_restricted(MEMFD_RSTD_USERMNT, STDOUT_FILENO); + + ASSERT_EQ(fd, -1); + ASSERT_EQ(errno, EINVAL); +} + +TEST(restrictedmem_tmpfile_not_tmpfs_mount) +{ + int fd =3D -1; + int mfd =3D -1; + + mfd =3D open("/proc", O_PATH); + ASSERT_NE(mfd, -1); + + fd =3D memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + + ASSERT_EQ(fd, -1); + ASSERT_EQ(errno, EINVAL); +} + +FIXTURE(tmpfs_sfd) +{ + int sfd; +}; + +FIXTURE_SETUP(tmpfs_sfd) +{ + self->sfd =3D fsopen("tmpfs", 0); + ASSERT_NE(self->sfd, -1); +} + +FIXTURE_TEARDOWN(tmpfs_sfd) +{ + EXPECT_EQ(close(self->sfd), 0); +} + +TEST_F(tmpfs_sfd, restrictedmem_fstat_tmpfs_huge_always) +{ + int ret =3D -1; + int fd =3D -1; + int mfd =3D -1; + struct stat stat; + + fsconfig(self->sfd, FSCONFIG_SET_STRING, "huge", "always", 0); + fsconfig(self->sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); + + mfd =3D fsmount(self->sfd, 0, 0); + ASSERT_NE(mfd, -1); + + fd =3D memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_GT(fd, 0); + + /* User can close reference to mount */ + ret =3D close(mfd); + ASSERT_EQ(ret, 0); + + ret =3D fstat(fd, &stat); + ASSERT_EQ(ret, 0); + ASSERT_EQ(stat.st_blksize, get_hpage_pmd_size()); + + close(fd); +} + +TEST_F(tmpfs_sfd, restrictedmem_fstat_tmpfs_huge_never) +{ + int ret =3D -1; + int fd =3D -1; + int mfd =3D -1; + struct stat stat; + + fsconfig(self->sfd, FSCONFIG_SET_STRING, "huge", "never", 0); + fsconfig(self->sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); + + mfd =3D fsmount(self->sfd, 0, 0); + ASSERT_NE(mfd, -1); + + fd =3D memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_GT(fd, 0); + + /* User can close reference to mount */ + ret =3D close(mfd); + ASSERT_EQ(ret, 0); + + ret =3D fstat(fd, &stat); + ASSERT_EQ(ret, 0); + ASSERT_EQ(stat.st_blksize, getpagesize()); + + close(fd); +} + +TEST_F(tmpfs_sfd, restrictedmem_check_mount_flags) +{ + int ret =3D -1; + int fd =3D -1; + int mfd =3D -1; + + fsconfig(self->sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); + + mfd =3D fsmount(self->sfd, 0, MOUNT_ATTR_RDONLY); + ASSERT_NE(mfd, -1); + + fd =3D memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_EQ(fd, -1); + ASSERT_EQ(errno, EROFS); + + ret =3D close(mfd); + ASSERT_EQ(ret, 0); +} + +TEST_F(tmpfs_sfd, restrictedmem_check_superblock_flags) +{ + int ret =3D -1; + int fd =3D -1; + int mfd =3D -1; + + fsconfig(self->sfd, FSCONFIG_SET_FLAG, "ro", NULL, 0); + fsconfig(self->sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); + + mfd =3D fsmount(self->sfd, 0, 0); + ASSERT_NE(mfd, -1); + + fd =3D memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_EQ(fd, -1); + ASSERT_EQ(errno, EROFS); + + ret =3D close(mfd); + ASSERT_EQ(ret, 0); +} + +static bool directory_exists(const char *path) +{ + struct stat sb; + + return stat(path, &sb) =3D=3D 0 && S_ISDIR(sb.st_mode); +} + +FIXTURE(restrictedmem_test_mount_path) +{ + char *mount_path; +}; + +FIXTURE_SETUP(restrictedmem_test_mount_path) +{ + int ret =3D -1; + + /* /tmp is an FHS-mandated world-writable directory */ + self->mount_path =3D "/tmp/restrictedmem-selftest-mnt"; + + if (!directory_exists(self->mount_path)) { + ret =3D mkdir(self->mount_path, 0777); + ASSERT_EQ(ret, 0); + } +} + +FIXTURE_TEARDOWN(restrictedmem_test_mount_path) +{ + int ret =3D -1; + + if (!directory_exists(self->mount_path)) + return; + + ret =3D umount2(self->mount_path, MNT_FORCE); + EXPECT_EQ(ret, 0); + if (ret =3D=3D -1 && errno =3D=3D EINVAL) + fprintf(stderr, " %s was not mounted\n", self->mount_path); + + ret =3D rmdir(self->mount_path); + EXPECT_EQ(ret, 0); + if (ret =3D=3D -1) + fprintf(stderr, " rmdir(%s) failed: %m\n", self->mount_path); +} + +/* + * memfd_restricted() syscall can only be used with the fd of the root of = the + * mount. When the restrictedmem's fd is open, a user should not be able to + * unmount or remove the mounted directory + */ +TEST_F(restrictedmem_test_mount_path, restrictedmem_umount_rmdir_while_fil= e_open) +{ + int ret =3D -1; + int fd =3D -1; + int mfd =3D -1; + struct stat stat; + + ret =3D mount("name", self->mount_path, "tmpfs", 0, "huge=3Dalways"); + ASSERT_EQ(ret, 0); + + mfd =3D open(self->mount_path, O_PATH); + ASSERT_NE(mfd, -1); + + fd =3D memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_GT(fd, 0); + + /* We don't need this reference to the mount anymore */ + ret =3D close(mfd); + ASSERT_EQ(ret, 0); + + /* restrictedmem's fd should still be usable */ + ret =3D fstat(fd, &stat); + ASSERT_EQ(ret, 0); + ASSERT_EQ(stat.st_blksize, get_hpage_pmd_size()); + + /* User should not be able to unmount directory */ + ret =3D umount2(self->mount_path, MNT_FORCE); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EBUSY); + + ret =3D rmdir(self->mount_path); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EBUSY); + + close(fd); +} + +/* The fd of a file on the mount cannot be provided as mount_fd */ +TEST_F(restrictedmem_test_mount_path, restrictedmem_provide_fd_of_file) +{ + int ret =3D -1; + int fd =3D -1; + int ffd =3D -1; + char tmp_file_path[PATH_MAX] =3D { 0 }; + + ret =3D mount("name", self->mount_path, "tmpfs", 0, "huge=3Dalways"); + ASSERT_EQ(ret, 0); + + snprintf(tmp_file_path, PATH_MAX, "%s/tmp-file", self->mount_path); + ret =3D write_string_to_file(tmp_file_path, "filler\n"); + ASSERT_EQ(ret, 0); + + ffd =3D open(tmp_file_path, O_RDWR); + ASSERT_GT(ffd, 0); + + fd =3D memfd_restricted(MEMFD_RSTD_USERMNT, ffd); + ASSERT_LT(fd, 0); + ASSERT_EQ(errno, EINVAL); + + ret =3D close(ffd); + ASSERT_EQ(ret, 0); + + close(fd); + remove(tmp_file_path); +} + +/* The fd of files on the mount cannot be provided as mount_fd */ +TEST_F(restrictedmem_test_mount_path, restrictedmem_provide_fd_of_file_in_= subdir) +{ + int ret =3D -1; + int fd =3D -1; + int ffd =3D -1; + char tmp_dir_path[PATH_MAX] =3D { 0 }; + char tmp_file_path[PATH_MAX] =3D { 0 }; + + ret =3D mount("name", self->mount_path, "tmpfs", 0, "huge=3Dalways"); + ASSERT_EQ(ret, 0); + + snprintf(tmp_dir_path, PATH_MAX, "%s/tmp-subdir", self->mount_path); + ret =3D mkdir(tmp_dir_path, 0777); + ASSERT_EQ(ret, 0); + + snprintf(tmp_file_path, PATH_MAX, "%s/tmp-subdir/tmp-file", + self->mount_path); + ret =3D write_string_to_file(tmp_file_path, "filler\n"); + ASSERT_EQ(ret, 0); + + ffd =3D open(tmp_file_path, O_RDWR); + ASSERT_NE(ffd, -1); + + fd =3D memfd_restricted(MEMFD_RSTD_USERMNT, ffd); + ASSERT_LT(fd, 0); + ASSERT_EQ(errno, EINVAL); + + ret =3D close(ffd); + ASSERT_EQ(ret, 0); + + remove(tmp_file_path); + rmdir(tmp_dir_path); +} + +/* + * fds representing bind mounts must represent the root of the original + * filesystem + */ +TEST_F(restrictedmem_test_mount_path, restrictedmem_provide_fd_of_original= _fs) +{ + int ret =3D -1; + int fd =3D -1; + int mfd =3D -1; + char tmp_dir_path_0[PATH_MAX] =3D { 0 }; + char tmp_dir_path_1[PATH_MAX] =3D { 0 }; + + ret =3D mount("name", self->mount_path, "tmpfs", 0, "huge=3Dalways"); + ASSERT_EQ(ret, 0); + + snprintf(tmp_dir_path_0, PATH_MAX, "%s/tmp-subdir-0", self->mount_path); + ret =3D mkdir(tmp_dir_path_0, 0777); + ASSERT_EQ(ret, 0); + + snprintf(tmp_dir_path_1, PATH_MAX, "%s/tmp-subdir-1", self->mount_path); + ret =3D mkdir(tmp_dir_path_1, 0777); + ASSERT_EQ(ret, 0); + + ret =3D mount(tmp_dir_path_0, tmp_dir_path_1, "tmpfs", MS_BIND, NULL); + ASSERT_EQ(ret, 0); + + mfd =3D open(tmp_dir_path_1, O_PATH); + ASSERT_NE(mfd, -1); + + fd =3D memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_LT(fd, 0); + ASSERT_EQ(errno, EINVAL); + + ret =3D close(mfd); + ASSERT_EQ(ret, 0); + + ret =3D umount2(tmp_dir_path_1, MNT_FORCE); + ASSERT_EQ(ret, 0); + + rmdir(tmp_dir_path_0); + rmdir(tmp_dir_path_1); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/self= tests/mm/run_vmtests.sh index 53de84e3ec2c..04238f86f037 100644 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -40,6 +40,8 @@ separated by spaces: test memadvise(2) MADV_POPULATE_{READ,WRITE} options - memfd_restricted_ test memfd_restricted(2) +- memfd_restricted_usermnt + test memfd_restricted(2)'s handling of provided userspace mounts - memfd_secret test memfd_secret(2) - process_mrelease @@ -239,6 +241,7 @@ CATEGORY=3D"hmm" run_test ./test_hmm.sh smoke CATEGORY=3D"madv_populate" run_test ./madv_populate =20 CATEGORY=3D"memfd_restricted" run_test ./memfd_restricted +CATEGORY=3D"memfd_restricted_usermnt" run_test ./memfd_restricted_usermnt =20 CATEGORY=3D"memfd_secret" run_test ./memfd_secret =20 --=20 2.40.0.577.gac1e443424-goog