From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (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 52C0E41685B for ; Fri, 24 Jul 2026 22:02:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784930543; cv=none; b=cnqKyY5TmxOnk7EJVjWeYH5aWnmYxYGNWMFN5kaw8mJNNBwWykZBrAOrNcnSc9Uy1ca7Ug5zwXN75WZSxxQIShdF9qB2fkQJKGwR+/oG/N/SgY2Mr7fg3NFjhu4poOFDqOcbXGH1afMd/25hvEu6Jsk6CS/heprz98tYS8xkBOg= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784930543; c=relaxed/simple; bh=oRGrlChxBMsxVyxpsQ43J9Y1ZaxNsqhcVFf/I6c2bm4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=o260ULv7sTxBsQLbfPjZL3IQe/OpeGAFD32fX1+dHTcBtCidR7/I9rEn3OHzmkt2c/NBVQym7Ex+GGmh7KaWTucsJHE7gCau1gD1y95o4zIey/1E9eHK24ovbS7S4+Q7NHng/K0X5G+Blx1AjOCRmTOtsWEFLdFxItGqpycO28Y= 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=I+WYZcIl; arc=none smtp.client-ip=209.85.214.176 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="I+WYZcIl" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2cf27856f9cso12841645ad.2 for ; Fri, 24 Jul 2026 15:02:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784930542; x=1785535342; darn=vger.kernel.org; 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:content-type; bh=dNz1nVhQciAPDe5/sp7R44bDI4wMfSaLt+M5kzutlc4=; b=I+WYZcIlU6cjFeBCxgGFlm/WVizV45lhRpsO7BxANf+wiGXZ6WgmDiXyvwNXSJqt1f r0vubLa4Eb7zmFCSqSYlm+sq9HBIAeeQs6sEPbpliuATGzismKZC9LMTvyhq00YjoN4F lrzGj+vlNXRhRA7xdiAjxOBH2ayJXxgoTz2Tyo/ikiGyejtmdGrm6rKAY2aJ/s+2u+42 z5vzIFqb2xVz+7eoIKIA/iz+v297u0Qz6K0tV9HmLphb2WUrR/7Xn+O4ehuexqaeaYOp m4a1WYtxWK8WREBhW2qmdZaCR9+OIi0pdSWN6AQ1ypk/UWxCOqxp/NaCs9IPhpFL3FBD +FQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784930542; x=1785535342; 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:content-type; bh=dNz1nVhQciAPDe5/sp7R44bDI4wMfSaLt+M5kzutlc4=; b=i095A6an9DZM3XXT1rCN/qMa42T8DCFRMLNLFpxvJ4I8rz1AAY9Jlgnm2HtdK6madO X3qKiGpOVGX8VIo9zUaYPUThwGVBPJ5ep7rPxrD92F89olXHLOpjx0ChcM9VI2hTL1SG oQbAzzZgs1cEhdGMdWrv083PYbQ7VzvTnnhzS4i+S3dw8btOSQdPVRg2XGzj+yoWXGqu Nxdt/NLSVSKM/Rb7nLsbpoyhDuG13a8yccZlVhIKN0yuA6rw56FZrt2v5yeRtUl3CON5 YdLdEeZ8UmLC4eKtI9HcsPGPCzaMN8/VMtzdc4XNgOMU0LzLGSrYXX0Roz398C+hxO6B AQhA== X-Forwarded-Encrypted: i=1; AHgh+Rq9teVHSApjANSEndq3RkGmuIRBKMCWca3sgMtmKVqlJfd7yn85dFfdU5hWIbzTX+5WbGTrzvMkPDL1rWs=@vger.kernel.org X-Gm-Message-State: AOJu0YwBJ/+pQWLXlU7aJHA8imjHHbzqdYFFVp0C/qd67T/VApRWu7+U QnJExxpnh4Cv2Lbrxi1pY4HrRhtqeBF59qrJc8HSqPCP5fUaE1wQ20rK X-Gm-Gg: AR+sD11edkGBmDdyFd37vbfD97aGJFyYW+NyOXnd7j17SV94pUVBfUeNaJ6QFdNr16m oC65PcEgxIBR+eN2z53FkMzeUa59xEXIuuNlea9Qc5Wd97BHNyF3qV347DBz/4dYubVPPL/JjHg S8uWyM4bvbngH1kC8SfBsA/+ZJlez8j7jWLTGzI/H+gVGabKDTFos1Jd6thQsjkv8zvcrb/4jhj uerz5TeyadDd8eweIcaJZqo6vEMZxHsBSrIt4ZaX/Pc9KG5FaN5q7AAXzebXs8OM1Vdt1OqsvMi dvwlSVAqALYrSLnf3BF9fZ29d2sulBBegkHQflu3OCOej9loTVHzcnfD178nNdmX/6fdqXDKose bK8xq6FYbLUMIevqY71bqVdTMj80C6Tr7F7J3douKcr81vbyRBgcltTWwFxriCibLemcufdVHDq wGuf68KdybzvasBSMmq7sVmTuu9tUGNZuJbyVLUcyRNa8JAHNUJnN+6xGEY/bn X-Received: by 2002:a17:90b:1e46:b0:387:e0bb:57f4 with SMTP id 98e67ed59e1d1-38f2978d427mr191838a91.37.1784930541375; Fri, 24 Jul 2026 15:02:21 -0700 (PDT) Received: from pop-os.tail4adac7.ts.net ([2601:647:6802:dbc0:546a:e1b0:9574:3a29]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-314bc5a67f3sm2962631eec.29.2026.07.24.15.02.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Jul 2026 15:02:20 -0700 (PDT) From: Cong Wang To: Andy Lutomirski Cc: Kees Cook , linux-kernel@vger.kernel.org, Will Drewry , Christian Brauner , Andrew Morton , linux-mm@kvack.org, Cong Wang Subject: [PATCH v7 3/8] seccomp: introduce SECCOMP_IOCTL_NOTIF_PIN_INSTALL Date: Fri, 24 Jul 2026 15:01:42 -0700 Message-ID: <20260724220147.214396-4-xiyou.wangcong@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260724220147.214396-1-xiyou.wangcong@gmail.com> References: <20260724220147.214396-1-xiyou.wangcong@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Cong Wang SECCOMP_IOCTL_NOTIF_PIN_INSTALL maps a supervisor-owned @memfd at @target_addr in the trapped task's mm via vm_mmap_remote(), PROT_READ, MAP_SHARED, MAP_FIXED_NOREPLACE and VM_SEALED. Because the mapping is sealed, neither the target nor a CLONE_VM peer can munmap, mremap, mprotect or MAP_FIXED-stomp it; its contents are immutable from the target's side while the supervisor retains write access through its own mapping of the same memfd. The install needs no target-side cooperation, which is what makes the feature usable for fork+execve sandbox wrappers (Sandlock, Firejail, Bubblewrap-style) that have no trusted post-exec window to install their own mappings. The pin is just a sealed VMA owned by the target's mm: it persists until the task execve()s or exits (a sealed VMA cannot be unmapped piecemeal), and the kernel keeps no per-pin bookkeeping. A supervisor reuses one region across many redirects. Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cong Wang --- include/linux/seccomp.h | 5 ++ include/uapi/linux/seccomp.h | 34 ++++++++++ kernel/seccomp.c | 126 +++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 9b959972bf4a..a91d1fc8a2b8 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -16,6 +16,11 @@ #define SECCOMP_NOTIFY_ADDFD_SIZE_VER0 24 #define SECCOMP_NOTIFY_ADDFD_SIZE_LATEST SECCOMP_NOTIFY_ADDFD_SIZE_VER0 +/* sizeof() the first published struct seccomp_notif_pin_install */ +#define SECCOMP_NOTIFY_PIN_INSTALL_SIZE_VER0 32 /* up to @size */ +#define SECCOMP_NOTIFY_PIN_INSTALL_SIZE_VER1 40 /* adds @offset */ +#define SECCOMP_NOTIFY_PIN_INSTALL_SIZE_LATEST SECCOMP_NOTIFY_PIN_INSTALL_SIZE_VER1 + #ifdef CONFIG_SECCOMP #include diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h index dbfc9b37fcae..d3249294788b 100644 --- a/include/uapi/linux/seccomp.h +++ b/include/uapi/linux/seccomp.h @@ -137,6 +137,37 @@ struct seccomp_notif_addfd { __u32 newfd_flags; }; +/** + * struct seccomp_notif_pin_install - have the kernel install a sealed + * MAP_SHARED mapping of @memfd into the trapped task's mm at @target_addr. + * + * The supervisor owns @memfd and the kernel installs the mapping without + * target-side cooperation. It is read-only and VM_SEALED, so the target and + * any CLONE_VM peer cannot munmap, mremap, mprotect or MAP_FIXED-stomp it. + * @memfd must be write-sealed (F_SEAL_WRITE or F_SEAL_FUTURE_WRITE, -EINVAL + * otherwise) so its bytes cannot be rewritten through any other reference to + * the same memfd. + * + * @id: The ID of an active seccomp notification on this listener, + * identifying the trapped task whose mm receives the pin. + * @flags: Reserved, must be 0. + * @memfd: Supervisor-side fd for the backing memfd. Must be write-sealed. + * @target_addr: Page-aligned address in the trapped task's mm to install at. + * If non-zero it is MAP_FIXED (no existing mapping may overlap + * [@target_addr, @target_addr + @size)); if zero the kernel + * picks a free area. The actual address is written back here. + * @size: Size of the pin in bytes. Must be page-aligned. + * @offset: Page-aligned byte offset into @memfd to map from. + */ +struct seccomp_notif_pin_install { + __u64 id; + __u32 flags; + __u32 memfd; + __u64 target_addr; + __u64 size; + __u64 offset; +}; + #define SECCOMP_IOC_MAGIC '!' #define SECCOMP_IO(nr) _IO(SECCOMP_IOC_MAGIC, nr) #define SECCOMP_IOR(nr, type) _IOR(SECCOMP_IOC_MAGIC, nr, type) @@ -154,4 +185,7 @@ struct seccomp_notif_addfd { #define SECCOMP_IOCTL_NOTIF_SET_FLAGS SECCOMP_IOW(4, __u64) +#define SECCOMP_IOCTL_NOTIF_PIN_INSTALL SECCOMP_IOWR(5, \ + struct seccomp_notif_pin_install) + #endif /* _UAPI_LINUX_SECCOMP_H */ diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 066909393c38..e894af0e7c78 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -37,12 +37,18 @@ #ifdef CONFIG_SECCOMP_FILTER #include #include +#include #include #include #include #include #include #include +#include +#include +#include +#include +#include /* * When SECCOMP_IOCTL_NOTIF_ID_VALID was first introduced, it had the @@ -1823,6 +1829,123 @@ static long seccomp_notify_addfd(struct seccomp_filter *filter, return ret; } +static unsigned long seccomp_install_pin(struct mm_struct *mm, + struct file *memfd_file, + unsigned long target_addr, size_t size, + unsigned long offset) +{ + unsigned long ret; + + if (!VM_SEALED) + return -EOPNOTSUPP; + + /* + * Install a sealed, read-only mapping. A fixed request (@target_addr + * != 0) is MAP_FIXED_NOREPLACE: an existing mapping yields -EEXIST + * rather than being silently clobbered. A request of 0 lets the kernel + * pick a free area in the target mm. + */ + ret = vm_mmap_remote(mm, memfd_file, target_addr, size, PROT_READ, + MAP_SHARED | MAP_FIXED_NOREPLACE, + offset >> PAGE_SHIFT, VM_SEALED); + if (IS_ERR_VALUE(ret)) + return ret; + if (target_addr && ret != target_addr) + return -ENOMEM; + return ret; +} + +static long seccomp_notify_pin_install(struct seccomp_filter *filter, + struct seccomp_notif_pin_install __user *upin, + unsigned int size) +{ + struct seccomp_notif_pin_install pin; + struct seccomp_knotif *knotif; + struct task_struct *target; + struct file *memfd_file; + struct mm_struct *mm; + unsigned long addr, as_limit, npages; + int seals; + long ret; + + BUILD_BUG_ON(sizeof(pin) < SECCOMP_NOTIFY_PIN_INSTALL_SIZE_VER0); + BUILD_BUG_ON(sizeof(pin) != SECCOMP_NOTIFY_PIN_INSTALL_SIZE_LATEST); + + if (size < SECCOMP_NOTIFY_PIN_INSTALL_SIZE_VER0 || size >= PAGE_SIZE) + return -EINVAL; + + ret = copy_struct_from_user(&pin, sizeof(pin), upin, size); + if (ret) + return ret; + + if (pin.flags) + return -EINVAL; + if (!pin.size || !IS_ALIGNED(pin.target_addr, PAGE_SIZE) || + !IS_ALIGNED(pin.size, PAGE_SIZE) || !IS_ALIGNED(pin.offset, PAGE_SIZE)) + return -EINVAL; + if (pin.target_addr + pin.size < pin.target_addr) + return -EINVAL; + if (pin.offset + pin.size < pin.offset) + return -EINVAL; + + memfd_file = fget(pin.memfd); + if (!memfd_file) + return -EBADF; + + seals = memfd_get_seals(memfd_file); + if (seals < 0 || !(seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE))) { + ret = -EINVAL; + goto out_fput; + } + + ret = mutex_lock_interruptible(&filter->notify_lock); + if (ret < 0) + goto out_fput; + + knotif = find_notification(filter, pin.id); + if (!knotif) { + ret = -ENOENT; + goto out_unlock; + } + if (knotif->state != SECCOMP_NOTIFY_SENT) { + ret = -EINPROGRESS; + goto out_unlock; + } + + target = knotif->task; + mm = get_task_mm(target); + as_limit = task_rlimit(target, RLIMIT_AS) >> PAGE_SHIFT; + mutex_unlock(&filter->notify_lock); + if (!mm) { + ret = -ESRCH; + goto out_fput; + } + + npages = pin.size >> PAGE_SHIFT; + if (npages > as_limit || READ_ONCE(mm->total_vm) > as_limit - npages) { + mmput(mm); + ret = -ENOMEM; + goto out_fput; + } + + addr = seccomp_install_pin(mm, memfd_file, pin.target_addr, pin.size, + pin.offset); + mmput(mm); + if (IS_ERR_VALUE(addr)) + ret = addr; + else if (put_user(addr, &upin->target_addr)) + ret = -EFAULT; + else + ret = 0; + goto out_fput; + +out_unlock: + mutex_unlock(&filter->notify_lock); +out_fput: + fput(memfd_file); + return ret; +} + static long seccomp_notify_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1847,6 +1970,9 @@ static long seccomp_notify_ioctl(struct file *file, unsigned int cmd, switch (EA_IOCTL(cmd)) { case EA_IOCTL(SECCOMP_IOCTL_NOTIF_ADDFD): return seccomp_notify_addfd(filter, buf, _IOC_SIZE(cmd)); + case EA_IOCTL(SECCOMP_IOCTL_NOTIF_PIN_INSTALL): + return seccomp_notify_pin_install(filter, buf, + _IOC_SIZE(cmd)); default: return -EINVAL; } -- 2.43.0