From patchwork Tue Apr 11 01:29:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ackerley Tng X-Patchwork-Id: 81685 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp2263834vqo; Mon, 10 Apr 2023 18:30:57 -0700 (PDT) X-Google-Smtp-Source: AKy350apNGeGYzoQZ8ENCQKGoKNyCe6ZJUxBdPxMW/hogh0rENNtscxCsIvw6Ao53ebrTrKbQB+8 X-Received: by 2002:a05:6a20:6705:b0:ea:eb3f:43 with SMTP id q5-20020a056a20670500b000eaeb3f0043mr1800794pzh.34.1681176657028; Mon, 10 Apr 2023 18:30:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1681176657; cv=none; d=google.com; s=arc-20160816; b=IPWiOdt+GCCdzW9vVsAsJ8fbWQvpuMTXDABDg6ejIifvuPiHZ59EcbB9N6tBePHczH tFAwxM0mPg7kBc3atY2p0KEmyWdF+Uquxe2fseP/ZUtlMAcwZIPrLctIVQrZ8nSCe9l3 IalanN0L/cCW1NnPau5xtnp+/PeHLAdyvvmH0xTu6ml5jAltaSzIMzoCavM0lowdJkeH kabTrqW9oQfmspatyUKQxt6MTfTi+8TI8upefSMiTperuWXm+ssU9D8/09capOc3B7of JVuMoRbZG0KaLoMg+5sUJmrDS59dJZjyO4BXwuT+6pRx6aySdX8j339tncN2s7aa2pkD VTAg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:dkim-signature; bh=jBCdWjTPDV7u7tywdkOEhqA30FFTo51cVFS76tLVdIk=; b=Hjhpn9O73G6YUe5srnRYtP1o4JMI5dn5nH0voP3kX+Hir5V5D/a1LIlrSIDcLZnpJx EYDT+mbVTJ2mYOzlcFOf3xp2+D/DmbLhL5n1LVAGZypNAk8JtJlC+zLOvNSnE6RqrHAf R40E+SL8AZVdP7qaqKEOEwiqJ6FR38mcJzdahS1xGKjr5PeVBdkmFXOElvCV/b68LmIS ZzcN4fU9P5GmvvkX6bksQmR+NI17ixnziLe0fzRR7ENoCixcun0e6iN5++KmVNU5eHie o+ac92n7vsgizLmHDrdwY8VfINhE8ygZRaOcTbikdQ++Nn8nWdd1Piz9qaQdJ51EYQaD ZF4Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20210112 header.b=XXnjZgXz; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id z17-20020aa79591000000b00639d66c8fbcsi2185817pfj.118.2023.04.10.18.30.44; Mon, 10 Apr 2023 18:30:57 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20210112 header.b=XXnjZgXz; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230013AbjDKB3q (ORCPT + 99 others); Mon, 10 Apr 2023 21:29:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35474 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229925AbjDKB3k (ORCPT ); Mon, 10 Apr 2023 21:29:40 -0400 Received: from mail-pj1-x104a.google.com (mail-pj1-x104a.google.com [IPv6:2607:f8b0:4864:20::104a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A49D3173B for ; Mon, 10 Apr 2023 18:29:38 -0700 (PDT) Received: by mail-pj1-x104a.google.com with SMTP id 98e67ed59e1d1-246a00d70eeso180908a91.1 for ; Mon, 10 Apr 2023 18:29:38 -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=xarv/79x9r0YcwsVrmJtPVzZL2uoNYJ+P9VYZlvbzBmCLryxan0TNnxGqrFOADVOYL i6huI2hy1QXs8OWZNDJ1TEre+vAHwHX91JwNd+6sFzX5w60NMErYrvNxZEWwqiVaR07N HKfBufBWz6wlTZaZ5rD6DHFpGcBeGhVuhPqRESuU0SjrttPMw4tL/AdhECw4Vfsrj5mD va812QrYdtFpYs8KRrTQmF9owwmVjU7ixY5AWGcYbmPoZBA6NumNMjNbD6hZeGENVYx8 6WmKIAuly4SEz6ry6Hm7J+e9VQOc/JZlRN87Gqykh5mgLZWuCrLssbr3YljcRnLVwtfc 3yZQ== X-Gm-Message-State: AAQBX9cvrWrYsPiA7f1X2dN/OaTgPinc+u0GrSmez4dakxckjGnCl3Vr 8p/YWcKp5posRWd70D0JFbqzsYwaPidqe0Do8A== 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 X-Spam-Status: No, score=-7.7 required=5.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1762841494670002136?= X-GMAIL-MSGID: =?utf-8?q?1762841494670002136?= 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); /* * Architecture-specific system calls diff --git a/include/uapi/linux/restrictedmem.h b/include/uapi/linux/restrictedmem.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 struct restrictedmem { @@ -250,19 +251,20 @@ static struct address_space_operations restricted_aops = { #endif }; -SYSCALL_DEFINE1(memfd_restricted, unsigned int, flags) +static int restrictedmem_create(struct vfsmount *mount) { struct file *file, *restricted_file; int fd, err; - if (flags) - return -EINVAL; - fd = get_unused_fd_flags(0); if (fd < 0) return fd; - file = shmem_file_setup("memfd:restrictedmem", 0, VM_NORESERVE); + if (mount) + file = shmem_file_setup_with_mnt(mount, "memfd:restrictedmem", 0, VM_NORESERVE); + else + file = shmem_file_setup("memfd:restrictedmem", 0, VM_NORESERVE); + if (IS_ERR(file)) { err = PTR_ERR(file); goto err_fd; @@ -286,6 +288,67 @@ SYSCALL_DEFINE1(memfd_restricted, unsigned int, flags) return err; } +static struct vfsmount *restrictedmem_get_user_mount(struct file *file) +{ + int ret; + struct vfsmount *mnt; + struct path *path; + + path = &file->f_path; + if (path->dentry != path->mnt->mnt_root) + return ERR_PTR(-EINVAL); + + /* + * Disallow bind-mounts that aren't bind-mounts of the whole + * filesystem + */ + mnt = path->mnt; + if (mnt->mnt_root != mnt->mnt_sb->s_root) + return ERR_PTR(-EINVAL); + + if (mnt->mnt_sb->s_magic != TMPFS_MAGIC) + return ERR_PTR(-EINVAL); + + ret = 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 = {}; + struct vfsmount *mnt = NULL; + + if (flags & ~MEMFD_RSTD_USERMNT) + return -EINVAL; + + if (flags & MEMFD_RSTD_USERMNT) { + f = fdget_raw(mount_fd); + if (!f.file) + return -EBADF; + + mnt = restrictedmem_get_user_mount(f.file); + if (IS_ERR(mnt)) { + ret = PTR_ERR(mnt); + goto out; + } + } + + ret = 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) { From patchwork Tue Apr 11 01:29:33 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ackerley Tng X-Patchwork-Id: 81686 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp2266819vqo; Mon, 10 Apr 2023 18:39:43 -0700 (PDT) X-Google-Smtp-Source: AKy350YKNQxtMLLju8MjV1/f4pG3G3zN88Igp98p9Df19+NOXj2UgNrgjMRnsHdLuD5lE9XQgwUR X-Received: by 2002:a17:90b:4a41:b0:234:e0c:caaa with SMTP id lb1-20020a17090b4a4100b002340e0ccaaamr1682500pjb.6.1681177183733; Mon, 10 Apr 2023 18:39:43 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1681177183; cv=none; d=google.com; s=arc-20160816; b=j+bkXnXsGb8L1y+rOfPE3HegtJ1dJBFUlixPDnWdy5t+j71KqJHEylif32wFidZFIL HadEYtPxfanTFXkH/EsNbCYthcygwT8Pfkqor0fxJCs1yfoVZN5CcZn2BJOvJb4LtGl3 8Syq8rzdvd1EIXa4rWunsXKzzoJiEJDRkXa6eDYnRK+lkciLhLqrWhJQqvYn5cjhN9Tj rDfUp1CcYBOrKl6LGkAeK1atHucM+EKMQdSRQ5ZN2A2DInR2Iq3F262KdF6zM7uMXxBb Fm8tItsgPwfu9mKm+fCdBf6exTaxhg2ACrCZmCFElWaLs122he47ubMLsA/Orvqlcr4K dB0w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:dkim-signature; bh=SUMdFa7+PPMn99YcGlnAfohLLYbWjlEJWXPRhdQ3G0w=; b=fBV76437QBV3PMeRh3zXKvv2k3VrsObWeOC26A1mOwIvk9OnmFGk4hBV4CCo9qYnS2 bOUNPMEXGM4HMp8esXlt3ZpOHkyAY8hip2qRRlbKqZTMaspsgrimL0JdrflLxab+w0IO q21T8KqhuryXiUCbwTa9xdUZGQwTor4RwsBxZ57I2BZK5xKFFTF8BUUfS/hDZY55sTzf AnYarXMEINcJ9Udv/N710Z+st//8usRPtvF8cFOcStyx6+UP6RMREG0L+BEaeBU/1zt2 aKHNcugYGL9etW4P8jTm1Lo6s5z4aky4oQhysXJdbWfCdmXJ2Td/qwGKogAmNjq8jOms EgKg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20210112 header.b=O5jPzmxv; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id h12-20020a63120c000000b0051343add56fsi11542118pgl.35.2023.04.10.18.39.31; Mon, 10 Apr 2023 18:39:43 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20210112 header.b=O5jPzmxv; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229978AbjDKBaE (ORCPT + 99 others); Mon, 10 Apr 2023 21:30:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35700 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229936AbjDKB3o (ORCPT ); Mon, 10 Apr 2023 21:29:44 -0400 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8E9611BFC for ; Mon, 10 Apr 2023 18:29:40 -0700 (PDT) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-54ef5a44bd9so63389337b3.8 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=RgEkTkt5oeW4UhYEq7H2vOhGT17dV+0VEcvm4se4iOUougnZXCJjU6SML6X8oJt9m0 rJCZXL8cm1yGpol9EzDP4PgF5g1G4itVoXvYt9pc2mJUGsEuzKdEfyvuh4xCDW8f/tnE 8MSYXbFcizBC7VzKB1Pq1B2hY8HYv0jZLBAUdNaQmswomMMTiqUI89YvV3LIhdP6XeGt WfbKNH3LdLXi8Cy2WVc7LaAR15JjRUCOWQ8vJfq1Ajn8+QICTpMQYv2peDdhfUnTcAC6 wulTaxN8N+vM/LMFJLljQ4QdpT/a4cAidQgHuAXSZeLt3cblhxGJSOyVtP0fbcCVGmSS zi0Q== X-Gm-Message-State: AAQBX9eq6btoiLulQhbL9klzJHYU9gzOg+ajIY1h/5MUNDQ4xEga9RBH w97E4UZXIwLJOnG0r8BBl3JsxRBYJYVfYfBoWA== 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 X-Spam-Status: No, score=-7.7 required=5.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1762842046588018011?= X-GMAIL-MSGID: =?utf-8?q?1762842046588018011?= 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/selftests/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 += map_fixed_noreplace TEST_GEN_FILES += map_hugetlb TEST_GEN_FILES += map_populate TEST_GEN_FILES += memfd_restricted +TEST_GEN_FILES += memfd_restricted_usermnt TEST_GEN_FILES += memfd_secret TEST_GEN_FILES += migration TEST_GEN_FILES += 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 = fopen("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", "r"); + if (!fp) + return -1; + + ret = fgets(buf, 100, fp); + if (ret != buf) { + size = -1; + goto out; + } + + if (sscanf(buf, "%d\n", &size) != 1) + size = -1; + +out: + fclose(fp); + + return size; +} + +static int write_string_to_file(const char *path, const char *string) +{ + FILE *fp; + size_t len = strlen(string); + int ret = -1; + + fp = fopen(path, "w"); + if (!fp) + return ret; + + if (fwrite(string, 1, len, fp) != len) + goto out; + + ret = 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") == 0) + return true; + if (strcmp(policy, "within_size") == 0) + return true; + if (strcmp(policy, "advise") == 0) + return true; + if (strcmp(policy, "never") == 0) + return true; + if (strcmp(policy, "deny") == 0) + return true; + if (strcmp(policy, "force") == 0) + return true; + + return false; +} + +static int get_shmem_thp_policy(char *policy) +{ + FILE *fp; + char buf[100]; + char *left = NULL; + char *right = NULL; + int ret = -1; + + fp = fopen("/sys/kernel/mm/transparent_hugepage/shmem_enabled", "r"); + if (!fp) + return -1; + + if (fgets(buf, 100, fp) != buf) + goto out; + + /* + * Expect shmem_enabled to be of format like "always within_size advise + * [never] deny force" + */ + left = memchr(buf, '[', 100); + if (!left) + goto out; + + right = memchr(buf, ']', 100); + if (!right) + goto out; + + memcpy(policy, left + 1, right - left - 1); + + ret = !is_valid_shmem_thp_policy(policy); + +out: + fclose(fp); + return ret; +} + +static int set_shmem_thp_policy(char *policy) +{ + int ret = -1; + /* +1 for newline */ + char to_write[POLICY_BUF_SIZE + 1] = { 0 }; + + if (!is_valid_shmem_thp_policy(policy)) + return ret; + + ret = snprintf(to_write, POLICY_BUF_SIZE + 1, "%s\n", policy); + if (ret != strlen(policy) + 1) + return -1; + + ret = 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 = -1; + struct stat stat; + + ASSERT_EQ(set_shmem_thp_policy("never"), 0); + + fd = 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 = -1; + struct stat stat; + + ASSERT_EQ(set_shmem_thp_policy("always"), 0); + + fd = 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 = memfd_restricted(MEMFD_RSTD_USERMNT, -2); + + ASSERT_EQ(fd, -1); + ASSERT_EQ(errno, EBADF); +} + +TEST(restrictedmem_tmpfile_fd_not_a_mount) +{ + int fd = memfd_restricted(MEMFD_RSTD_USERMNT, STDOUT_FILENO); + + ASSERT_EQ(fd, -1); + ASSERT_EQ(errno, EINVAL); +} + +TEST(restrictedmem_tmpfile_not_tmpfs_mount) +{ + int fd = -1; + int mfd = -1; + + mfd = open("/proc", O_PATH); + ASSERT_NE(mfd, -1); + + fd = 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 = 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 = -1; + int fd = -1; + int mfd = -1; + struct stat stat; + + fsconfig(self->sfd, FSCONFIG_SET_STRING, "huge", "always", 0); + fsconfig(self->sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); + + mfd = fsmount(self->sfd, 0, 0); + ASSERT_NE(mfd, -1); + + fd = memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_GT(fd, 0); + + /* User can close reference to mount */ + ret = close(mfd); + ASSERT_EQ(ret, 0); + + ret = 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 = -1; + int fd = -1; + int mfd = -1; + struct stat stat; + + fsconfig(self->sfd, FSCONFIG_SET_STRING, "huge", "never", 0); + fsconfig(self->sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); + + mfd = fsmount(self->sfd, 0, 0); + ASSERT_NE(mfd, -1); + + fd = memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_GT(fd, 0); + + /* User can close reference to mount */ + ret = close(mfd); + ASSERT_EQ(ret, 0); + + ret = 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 = -1; + int fd = -1; + int mfd = -1; + + fsconfig(self->sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); + + mfd = fsmount(self->sfd, 0, MOUNT_ATTR_RDONLY); + ASSERT_NE(mfd, -1); + + fd = memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_EQ(fd, -1); + ASSERT_EQ(errno, EROFS); + + ret = close(mfd); + ASSERT_EQ(ret, 0); +} + +TEST_F(tmpfs_sfd, restrictedmem_check_superblock_flags) +{ + int ret = -1; + int fd = -1; + int mfd = -1; + + fsconfig(self->sfd, FSCONFIG_SET_FLAG, "ro", NULL, 0); + fsconfig(self->sfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); + + mfd = fsmount(self->sfd, 0, 0); + ASSERT_NE(mfd, -1); + + fd = memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_EQ(fd, -1); + ASSERT_EQ(errno, EROFS); + + ret = close(mfd); + ASSERT_EQ(ret, 0); +} + +static bool directory_exists(const char *path) +{ + struct stat sb; + + return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode); +} + +FIXTURE(restrictedmem_test_mount_path) +{ + char *mount_path; +}; + +FIXTURE_SETUP(restrictedmem_test_mount_path) +{ + int ret = -1; + + /* /tmp is an FHS-mandated world-writable directory */ + self->mount_path = "/tmp/restrictedmem-selftest-mnt"; + + if (!directory_exists(self->mount_path)) { + ret = mkdir(self->mount_path, 0777); + ASSERT_EQ(ret, 0); + } +} + +FIXTURE_TEARDOWN(restrictedmem_test_mount_path) +{ + int ret = -1; + + if (!directory_exists(self->mount_path)) + return; + + ret = umount2(self->mount_path, MNT_FORCE); + EXPECT_EQ(ret, 0); + if (ret == -1 && errno == EINVAL) + fprintf(stderr, " %s was not mounted\n", self->mount_path); + + ret = rmdir(self->mount_path); + EXPECT_EQ(ret, 0); + if (ret == -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_file_open) +{ + int ret = -1; + int fd = -1; + int mfd = -1; + struct stat stat; + + ret = mount("name", self->mount_path, "tmpfs", 0, "huge=always"); + ASSERT_EQ(ret, 0); + + mfd = open(self->mount_path, O_PATH); + ASSERT_NE(mfd, -1); + + fd = memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_GT(fd, 0); + + /* We don't need this reference to the mount anymore */ + ret = close(mfd); + ASSERT_EQ(ret, 0); + + /* restrictedmem's fd should still be usable */ + ret = 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 = umount2(self->mount_path, MNT_FORCE); + ASSERT_EQ(ret, -1); + ASSERT_EQ(errno, EBUSY); + + ret = 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 = -1; + int fd = -1; + int ffd = -1; + char tmp_file_path[PATH_MAX] = { 0 }; + + ret = mount("name", self->mount_path, "tmpfs", 0, "huge=always"); + ASSERT_EQ(ret, 0); + + snprintf(tmp_file_path, PATH_MAX, "%s/tmp-file", self->mount_path); + ret = write_string_to_file(tmp_file_path, "filler\n"); + ASSERT_EQ(ret, 0); + + ffd = open(tmp_file_path, O_RDWR); + ASSERT_GT(ffd, 0); + + fd = memfd_restricted(MEMFD_RSTD_USERMNT, ffd); + ASSERT_LT(fd, 0); + ASSERT_EQ(errno, EINVAL); + + ret = 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 = -1; + int fd = -1; + int ffd = -1; + char tmp_dir_path[PATH_MAX] = { 0 }; + char tmp_file_path[PATH_MAX] = { 0 }; + + ret = mount("name", self->mount_path, "tmpfs", 0, "huge=always"); + ASSERT_EQ(ret, 0); + + snprintf(tmp_dir_path, PATH_MAX, "%s/tmp-subdir", self->mount_path); + ret = mkdir(tmp_dir_path, 0777); + ASSERT_EQ(ret, 0); + + snprintf(tmp_file_path, PATH_MAX, "%s/tmp-subdir/tmp-file", + self->mount_path); + ret = write_string_to_file(tmp_file_path, "filler\n"); + ASSERT_EQ(ret, 0); + + ffd = open(tmp_file_path, O_RDWR); + ASSERT_NE(ffd, -1); + + fd = memfd_restricted(MEMFD_RSTD_USERMNT, ffd); + ASSERT_LT(fd, 0); + ASSERT_EQ(errno, EINVAL); + + ret = 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 = -1; + int fd = -1; + int mfd = -1; + char tmp_dir_path_0[PATH_MAX] = { 0 }; + char tmp_dir_path_1[PATH_MAX] = { 0 }; + + ret = mount("name", self->mount_path, "tmpfs", 0, "huge=always"); + ASSERT_EQ(ret, 0); + + snprintf(tmp_dir_path_0, PATH_MAX, "%s/tmp-subdir-0", self->mount_path); + ret = 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 = mkdir(tmp_dir_path_1, 0777); + ASSERT_EQ(ret, 0); + + ret = mount(tmp_dir_path_0, tmp_dir_path_1, "tmpfs", MS_BIND, NULL); + ASSERT_EQ(ret, 0); + + mfd = open(tmp_dir_path_1, O_PATH); + ASSERT_NE(mfd, -1); + + fd = memfd_restricted(MEMFD_RSTD_USERMNT, mfd); + ASSERT_LT(fd, 0); + ASSERT_EQ(errno, EINVAL); + + ret = close(mfd); + ASSERT_EQ(ret, 0); + + ret = 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/selftests/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="hmm" run_test ./test_hmm.sh smoke CATEGORY="madv_populate" run_test ./madv_populate CATEGORY="memfd_restricted" run_test ./memfd_restricted +CATEGORY="memfd_restricted_usermnt" run_test ./memfd_restricted_usermnt CATEGORY="memfd_secret" run_test ./memfd_secret