From nobody Mon Jul 27 02:14:44 2026 Received: from mail-pj1-f54.google.com (mail-pj1-f54.google.com [209.85.216.54]) (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 1735D35C1A6 for ; Fri, 10 Jul 2026 20:53:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716839; cv=none; b=s3xoUDSpMrjahKBPTkhedWnxs2XZDH96PiEAXU/eW7tp+iSEAjnTGeKkl6S10RsLFKTZSfhf9UGDhkVujBj3+XY9y88AmJEz/ilUO3CaBqjzlltnJv8EpbeC2ZE5jEFRBTeO3I4W0XKFwFLBSOH3WPIEHZYDlFfcznsokRb/2BU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716839; c=relaxed/simple; bh=oQbNURJxL+tjqWctpsqGm1Ty7qfTIql5z83UGi8D+2U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gwhQihDCgSxWE+Od60O4qgZxEheQOUYiIuIiFJ4tmceM2Aq7yD7HWdqSd3eHwg2mouckC8JnbO71o2GchfuTVlXoWJsIIgOQVH8ubgNvhgxwozgTyZ1XMl1rYKuf1uNCw3js0sMFyxHryk4BHl8P/zzGMlg5vNZ+QUKRI1XJ0Ag= 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=exdEfVzT; arc=none smtp.client-ip=209.85.216.54 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="exdEfVzT" Received: by mail-pj1-f54.google.com with SMTP id 98e67ed59e1d1-381c51fde6bso1379947a91.2 for ; Fri, 10 Jul 2026 13:53:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783716836; x=1784321636; 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=sCserWiFtJpC7hBtSNUd5SRB8rb8z/bWzdzLHsDDkzQ=; b=exdEfVzTkirQYZR79aeeak5D9efLhsTtokmZuHpgn8VXUeWKXJoWG81Vviiv3p8qHQ EZQZRhucKMm/lVlNi8u6rKvEQR2ilQkQXCgX/9Dg68+s7Q+OXVb1lpKqx49Q/j/KI2gN yk1aMpa0/eZA3sam+dXe3aV1TMT2LIPpnX9iCsGbgOecvGI/NFF4vOkjcVhOBA9YUGYg JKxZy4oVCj8imNI+s4/KrXpoO7GUhbkdiskNGvX2oxBwHtjfcOEmSGR0gqU9y3bxtYkj LJMnoyFP9mrLGo+Z1u87/y1cHzDv0q9kwdUIIJOuiwWwqFSavbslN0uIbizNabBGv6ye MstA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783716836; x=1784321636; 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=sCserWiFtJpC7hBtSNUd5SRB8rb8z/bWzdzLHsDDkzQ=; b=I7fLaWtWII7mCJdpXHQzsqIvWyqL5IvZRKmFBiGMrSZSo8O1NvGc3Rcmo6XV0pdnJP qXpBOeu/Ra77jGGBFxwK+JbbwgnKfJuq2nQTzXp8pfSMNh3ofJwiVHya78hd3WLriKB/ MxHUysNC+RwDU93IkSRuQCSpkYt1yP8D2kl2jZSkw20hCW6dgxd7LcC1ycqphFWZp9db 6aJCboYtsimkzCLqSyJFaMAak+iKWkAbAW4FDLKEnf05Bp32aiHZpP63TPesg/wWUXMV snDLc4lXFswSKiEO3MsBixN+bei7Sn6gAaavJXCMtK9bY5u25P40ZCZyr89TXxbeIOht yonQ== X-Forwarded-Encrypted: i=1; AHgh+RpJBcB0ls3GzcDq4T/BzwCnXs2U7HSlstXoraqA5t1nSYGJdqDX+JQ6D0PAhfkC8ZJlDpNc0w6ypaOn8AM=@vger.kernel.org X-Gm-Message-State: AOJu0YzfgQ/fRwln5Yb6qtH6XgzAQGcdpJUm4Righkp+wBEtPvUIUEv5 p+9wBc7B/Un//uADOZiPkTWkqcYWEwAr31obc0Ym56uByABcGINyj/+O X-Gm-Gg: AfdE7cktfK/mQ9pIzspkWS0PN+HeKOmaG/7eF969xbkcKdbEDpC8OAZF3WDTS0RRzxX OtZh72bJi2C4lMRoV+H5zhkcqf3RDENWhOfeUHbl/mmZo6RGkfaZBeTwRRJhutCRZuc1BrnhnQG yn/m91qqW0e/FAN0+qKDR0lEP/6+ta/lVKX1rHvozxpasmbZffE6D0KJaXttzktM0pPMuZvD10W KWUUaJCRz27/nfNR0FwQHWXitu9tzYnwkpLCEDnXq10EIoEuw0Gzy4jhzgAHJxI5dvB5Wv62b6+ OhHX5T8T3cdedUGqFQLT816LVqyjAK4cofQSamuUjr8gLn3FWztclGFXhXegLsJC/CJ/j9WtZi1 KrCoDvZTPiPLAyt+MrjmZ24J+OvMYBjev4y68/ioppViIHiCiIRqSfJl3pmV3HINJVmIoHp/pve ChWoFjSZHn74nYcFnJ8sXvcaetRlPKSm2/SJYBEusZPrD2DJPgcuYatQo= X-Received: by 2002:a05:6a21:46c5:b0:3bf:b40b:def2 with SMTP id adf61e73a8af0-3c11003092dmr563350637.9.1783716835815; Fri, 10 Jul 2026 13:53:55 -0700 (PDT) Received: from pop-os.scu.edu ([129.210.115.107]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3119c2a7bb5sm21724371eec.25.2026.07.10.13.53.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 13:53:55 -0700 (PDT) From: Cong Wang To: Richard Weinberger , Anton Ivanov , Johannes Berg Cc: Benjamin Berg , linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, Cong Wang Subject: [RFC PATCH 1/6] pidfd: add pidfd_mmap()/pidfd_munmap() syscalls Date: Fri, 10 Jul 2026 13:53:19 -0700 Message-ID: <20260710205324.1343217-2-xiyou.wangcong@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260710205324.1343217-1-xiyou.wangcong@gmail.com> References: <20260710205324.1343217-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: quoted-printable Content-Type: text/plain; charset="utf-8" From: Cong Wang A supervisor that may PTRACE_ATTACH a target installs or removes a mapping in the target's address space directly, without target-side cooperation. The backing fd is resolved in the *caller's* fd table, mirroring pidfd_getfd()'s cross-task install model. pidfd_mmap() takes an extensible struct pidfd_mmap_args (clone3/openat2 style, versioned by a leading size field) since an mmap-shaped call exceeds the 6-argument syscall limit. pidfd_munmap() is a flat 3-arg call. Both are gated by ptrace_may_access(PTRACE_MODE_ATTACH_REALCREDS) and wired for x86_64 and the asm-generic table. Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cong Wang --- arch/x86/entry/syscalls/syscall_64.tbl | 2 + include/linux/syscalls.h | 5 + include/uapi/linux/pidfd.h | 18 ++++ kernel/pid.c | 132 +++++++++++++++++++++++++ scripts/syscall.tbl | 2 + 5 files changed, 159 insertions(+) diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscal= ls/syscall_64.tbl index 524155d655da..12e3cbeb0d2c 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -396,6 +396,8 @@ 469 common file_setattr sys_file_setattr 470 common listns sys_listns 471 common rseq_slice_yield sys_rseq_slice_yield +472 common pidfd_mmap sys_pidfd_mmap +473 common pidfd_munmap sys_pidfd_munmap =20 # # Due to a historical design error, certain syscalls are numbered differen= tly diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 874d9067a43b..301a2d80823b 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -78,6 +78,7 @@ struct statmount; struct mnt_id_req; struct ns_id_req; struct xattr_args; +struct pidfd_mmap_args; struct file_attr; =20 #include @@ -986,6 +987,10 @@ asmlinkage long sys_pidfd_send_signal(int pidfd, int s= ig, siginfo_t __user *info, unsigned int flags); asmlinkage long sys_pidfd_getfd(int pidfd, int fd, unsigned int flags); +asmlinkage long sys_pidfd_mmap(int pidfd, struct pidfd_mmap_args __user *u= args, + unsigned int flags); +asmlinkage long sys_pidfd_munmap(int pidfd, unsigned long addr, + unsigned long len); asmlinkage long sys_landlock_create_ruleset(const struct landlock_ruleset_= attr __user *attr, size_t size, __u32 flags); asmlinkage long sys_landlock_add_rule(int ruleset_fd, enum landlock_rule_t= ype rule_type, diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h index 0919246a1611..d68a11d844e3 100644 --- a/include/uapi/linux/pidfd.h +++ b/include/uapi/linux/pidfd.h @@ -107,6 +107,24 @@ struct pidfd_info { __u64 supported_mask; /* Mask flags that this kernel supports */ }; =20 +/* + * Argument block for pidfd_mmap(). Extensible: @size is sizeof(struct) as + * known to userspace; the kernel zero-extends older/smaller structs and + * rejects unknown non-zero trailing bytes (see copy_struct_from_user()). + */ +struct pidfd_mmap_args { + __u64 size; /* sizeof(struct pidfd_mmap_args) */ + __u64 addr; /* MAP_FIXED target address, or 0 to let the kernel choose */ + __u64 len; /* length in bytes */ + __u64 prot; /* PROT_* protection bits */ + __u64 flags; /* MAP_* flags */ + __u64 pgoff; /* page offset into @fd */ + __s32 fd; /* backing fd in the caller's fd table, or -1 (MAP_ANONYMOUS) */ + __u32 __spare; /* must be zero */ +}; + +#define PIDFD_MMAP_ARGS_SIZE_VER0 56 /* sizeof first published struct */ + #define PIDFS_IOCTL_MAGIC 0xFF =20 #define PIDFD_GET_CGROUP_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 1) diff --git a/kernel/pid.c b/kernel/pid.c index f55189a3d07d..d51471a36f3e 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -28,6 +28,12 @@ */ =20 #include +#include +#include +#include +#include +#include +#include #include #include #include @@ -972,3 +978,129 @@ SYSCALL_DEFINE3(pidfd_getfd, int, pidfd, int, fd, =20 return pidfd_getfd(pid, fd); } + +/* + * Resolve @pidfd to its task's mm, gated like a ptrace attach. On success= the + * caller owns the returned mm reference and must mmput() it. Mirrors the + * cross-task authorization of pidfd_getfd(): a supervisor that may + * PTRACE_ATTACH the target may install into its address space. + */ +static int pidfd_target_mm(int pidfd, struct mm_struct **mmp) +{ + struct task_struct *task; + struct mm_struct *mm; + unsigned int f_flags; + + task =3D pidfd_get_task(pidfd, &f_flags); + if (IS_ERR(task)) + return PTR_ERR(task); + + if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS)) { + put_task_struct(task); + return -EPERM; + } + + mm =3D get_task_mm(task); + put_task_struct(task); + if (!mm) + return -ESRCH; + + *mmp =3D mm; + return 0; +} + +/** + * sys_pidfd_mmap() - install a mapping into another process's address spa= ce. + * @pidfd: pidfd of the target task. + * @uargs: pointer to a struct pidfd_mmap_args describing the mapping. + * @flags: reserved, must be zero. + * + * The backing fd (if any) is resolved in the *caller's* fd table, so a + * supervisor maps from its own descriptors into the target -- the target + * never needs to receive the fd. Gated by ptrace_may_access(); LSM/fsnoti= fy + * hooks run against the caller. + * + * Returns the mapped address on success, or a negative errno. + */ +SYSCALL_DEFINE3(pidfd_mmap, int, pidfd, struct pidfd_mmap_args __user *, u= args, + unsigned int, flags) +{ + struct pidfd_mmap_args args; + struct mm_struct *mm; + struct file *file =3D NULL; + unsigned long ret; + u64 usize; + int err; + + if (flags) + return -EINVAL; + + if (get_user(usize, &uargs->size)) + return -EFAULT; + if (usize < PIDFD_MMAP_ARGS_SIZE_VER0) + return -EINVAL; + if (usize > PAGE_SIZE) + return -E2BIG; + + err =3D copy_struct_from_user(&args, sizeof(args), uargs, usize); + if (err) + return err; + if (args.__spare) + return -EINVAL; + + if (args.flags & MAP_HUGETLB) + return -EINVAL; + if (args.addr !=3D (unsigned long)args.addr || + args.len !=3D (unsigned long)args.len || + args.prot !=3D (unsigned long)args.prot || + args.flags !=3D (unsigned long)args.flags || + args.pgoff !=3D (unsigned long)args.pgoff) + return -EINVAL; + + err =3D pidfd_target_mm(pidfd, &mm); + if (err) + return err; + + if (!(args.flags & MAP_ANONYMOUS)) { + audit_mmap_fd(args.fd, args.flags); + file =3D fget(args.fd); + if (!file) { + ret =3D -EBADF; + goto out_mm; + } + } + + ret =3D vm_mmap_remote(mm, file, args.addr, args.len, args.prot, + args.flags, args.pgoff, 0); + + if (file) + fput(file); +out_mm: + mmput(mm); + return ret; +} + +/** + * sys_pidfd_munmap() - remove a mapping from another process's address sp= ace. + * @pidfd: pidfd of the target task. + * @addr: start address of the range to unmap. + * @len: length in bytes. + * + * Gated by ptrace_may_access(), like sys_pidfd_mmap(). + * + * Returns 0 on success, or a negative errno. + */ +SYSCALL_DEFINE3(pidfd_munmap, int, pidfd, unsigned long, addr, + unsigned long, len) +{ + struct mm_struct *mm; + int ret; + + ret =3D pidfd_target_mm(pidfd, &mm); + if (ret) + return ret; + + ret =3D vm_munmap_remote(mm, addr, len); + mmput(mm); + return ret; +} diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl index 7a42b32b6577..f4fa7826232b 100644 --- a/scripts/syscall.tbl +++ b/scripts/syscall.tbl @@ -412,3 +412,5 @@ 469 common file_setattr sys_file_setattr 470 common listns sys_listns 471 common rseq_slice_yield sys_rseq_slice_yield +472 common pidfd_mmap sys_pidfd_mmap +473 common pidfd_munmap sys_pidfd_munmap --=20 2.43.0 From nobody Mon Jul 27 02:14:44 2026 Received: from mail-pj1-f46.google.com (mail-pj1-f46.google.com [209.85.216.46]) (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 815263AE1A9 for ; Fri, 10 Jul 2026 20:53:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716841; cv=none; b=Jekg/zuSCDyvDEnRybU4S9Q3JaJtzaM8zYJzVH8eHevvoL1xTQTyQSwOttpNv6nvtSvzbTAlluVg624kg1mKVXc/9PS0E6eyIiwFkY3HU1/EYGTuSoGot534JLhhbqqtcc6K4Ls1hRTmltFW7TzmZSKdP/4Z2D1k2Vb4/ynujBs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716841; c=relaxed/simple; bh=URA/PDsEMf5TxxwJjs2xq59Ev+4rkYZbGOd7s095fVM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=N/qkzkV4Wgwmit4C5KtEN86JQFuDerJRn65KSYxHv5c4lcJpEE+12iyhHFEH4Y3svLVUxSmFg2iUrG8H+Iy7eqUAFNIX8CxelxkUYJGr/w9I/jxxUn3Yl0eEXg11HfpK3h57lctuACh+FDq3mljN23f5yHw61H2uNBsYofxgwL0= 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=YAAASyZz; arc=none smtp.client-ip=209.85.216.46 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="YAAASyZz" Received: by mail-pj1-f46.google.com with SMTP id 98e67ed59e1d1-38125cebfdaso1930665a91.1 for ; Fri, 10 Jul 2026 13:53:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783716837; x=1784321637; 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=H+bYaPwfClCvmZMCpl+tD+9q31JoaywpOhKbQK6CIro=; b=YAAASyZzR40ZEhV5UhnZyaY+f76AnZZESW0RUyZdU1LV0Gm3gPdEEO72ntPyJreTIZ BakEhkLzro82yI8l/4z6792syUyZ/u09eu1dat5Rrd/l+yr+h/zJWJfdhN42ZfTKFYQZ vVXPKiQF6y1w39R5txZKaPdqTqx7oB4paKRnS8VlpMBc6djb/Ctz778KqtzpL2kICUC+ X6njAL+EJEJPcZJm9I88Mh8QeTCqNXRh7Skc+RwJGRBenxbFkw82YdatmD/mrGmgY0Gl 0/g8LufWbWTXhxDxEfw7FLyJktw0nvpwHaLw9cLpJuevydJS36R3WwK9+7+ipV3Pu6E9 rknA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783716837; x=1784321637; 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=H+bYaPwfClCvmZMCpl+tD+9q31JoaywpOhKbQK6CIro=; b=drRJHe0Z+jzHILiaWOaR1m0qyhPJWmUhXlpkWZm2VgoFwoRm65R4exM++GHnGQYF3n BofXpvRzSMCyQObu3Mw457bmiVCxnWKTnXdsm/CgbDdXqSFjYfBiFkiqp1X0hCnmtpry C6HGELOzYggqPhvThOkEo1pFp/XzwwoaeQXsaxqoqvfud2ZFeHlFQy5yGdW5esUtAS/R KSRShFxBQXJ3PxZXMZgdn2jBfVUdiqlxg/Mq93nCDOarf6rdcd4klxVn+zkjlCykXaZF rj0XnEkgHXdVHKIKJA1DDTOLGGs6BZku6S6r1IQRMfeFRHcVT2lnDwpINCG5KkB6R6ZP VTbg== X-Forwarded-Encrypted: i=1; AHgh+RrB4WTODWQZh4194vJIOKtp4Mvea8jf1UoYwHoG8l2hSpdSKK4P1uaNNFc+T40+c1LR4Vq5bsRy87TZmdA=@vger.kernel.org X-Gm-Message-State: AOJu0YwtSpVw/SVgwU+OkfB1N+QhrjW3JhfKGByLYe1p/GefcFwjXn68 XEZaWYoSFiFIzFYGF2qeO1COUjxIQqfjHt/WN0wDEqrREILN9yVVsyBp X-Gm-Gg: AfdE7cmbZWSETrtHzkhlgKKc1tLlNEUoV9o49FLJDwjo12B/IryQRFbwzcCkTwpQ6fW MzPCsLMwrzrdYbeC1kt1qWsSynEqnSWLf3EXIYTxw8LwTwigBnx2J8f72TheCNcZVOwsNkFDez+ FDgQ83Kc7FnNSGR5aLP7Z+LcSKhnMP/B+eRwvUr7pEmJh9WBtEQylgaAy78pUd051NxBM8PfbjC gmcdRfl2Y71utn1hfdDn+2bMFlZOEwp9wXBPV8NWYhj/KkMfAWXFSlhKjGdJ1sBAWRh5ymjWuUk LHqOpt1lIbVh3iGkXBTS7jpykS4fmQ12G1dRpEGoiA+W3jjqyP3QIO/OrQwRGdEpMCsXpzitWZJ v38pLudg/7HHZBiaPJ8WifmoLXEMu7DmRykIC4FF9gwApkGcOP8xEkbpvzegbsMYwF2YaTNbSYN os5SgCvIUBmAwXD/+wcTFYfVq+LIG2oZrSxoJs93kItJw77CWOuZcNi58= X-Received: by 2002:a17:90b:33c8:b0:387:e0db:3fb1 with SMTP id 98e67ed59e1d1-38dc77993c3mr515274a91.42.1783716837230; Fri, 10 Jul 2026 13:53:57 -0700 (PDT) Received: from pop-os.scu.edu ([129.210.115.107]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3119c2a7bb5sm21724371eec.25.2026.07.10.13.53.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 13:53:56 -0700 (PDT) From: Cong Wang To: Richard Weinberger , Anton Ivanov , Johannes Berg Cc: Benjamin Berg , linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, Cong Wang Subject: [RFC PATCH 2/6] um: acquire a stub pidfd via CLONE_PIDFD in seccomp mode Date: Fri, 10 Jul 2026 13:53:20 -0700 Message-ID: <20260710205324.1343217-3-xiyou.wangcong@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260710205324.1343217-1-xiyou.wangcong@gmail.com> References: <20260710205324.1343217-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: quoted-printable Content-Type: text/plain; charset="utf-8" From: Cong Wang In SECCOMP mode the monitor will install guest mappings into the stub's address space directly via pidfd_mmap(), so it needs a pidfd to the stub. Acquire one atomically at clone() time via CLONE_PIDFD (returned through the legacy-clone parent_tid argument), store it in mm_id->stub_pidfd, and close it on teardown. The ptrace (SKAS0) mode does not use pidfd_mmap and leaves stub_pidfd as -1. Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cong Wang --- arch/um/include/shared/skas/mm_id.h | 1 + arch/um/kernel/skas/mmu.c | 6 ++++++ arch/um/os-Linux/skas/process.c | 24 ++++++++++++++++++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/arch/um/include/shared/skas/mm_id.h b/arch/um/include/shared/s= kas/mm_id.h index 18c0621430d2..cec97189f12b 100644 --- a/arch/um/include/shared/skas/mm_id.h +++ b/arch/um/include/shared/skas/mm_id.h @@ -16,6 +16,7 @@ struct mm_id { int syscall_data_len; =20 /* Only used with SECCOMP mode */ + int stub_pidfd; /* pidfd to the stub, or -1 */ int sock; int syscall_fd_num; int syscall_fd_map[STUB_MAX_FDS]; diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c index b5017096028b..441dcf94ec9c 100644 --- a/arch/um/kernel/skas/mmu.c +++ b/arch/um/kernel/skas/mmu.c @@ -54,6 +54,7 @@ int init_new_context(struct task_struct *task, struct mm_= struct *mm) goto out; =20 new_id->stack =3D stack; + new_id->stub_pidfd =3D -1; new_id->syscall_data_len =3D 0; new_id->syscall_fd_num =3D 0; =20 @@ -103,6 +104,11 @@ void destroy_context(struct mm_struct *mm) mmu->id.pid =3D -1; } =20 + if (mmu->id.stub_pidfd >=3D 0) { + os_close_file(mmu->id.stub_pidfd); + mmu->id.stub_pidfd =3D -1; + } + if (using_seccomp && mmu->id.sock) os_close_file(mmu->id.sock); =20 diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/proces= s.c index d6c22f8aa06d..3dd97ca7999a 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -34,6 +34,10 @@ #include #include "../internal.h" =20 +#ifndef CLONE_PIDFD +#define CLONE_PIDFD 0x00001000 +#endif + int is_skas_winch(int pid, int fd, void *data) { return pid =3D=3D getpgrp(); @@ -448,6 +452,9 @@ int start_userspace(struct mm_id *mm_id) void *stack; unsigned long sp; int status, n, err; + int stub_pidfd =3D -1; + + mm_id->stub_pidfd =3D -1; =20 /* setup a temporary stack page */ stack =3D mmap(NULL, UM_KERN_PAGE_SIZE, @@ -474,15 +481,25 @@ int start_userspace(struct mm_id *mm_id) if (using_seccomp) proc_data->futex =3D FUTEX_IN_CHILD; =20 + /* + * In SECCOMP mode, acquire a pidfd to the stub via CLONE_PIDFD (it is + * returned through the legacy-clone parent_tid argument). The monitor + * installs guest mappings into the stub's mm directly via pidfd_mmap(), + * so the stub itself never needs the mmap capability. The ptrace mode + * does not use it and drives the stub directly. + */ mm_id->pid =3D clone(userspace_tramp, (void *) sp, - CLONE_VFORK | CLONE_VM | SIGCHLD, - (void *)&tramp_data); + CLONE_VFORK | CLONE_VM | (using_seccomp ? CLONE_PIDFD : 0) | + SIGCHLD, + (void *)&tramp_data, &stub_pidfd); if (mm_id->pid < 0) { err =3D -errno; printk(UM_KERN_ERR "%s : clone failed, errno =3D %d\n", __func__, errno); goto out_close; } + if (using_seccomp) + mm_id->stub_pidfd =3D stub_pidfd; =20 if (using_seccomp) { wait_stub_done_seccomp(mm_id, 1, 1); @@ -534,8 +551,11 @@ int start_userspace(struct mm_id *mm_id) out_close: close(tramp_data.sockpair[0]); close(tramp_data.sockpair[1]); + if (stub_pidfd >=3D 0) + close(stub_pidfd); =20 mm_id->pid =3D -1; + mm_id->stub_pidfd =3D -1; =20 return err; } --=20 2.43.0 From nobody Mon Jul 27 02:14:44 2026 Received: from mail-pj1-f50.google.com (mail-pj1-f50.google.com [209.85.216.50]) (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 DA1B43AEF3F for ; Fri, 10 Jul 2026 20:54:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716843; cv=none; b=hjxH7VnxxH2kFM3qp2yVdp9j9bFYZvp4vHthdyE+3cMgOzyB89IPsOh/RpjhebGP5NpteqvTQgRc3e74RhKL3tcZrdYG4p1SsMcL+1ZUPYGYTWSFBCWn8etiRZt52Gc3PXgCb0VnP6BJjtSEGWbcfojFqE+tu+xPw7zhdPwl37Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716843; c=relaxed/simple; bh=8qAqz7cg5f8GkHTrwDXGFOM6ENG8y+b2fi75L4bJTLs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pQzaPc+tmD1Ufce9whWXki/a/kDR16t83ym0qNlB6fdmDve0VnIuNaCcudJsmwX++yRI7PetscLFSy02v0lldZ4hAGrs7b5mye4O4XRzNWiDv7VzC+BcmUc3Vlhwsa09pLsnDjS9Taqu/MsqeuSVdhHDMZdMNqe7wgq0W0/i9D4= 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=DE+ajnQH; arc=none smtp.client-ip=209.85.216.50 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="DE+ajnQH" Received: by mail-pj1-f50.google.com with SMTP id 98e67ed59e1d1-36b9d265355so1211341a91.2 for ; Fri, 10 Jul 2026 13:54:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783716839; x=1784321639; 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=UWjxCj/TKTNYVdvKD41KqqSUzOm1MXKHzbWV9E2lCOM=; b=DE+ajnQH8HQxXlJIWpQzt/rFuOThFKrQhVisxd/iJNVkFL1PXumBEVWY3vBcFhm7wt cgyxEVKgKezU+TUVjho9Ik9fjNGE3lWXo9jcQdM4G0Z/P+y8dytTBNNY/9JHImshzDun dIKk7/8sTLcXKf6RHvLwM95xD5ha0BhiyRbrrwn4DPYBWJG4/SJi3Bi+oiXZdNb2cAlV RiYlrKQ5jIRSN569RyyKoIUdklcekB3aTLXfBiOznzbKmnW8ANp+EDog23GNCbtEdbWt M1c8WamCe7LE7Q+WZK0J/vA7OKM2ctTQNBlGaXFsL4CnN1b0oG7pZ49WD2tJmngM7boy J4vQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783716839; x=1784321639; 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=UWjxCj/TKTNYVdvKD41KqqSUzOm1MXKHzbWV9E2lCOM=; b=EDLGExD9/Jx6mEv6+EkxqdwWrYe7Q+/cLCW+nXB5zessRXuVMD3hDiIvyXwNenCkiK E1u7pB8KYGUiyg69Lik1iyuaArmYpZEgGRkrtDuTDMAc+VFEzsx3lQS6M9ba/VmZmGf7 BMNYqCWCF1r3HYpmtgaKKiJ8hJ2B+r1hCuoI1E/xlEuH3cTBW+xEUpzJSK4vxGN2IbWo ZcpoTKDXRBZq/zyhCVuzo1suQasqCK9xcWWRdaMuI7Gw/g6cM96iM2PaTS6xRCncqr39 an9WSUnEN+St8G55UMRrjrZ98alBezmhyZB40X0zRURpb9FYfEI0Keq2RcH4INOOdc56 zbnw== X-Forwarded-Encrypted: i=1; AHgh+RoJJPJNSv0rDOAXjnU0gTwBPUVOHQhbkjR0ijPcJ4iDB8Xcft2DYLkJ9smPInIfhvKDd4jIZy+5caermnU=@vger.kernel.org X-Gm-Message-State: AOJu0YzGysQIL3112dsBybGgjH7oc9YujGYgjAhCKZCDJEa7GxufHolX 4H26ddknR73OtVCpiZPNvmzr0IVx/uBw9CG7gD2shu/84DXvYHdrAdzr X-Gm-Gg: AfdE7ckfOgcNYDhEaeaLuguoqj5jvXIkU+VLx7GCMIqoMOR0GEKZZf1qFKpVd1BiUxw vUv7RJ02NFnUWFD/YE6SURcZWwXEwvXG2UTq6qaecpprKS9OwW9XPzSf/eu0Zf/KDS19vo34jlr g20CzXXUPLnkYRgDUiLyxVLaue4mnS0KplZgx30a7UmLNOd7y/oBmgOEBXHJb8OuMR1JfBArthr sD7XWoGMVTaukXI+4TrurgRu1G1G0HNYHzwXZ2amOPBtPX8Yz88Smc63SH0dxWJYNs3NAQIuW41 a9s7PLYF7J5sKnWZGdM6mKkUdrno6huZN9G+sxWQWXNenwllspTuG8QGDTYIoTegsA2NegAMjFd FQ0MzUi8rMvrT+vq8uVFvCCqbUzuqxQXARIzk8CHcadgSQV7rM3Vu/tNx3HdabNax7LjAK92MK/ +W9lg4g9PtzllUh10GibRO/ZYph5j3BBF8JivX45Ydu3hRVx70etT6ABg= X-Received: by 2002:a17:90b:5583:b0:383:f52b:d616 with SMTP id 98e67ed59e1d1-38dc75e3a5fmr461534a91.10.1783716839175; Fri, 10 Jul 2026 13:53:59 -0700 (PDT) Received: from pop-os.scu.edu ([129.210.115.107]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3119c2a7bb5sm21724371eec.25.2026.07.10.13.53.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 13:53:58 -0700 (PDT) From: Cong Wang To: Richard Weinberger , Anton Ivanov , Johannes Berg Cc: Benjamin Berg , linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, Cong Wang Subject: [RFC PATCH 3/6] um: install guest mappings via pidfd_mmap() in seccomp mode Date: Fri, 10 Jul 2026 13:53:21 -0700 Message-ID: <20260710205324.1343217-4-xiyou.wangcong@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260710205324.1343217-1-xiyou.wangcong@gmail.com> References: <20260710205324.1343217-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: quoted-printable Content-Type: text/plain; charset="utf-8" From: Cong Wang In SECCOMP mode, map()/unmap() now install into the stub's address space directly from the monitor via pidfd_mmap()/pidfd_munmap(), resolving the physmem fd in the monitor's own fd table. The stub neither receives the fd nor executes mmap/munmap itself. The ptrace (SKAS0) mode is unchanged: it keeps batching STUB_SYSCALL_MMAP /MUNMAP for the stub to execute, which works on any host kernel and needs no new syscall. Only the seccomp path, where the stub is sandboxed, is converted. Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cong Wang --- arch/um/os-Linux/skas/mem.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/um/os-Linux/skas/mem.c b/arch/um/os-Linux/skas/mem.c index 8b9921ac3ef8..f751a1ea1c4c 100644 --- a/arch/um/os-Linux/skas/mem.c +++ b/arch/um/os-Linux/skas/mem.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -236,6 +238,21 @@ int map(struct mm_id *mm_idp, unsigned long virt, unsi= gned long len, int prot, { struct stub_syscall *sc; =20 + if (using_seccomp) { + struct pidfd_mmap_args args =3D { + .size =3D sizeof(args), + .addr =3D virt, + .len =3D len, + .prot =3D prot, + .flags =3D MAP_SHARED | MAP_FIXED, + .pgoff =3D offset >> UM_KERN_PAGE_SHIFT, + .fd =3D phys_fd, + }; + long res =3D syscall(__NR_pidfd_mmap, mm_idp->stub_pidfd, &args, 0); + + return res < 0 ? -errno : 0; + } + /* Compress with previous syscall if that is possible */ sc =3D syscall_stub_get_previous(mm_idp, STUB_SYSCALL_MMAP, virt); if (sc && sc->mem.prot =3D=3D prot && @@ -268,6 +285,13 @@ int unmap(struct mm_id *mm_idp, unsigned long addr, un= signed long len) { struct stub_syscall *sc; =20 + if (using_seccomp) { + long res =3D syscall(__NR_pidfd_munmap, mm_idp->stub_pidfd, + addr, len); + + return res < 0 ? -errno : 0; + } + /* Compress with previous syscall if that is possible */ sc =3D syscall_stub_get_previous(mm_idp, STUB_SYSCALL_MUNMAP, addr); if (sc) { --=20 2.43.0 From nobody Mon Jul 27 02:14:44 2026 Received: from mail-pj1-f43.google.com (mail-pj1-f43.google.com [209.85.216.43]) (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 B6B5E3B27C4 for ; Fri, 10 Jul 2026 20:54:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716845; cv=none; b=R1BcVENEctC9NRLWhb0/rgECfza/v7VEItn29O6PUY0CucbrpKbFzlmlUejt4zroELeLYDLnhSfRS32i8/oR8hFM9S3TdHyg4+CGTy4iY6l6t0ibfXDxqZPopr+A6zSinvydR0uMbzpLiXRrsoDtuTqgvZMldYzz8jxgjIuVdEw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716845; c=relaxed/simple; bh=ojRTkUiKvu/X9Yw9tLyjUCTFXVcDut/fZfe5MtwhrdE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=p2FNoQQ5IEXcXUk1hlAz9pnGlUxuy2dhxw5IDCYi7rOYwlPa5oY7ullxRbAi4IBEjhM2JGLAIwOgcoAi8KQTk9dZGJ5v3lkYabXSMOkFnHlKlYBJ5ENwH9+Dy7GGT3LyO9LAAuOO60JGc7RKCQ9fSzJ7BKVb6UzPExA5G5IaZi0= 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=G/Cp2LKR; arc=none smtp.client-ip=209.85.216.43 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="G/Cp2LKR" Received: by mail-pj1-f43.google.com with SMTP id 98e67ed59e1d1-36b9d265355so1211354a91.2 for ; Fri, 10 Jul 2026 13:54:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783716841; x=1784321641; 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=aT1mgykKIjplp+s22AEABT75vPOdQw6AoiJWmWFEB/s=; b=G/Cp2LKRIzizoTRuRDixdSVUSosYIHUE8yrFNsaHLP5iLuhwZANptd4/ICJAjt5htP riH5jVdm4glXFdr42zwXU8A3IJfQ4+QzGhZpP+Z9l2Vx9tDmojNvFqq/MUswbPNSARxG VaDuWOmPqoqa9Oi72i4No05PEgAn1UD3yxu8s81uOXM/7RChgb0oZ0HTSkzubgINVEpC NjamLW/qhkwoCLZzaizWNO2QwXXCYh44T0hlJ1UXLBdZ47NJFp12n5uCSWI/EXY17LXc dhv8s0jbfJOy12Oj4W0JImnqrS4wJ3djMocyB+CfV+vmYiUfFq4+4B9RmGZRTEFd2wQN gj8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783716841; x=1784321641; 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=aT1mgykKIjplp+s22AEABT75vPOdQw6AoiJWmWFEB/s=; b=V6/MeVxfMzR8KNSfflLZ6srW/zCLp66KvL43PJWUrualgp5gDZAKYoc8iEGNIiYyRs gclxuoHe/0ry4DtkQAYT/qEQrT3us2jGlLEaeX0R0liQfU6pensbWxReQgNB4vTnBecc zkULrFzFacaRXy4u5v/ZkEpHvNR2iwl0U5zuWtmgAIEbqd0ARYxujvVvWzD0XmTj3+m4 zuNKRZQ20zvkJp7R9jnDgQ+S4KC2ZcdGrEw7EZJXG6vOOoHggXaFvarv6wYToRAjzflR uvN2owmE6++Fk430zFiA7krlFu11nilDl/hbxR5yUaGz/jbT/x2jwb3fIXL9cJ4irH5L njZw== X-Forwarded-Encrypted: i=1; AHgh+RrQZE0De+vXTndOAA2FzK+XqFroDtesZQr9/VxzQoexhf1XNqyPYljCa1NTAgUvbmsIvMK5fj/IJGFV4VI=@vger.kernel.org X-Gm-Message-State: AOJu0YxeSk03fDCrexefzXfBJBeL3kAo8h7EKWABFrLiwKZvqaSZ7pTX wdGmUEN22E4IyEYNyysH2lUo+eooTGjgLcr8MItBs97gZM3aVc3+Virq X-Gm-Gg: AfdE7ckyy2dGW1mbFP/X0Ncuk9y+pxavY1hnWeZmmHexpj1FfuUeH4w7xoaSwJTM3aD 7CoK5jCEfHCx8XotZ/hKzdfVanG+rrnRz0i5m5YMcHyafyzv3eU8/vwA/HYUBCsKKv3FGXqRFcs wLnLOoxPCGNwxbKleOMvPNEL4tay1CXz2hCP7k7lhMO2Tt5xG/bJCP4Au7e/3/hRvC9YoOBFKoB AcLcjpSVIm8v5D+Mac6PrCYZXCpKnU8wJQJc8Qy+VP65J0xLwBiieTma7rCSnlo8k4bopY1fPlc 6xYJPbwrRiZmhQVsgvYz5I/4+uVQ5BbQO6TIvm1VlEDUdIbowyL6qpUwjc4JHvIXA2mwoFRU12i cE1tJwdxm1Pr0Uk5v24v1GeW7BOYrHVd79aJjXImT+jF3tPHUfiXrPlf9I7F/aimtvI3/rPTL7z LH3BqCLc2JPCt4lVjHFNdtCZqhGT2yoJ/CLX7jmYSqQoAo1csbYzYlReM= X-Received: by 2002:a17:90b:5545:b0:36b:944b:fd81 with SMTP id 98e67ed59e1d1-38dc74d0274mr494219a91.4.1783716840931; Fri, 10 Jul 2026 13:54:00 -0700 (PDT) Received: from pop-os.scu.edu ([129.210.115.107]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3119c2a7bb5sm21724371eec.25.2026.07.10.13.53.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 13:54:00 -0700 (PDT) From: Cong Wang To: Richard Weinberger , Anton Ivanov , Johannes Berg Cc: Benjamin Berg , linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, Cong Wang Subject: [RFC PATCH 4/6] um: forbid mmap/munmap in the stub seccomp filter Date: Fri, 10 Jul 2026 13:53:22 -0700 Message-ID: <20260710205324.1343217-5-xiyou.wangcong@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260710205324.1343217-1-xiyou.wangcong@gmail.com> References: <20260710205324.1343217-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: quoted-printable Content-Type: text/plain; charset="utf-8" From: Cong Wang In seccomp mode the monitor now installs all guest mappings via pidfd_mmap(), so the stub has no legitimate need to mmap/munmap after its filter is in place (the pre-filter bootstrap mmaps are unaffected). Drop the STUB_MMAP_NR and __NR_munmap allowlist arms and fix the jump offsets; both now hit SECCOMP_RET_KILL_PROCESS. A hijacked stub can no longer map arbitrary physmem. The filter exists only in seccomp mode, so ptrace (SKAS0) is untouched. Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cong Wang --- arch/um/kernel/skas/stub_exe.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/arch/um/kernel/skas/stub_exe.c b/arch/um/kernel/skas/stub_exe.c index cbafaa684e66..a88274449168 100644 --- a/arch/um/kernel/skas/stub_exe.c +++ b/arch/um/kernel/skas/stub_exe.c @@ -165,16 +165,18 @@ noinline static void real_init(void) BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)), =20 - /* [10-16] Check against permitted syscalls */ + /* + * [10-14] Check against permitted syscalls. mmap and + * munmap are deliberately absent: in seccomp mode the + * monitor installs all guest mappings via pidfd_mmap(), + * so a stub that attempts mmap/munmap hits the KILL at + * [15]. + */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_futex, - 7, 0), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,__NR_recvmsg, - 6, 0), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,__NR_close, 5, 0), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, STUB_MMAP_NR, + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_recvmsg, 4, 0), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_munmap, + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_close, 3, 0), #ifdef __i386__ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_set_thread_area, @@ -186,10 +188,10 @@ noinline static void real_init(void) BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_rt_sigreturn, 1, 0), =20 - /* [17] Not one of the permitted syscalls */ + /* [15] Not one of the permitted syscalls */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS), =20 - /* [18] Permitted call for the stub */ + /* [16] Permitted call for the stub */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), }; struct sock_fprog prog =3D { --=20 2.43.0 From nobody Mon Jul 27 02:14:44 2026 Received: from mail-pg1-f173.google.com (mail-pg1-f173.google.com [209.85.215.173]) (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 0312A3AFCF0 for ; Fri, 10 Jul 2026 20:54:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716849; cv=none; b=Ua3kqqsjnZmZY6BZhRYnge1e4cYyvkG0Nk+h96IU0Xznf7G3/m4lgZamrRY0QKS6iqHd+EHtAyUX5V3oXZkDt28kuRQF6Q5x19uLxMedAurXXnzaKv647IzkSWr7wKZShSVc5FJm5aPDYlUZUVXzrAcKd6EmeKtGWcc4vN4JRbg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716849; c=relaxed/simple; bh=6TQm8jik3p2ghQlqIwiRd+fnZWBXNZgB7jUPKM4YXGw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SzSTYnhgrSVrxNB0rhTtOD6nugayJXOSJrgcLI/UuV8CWImPelmZYyYRQqUTYoElLZxNvFz+3Pf82UewOvea+q4rvHBWvPrUAki2vgwU0h3S02ktSf2COB60b17/o4C1bo+meEbqb1lau6o9rh0wxiYwmUtAQj4yOVESRvdPEOk= 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=XPos0K1G; arc=none smtp.client-ip=209.85.215.173 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="XPos0K1G" Received: by mail-pg1-f173.google.com with SMTP id 41be03b00d2f7-c96cb024ee0so965094a12.1 for ; Fri, 10 Jul 2026 13:54:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783716843; x=1784321643; 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=n7rWVvgV3uvDQnachgCKO9gIdalZXrgrsCv3l41RcBw=; b=XPos0K1GBvssgwf+VdtvbxRuaWN5GgUeB+7WPQ+3637BafKtrvpA1fn3o4Z0a824J3 ram2yZYbhAYdaWnqXsqzIzVSFZ2/0eNDA8DJrI++6bL9ZDkBisUnocHGgIhvA+C/PRNO UhahSW3wLg0oc9HvSI0sDpKeq43Hozzm9GRnKoYr7uV7y9vOb7a+otJ8vOsEqjdLuOjQ Hlp9MlFKVEZ9ni+9ZwRJCTPknqvCmzNxdzIcomKi9401KVQ6oGnCvpNQ4o70/yWFQVny PZ56i6cZq3j4Fvifw1Xjlxo7UZ2+uXXrMP9KljMJ9lm+UXCsfoN0BnLoUR/+D+EkGlHt rG/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783716843; x=1784321643; 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=n7rWVvgV3uvDQnachgCKO9gIdalZXrgrsCv3l41RcBw=; b=dPPcVueFIG1XzFa1Ia8D9IgaFmpUBb7vJYTy08H+gxl1ewowSI8xh28++w13HbG32a uh65lFVuJmydXrHB0Hz4ojRZ4OgC4c143lCkD5scOfizVofeVz+uyrR8krOLTqGsnR5o cAOiPIymrooq1dbXBqf+WLjffwypJJc2MJxa78XXBV3itFxBUxawMrFqxVZ8SwH6hJTe v+V/k624K/6UysuF4EuUBIlyZ8rig7DALrVsR9NUDxBNQEDXqs6ZaP8X6/+EQ4dhG/vb roqDU34Vq00tYrdahTlwn0wuUFK66nqXugnNrzFzm5JxxFOOekF20PjWW7hK/NMPSl7S a3gg== X-Forwarded-Encrypted: i=1; AHgh+Rr38m6Wp+HnNLNaCPm4QecQvxPFIBfmmAezgLEIQyuEdQlvjdHTWCUFrGpfNInoX0zSClvvUgvdg711n4c=@vger.kernel.org X-Gm-Message-State: AOJu0Yy6Hxvttug/lZy0pgOwfO2O97a6H1cATzbxijpOQooZ4piHHDzB 5L8SawSEpIqJSag3wPT9oDWEt5BBM7xTi8YsxDbQUzqrHzXuU2CVESBJ X-Gm-Gg: AfdE7ck0n4X5DLqpNLylmtUbQLtaoUd8iE9znXeCde6BV6QkZNjRlijGLaSkC7MYGqR WhCP8XwpN/x3VRDuePgJEs2uhE3ZQxTYcaSa0Lle0tWQn5DAFTRbxGfV9bA7CXEN0x+qI8xgY1/ s7Scn1tO+r/KFC+B4afa1dugBFchalXcaj/1Ytvq6hXZ0xM2JIn4tjbYlPAOVJwfqenuguAiN/f 9cVF92x9Fj2wcXGfEiCpyZBRs2qFHEBIj9SqEttlY3Im+/m8LUCPEzhs1tz3yDVeDLQTZ+BaNiv 2eJqBs0NIv41HvfuBuEOTUjhgpexdwz0eL8sGd3eKuU3sUwR+c+043PlYk7wcg1I+MGlFBzCM/z CUKKjTnPNCfbxY/tNhsnL2g60lfZl2UVuttJT/E6I16T62Pch5MPWW88ji7SnOJ/WoiJhNNr+16 vWCqXrx7oDT3hS4UJTSeNDqD+zaEczbDP+qFG9qyhUFpN1OmqKYJseCYc= X-Received: by 2002:a05:6a20:a109:b0:3c0:9c18:d5a6 with SMTP id adf61e73a8af0-3c1110784f4mr529762637.67.1783716842812; Fri, 10 Jul 2026 13:54:02 -0700 (PDT) Received: from pop-os.scu.edu ([129.210.115.107]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3119c2a7bb5sm21724371eec.25.2026.07.10.13.54.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 13:54:02 -0700 (PDT) From: Cong Wang To: Richard Weinberger , Anton Ivanov , Johannes Berg Cc: Benjamin Berg , linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, Cong Wang Subject: [RFC PATCH 5/6] um: install guest mappings via pidfd_mmap() in both modes Date: Fri, 10 Jul 2026 13:53:23 -0700 Message-ID: <20260710205324.1343217-6-xiyou.wangcong@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260710205324.1343217-1-xiyou.wangcong@gmail.com> References: <20260710205324.1343217-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: quoted-printable Content-Type: text/plain; charset="utf-8" From: Cong Wang Route both seccomp and ptrace (SKAS0) map()/unmap() through pidfd_mmap()/pidfd_munmap(): the monitor resolves the physmem fd in its own fd table and installs the mapping into the stub's mm directly, so the stub never executes mmap/munmap itself in either mode. Neither mode drives the stub to execute syscalls anymore, so the whole stub-syscall batcher and the seccomp-only SCM_RIGHTS fd-passing become dead code and are removed. The seccomp model's inherent risks are not addressed by pidfd_mmap() (a malicious guest can still block SIGALRM to dodge scheduling), so that note is preserved in stub.c. This is an RFC demonstration of pidfd_mmap()'s reach; it assumes a host kernel that provides the syscall and does not add a fallback for older hosts intentionally. Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cong Wang --- arch/um/include/shared/os.h | 4 - arch/um/include/shared/skas/mm_id.h | 7 - arch/um/include/shared/skas/stub-data.h | 28 --- arch/um/kernel/skas/mmu.c | 5 - arch/um/kernel/skas/stub.c | 151 ++---------- arch/um/kernel/skas/stub_exe.c | 16 +- arch/um/os-Linux/skas/mem.c | 295 ++---------------------- arch/um/os-Linux/skas/process.c | 86 +------ arch/um/os-Linux/start_up.c | 8 +- 9 files changed, 67 insertions(+), 533 deletions(-) diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index b26e94292fc1..c6917583ca64 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h @@ -280,10 +280,6 @@ extern long long os_persistent_clock_emulation(void); extern long long os_nsecs(void); =20 /* skas/mem.c */ -int syscall_stub_flush(struct mm_id *mm_idp); -struct stub_syscall *syscall_stub_alloc(struct mm_id *mm_idp); -void syscall_stub_dump_error(struct mm_id *mm_idp); - int map(struct mm_id *mm_idp, unsigned long virt, unsigned long len, int prot, int phys_fd, unsigned long long offset); diff --git a/arch/um/include/shared/skas/mm_id.h b/arch/um/include/shared/s= kas/mm_id.h index cec97189f12b..0f74e1728a1e 100644 --- a/arch/um/include/shared/skas/mm_id.h +++ b/arch/um/include/shared/skas/mm_id.h @@ -8,18 +8,11 @@ =20 #include =20 -#define STUB_MAX_FDS 4 - struct mm_id { int pid; unsigned long stack; - int syscall_data_len; =20 - /* Only used with SECCOMP mode */ int stub_pidfd; /* pidfd to the stub, or -1 */ - int sock; - int syscall_fd_num; - int syscall_fd_map[STUB_MAX_FDS]; }; =20 struct mutex *__get_turnstile(struct mm_id *mm_id); diff --git a/arch/um/include/shared/skas/stub-data.h b/arch/um/include/shar= ed/skas/stub-data.h index 27db38e95df9..eaa208bcdaff 100644 --- a/arch/um/include/shared/skas/stub-data.h +++ b/arch/um/include/shared/skas/stub-data.h @@ -31,36 +31,8 @@ struct stub_init_data { unsigned long signal_restorer; }; =20 -#define STUB_NEXT_SYSCALL(s) \ - ((struct stub_syscall *) (((unsigned long) s) + (s)->cmd_len)) - -enum stub_syscall_type { - STUB_SYSCALL_UNSET =3D 0, - STUB_SYSCALL_MMAP, - STUB_SYSCALL_MUNMAP, -}; - -struct stub_syscall { - struct { - unsigned long addr; - unsigned long length; - unsigned long offset; - int fd; - int prot; - } mem; - - enum stub_syscall_type syscall; -}; - struct stub_data { - long err; - - int syscall_data_len; - /* 128 leaves enough room for additional fields in the struct */ - struct stub_syscall syscall_data[(UM_KERN_PAGE_SIZE - 128) / sizeof(struc= t stub_syscall)] __aligned(16); - /* data shared with signal handler (only used in seccomp mode) */ - short restart_wait; unsigned int futex; int signal; unsigned short si_offset; diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c index 441dcf94ec9c..b2c9cf023789 100644 --- a/arch/um/kernel/skas/mmu.c +++ b/arch/um/kernel/skas/mmu.c @@ -55,8 +55,6 @@ int init_new_context(struct task_struct *task, struct mm_= struct *mm) =20 new_id->stack =3D stack; new_id->stub_pidfd =3D -1; - new_id->syscall_data_len =3D 0; - new_id->syscall_fd_num =3D 0; =20 scoped_guard(spinlock_irqsave, &mm_list_lock) { /* Insert into list, used for lookups when the child dies */ @@ -109,9 +107,6 @@ void destroy_context(struct mm_struct *mm) mmu->id.stub_pidfd =3D -1; } =20 - if (using_seccomp && mmu->id.sock) - os_close_file(mmu->id.sock); - free_pages(mmu->id.stack, ilog2(STUB_DATA_PAGES)); } =20 diff --git a/arch/um/kernel/skas/stub.c b/arch/um/kernel/skas/stub.c index e09216a20cb5..08443f2eb09d 100644 --- a/arch/um/kernel/skas/stub.c +++ b/arch/um/kernel/skas/stub.c @@ -6,119 +6,46 @@ #include =20 #include -#include #include =20 /* - * Known security issues + * Known security issues (SECCOMP userspace) * - * Userspace can jump to this address to execute *any* syscall that is - * permitted by the stub. As we will return afterwards, it can do - * whatever it likes, including: - * - Tricking the kernel into handing out the memory FD - * - Using this memory FD to read/write all physical memory - * - Running in parallel to the kernel processing a syscall - * (possibly creating data races?) - * - Blocking e.g. SIGALRM to avoid time based scheduling + * The stub shares its address space with the untrusted guest application. + * Installing guest mappings via pidfd_mmap() closed some earlier holes -- + * the stub no longer holds the physmem fd and no longer executes mmap/mun= map + * on the guest's behalf (both are rejected by the SECCOMP filter) -- but = the + * following are inherent to the model and remain unaddressed: * - * To avoid this, the permitted location for each syscall needs to be - * checked for in the SECCOMP filter (which is reasonably simple). Also, - * more care will need to go into considerations how the code might be - * tricked by using a prepared stack (or even modifying the stack from - * another thread in case SMP support is added). - * - * As for the SIGALRM, the best counter measure will be to check in the - * kernel that the process is reporting back the SIGALRM in a timely - * fashion. + * - A malicious guest can influence the stub's signal state (e.g. via a + * prepared stack restored on rt_sigreturn) and block SIGALRM to avoid + * time-based scheduling. The intended counter measure is for the monitor + * to check that the process reports SIGALRM back in a timely fashion; t= hat + * is not yet implemented. + * - The permitted location of each syscall the stub does issue must be + * pinned down in the SECCOMP filter, and more care is needed around a + * prepared stack (or the stack being modified from another thread should + * SMP support be added). */ -static __always_inline int syscall_handler(int fd_map[STUB_MAX_FDS]) -{ - struct stub_data *d =3D get_stub_data(); - int i; - unsigned long res; - int fd; - - for (i =3D 0; i < d->syscall_data_len; i++) { - struct stub_syscall *sc =3D &d->syscall_data[i]; - - switch (sc->syscall) { - case STUB_SYSCALL_MMAP: - if (fd_map) - fd =3D fd_map[sc->mem.fd]; - else - fd =3D sc->mem.fd; - - res =3D stub_syscall6(STUB_MMAP_NR, - sc->mem.addr, sc->mem.length, - sc->mem.prot, - MAP_SHARED | MAP_FIXED, - fd, sc->mem.offset); - if (res !=3D sc->mem.addr) { - d->err =3D res; - d->syscall_data_len =3D i; - return -1; - } - break; - case STUB_SYSCALL_MUNMAP: - res =3D stub_syscall2(__NR_munmap, - sc->mem.addr, sc->mem.length); - if (res) { - d->err =3D res; - d->syscall_data_len =3D i; - return -1; - } - break; - default: - d->err =3D -95; /* EOPNOTSUPP */ - d->syscall_data_len =3D i; - return -1; - } - } - - d->err =3D 0; - d->syscall_data_len =3D 0; - - return 0; -} - -void __section(".__syscall_stub") -stub_syscall_handler(void) -{ - syscall_handler(NULL); - - trap_myself(); -} =20 void __section(".__syscall_stub") stub_signal_interrupt(int sig, siginfo_t *info, void *p) { struct stub_data *d =3D get_stub_data(); - char rcv_data; - union { - char data[CMSG_SPACE(sizeof(int) * STUB_MAX_FDS)]; - struct cmsghdr align; - } ctrl =3D {}; - struct iovec iov =3D { - .iov_base =3D &rcv_data, - .iov_len =3D 1, - }; - struct msghdr msghdr =3D { - .msg_iov =3D &iov, - .msg_iovlen =3D 1, - .msg_control =3D &ctrl, - .msg_controllen =3D sizeof(ctrl), - }; ucontext_t *uc =3D p; - struct cmsghdr *fd_msg; - int *fd_map; - int num_fds; long res; =20 d->signal =3D sig; d->si_offset =3D (unsigned long)info - (unsigned long)&d->sigstack[0]; d->mctx_offset =3D (unsigned long)&uc->uc_mcontext - (unsigned long)&d->s= igstack[0]; =20 -restart_wait: + /* + * Hand the guest trap to the monitor and block until it resumes us: + * wake the monitor (FUTEX_WAKE) and FUTEX_WAIT until it is done. In + * seccomp mode the monitor installs any address-space changes itself + * via pidfd_mmap(), so the stub no longer executes syscalls on its + * behalf and never receives an fd. + */ d->futex =3D FUTEX_IN_KERN; do { res =3D stub_syscall3(__NR_futex, (unsigned long)&d->futex, @@ -133,40 +60,6 @@ stub_signal_interrupt(int sig, siginfo_t *info, void *p) if (res < 0 && res !=3D -EAGAIN) stub_syscall1(__NR_exit_group, 1); =20 - if (d->syscall_data_len) { - /* Read passed FDs (if any) */ - do { - res =3D stub_syscall3(__NR_recvmsg, 0, (unsigned long)&msghdr, 0); - } while (res =3D=3D -EINTR); - - /* We should never have a receive error (other than -EAGAIN) */ - if (res < 0 && res !=3D -EAGAIN) - stub_syscall1(__NR_exit_group, 1); - - /* Receive the FDs */ - num_fds =3D 0; - fd_msg =3D msghdr.msg_control; - fd_map =3D (void *)CMSG_DATA(fd_msg); - if (res =3D=3D iov.iov_len && msghdr.msg_controllen > sizeof(struct cmsg= hdr)) - num_fds =3D (fd_msg->cmsg_len - CMSG_LEN(0)) / sizeof(int); - - /* Try running queued syscalls. */ - res =3D syscall_handler(fd_map); - - while (num_fds) - stub_syscall2(__NR_close, fd_map[--num_fds], 0); - } else { - res =3D 0; - } - - if (res < 0 || d->restart_wait) { - /* Report SIGSYS if we restart. */ - d->signal =3D SIGSYS; - d->restart_wait =3D 0; - - goto restart_wait; - } - /* Restore arch dependent state that is not part of the mcontext */ stub_seccomp_restore_state(&d->arch_data); =20 diff --git a/arch/um/kernel/skas/stub_exe.c b/arch/um/kernel/skas/stub_exe.c index a88274449168..af1d011be0a4 100644 --- a/arch/um/kernel/skas/stub_exe.c +++ b/arch/um/kernel/skas/stub_exe.c @@ -166,15 +166,13 @@ noinline static void real_init(void) offsetof(struct seccomp_data, nr)), =20 /* - * [10-14] Check against permitted syscalls. mmap and - * munmap are deliberately absent: in seccomp mode the - * monitor installs all guest mappings via pidfd_mmap(), - * so a stub that attempts mmap/munmap hits the KILL at - * [15]. + * [10-13] Check against permitted syscalls. mmap, munmap + * and recvmsg are deliberately absent: in seccomp mode + * the monitor installs all guest mappings via + * pidfd_mmap() and no longer passes fds to the stub, so a + * stub that attempts mmap/munmap hits the KILL at [14]. */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_futex, - 5, 0), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_recvmsg, 4, 0), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_close, 3, 0), @@ -188,10 +186,10 @@ noinline static void real_init(void) BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_rt_sigreturn, 1, 0), =20 - /* [15] Not one of the permitted syscalls */ + /* [14] Not one of the permitted syscalls */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS), =20 - /* [16] Permitted call for the stub */ + /* [15] Permitted call for the stub */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), }; struct sock_fprog prog =3D { diff --git a/arch/um/os-Linux/skas/mem.c b/arch/um/os-Linux/skas/mem.c index f751a1ea1c4c..9330f97f8beb 100644 --- a/arch/um/os-Linux/skas/mem.c +++ b/arch/um/os-Linux/skas/mem.c @@ -22,287 +22,34 @@ #include #include "../internal.h" =20 -extern char __syscall_stub_start[]; - -void syscall_stub_dump_error(struct mm_id *mm_idp) -{ - struct stub_data *proc_data =3D (void *)mm_idp->stack; - struct stub_syscall *sc; - - if (proc_data->syscall_data_len < 0 || - proc_data->syscall_data_len >=3D ARRAY_SIZE(proc_data->syscall_data)) - panic("Syscall data was corrupted by stub (len is: %d, expected maximum:= %d)!", - proc_data->syscall_data_len, - mm_idp->syscall_data_len); - - sc =3D &proc_data->syscall_data[proc_data->syscall_data_len]; - - printk(UM_KERN_ERR "%s : length =3D %d, last offset =3D %d", - __func__, mm_idp->syscall_data_len, - proc_data->syscall_data_len); - printk(UM_KERN_ERR "%s : stub syscall type %d failed, return value =3D 0x= %lx\n", - __func__, sc->syscall, proc_data->err); - - print_hex_dump(UM_KERN_ERR, " syscall data: ", 0, - 16, 4, sc, sizeof(*sc), 0); - - if (using_seccomp) { - printk(UM_KERN_ERR "%s: FD map num: %d", __func__, - mm_idp->syscall_fd_num); - print_hex_dump(UM_KERN_ERR, - " FD map: ", 0, 16, - sizeof(mm_idp->syscall_fd_map[0]), - mm_idp->syscall_fd_map, - sizeof(mm_idp->syscall_fd_map), 0); - } -} - -static inline unsigned long *check_init_stack(struct mm_id * mm_idp, - unsigned long *stack) -{ - if (stack =3D=3D NULL) { - stack =3D (unsigned long *) mm_idp->stack + 2; - *stack =3D 0; - } - return stack; -} - -static unsigned long syscall_regs[MAX_REG_NR]; - -static int __init init_syscall_regs(void) -{ - get_safe_registers(syscall_regs, NULL); - - syscall_regs[REGS_IP_INDEX] =3D STUB_CODE + - ((unsigned long) stub_syscall_handler - - (unsigned long) __syscall_stub_start); - syscall_regs[REGS_SP_INDEX] =3D STUB_DATA + - offsetof(struct stub_data, sigstack) + - sizeof(((struct stub_data *) 0)->sigstack) - - sizeof(void *); - - return 0; -} - -__initcall(init_syscall_regs); - -static inline long do_syscall_stub(struct mm_id *mm_idp) -{ - struct stub_data *proc_data =3D (void *)mm_idp->stack; - int n, i; - int err, pid =3D mm_idp->pid; - - /* Inform process how much we have filled in. */ - proc_data->syscall_data_len =3D mm_idp->syscall_data_len; - - if (using_seccomp) { - proc_data->restart_wait =3D 1; - wait_stub_done_seccomp(mm_idp, 0, 1); - } else { - n =3D ptrace_setregs(pid, syscall_regs); - if (n < 0) { - printk(UM_KERN_ERR "Registers -\n"); - for (i =3D 0; i < MAX_REG_NR; i++) - printk(UM_KERN_ERR "\t%d\t0x%lx\n", i, syscall_regs[i]); - panic("%s : PTRACE_SETREGS failed, errno =3D %d\n", - __func__, -n); - } - - err =3D ptrace(PTRACE_CONT, pid, 0, 0); - if (err) - panic("Failed to continue stub, pid =3D %d, errno =3D %d\n", - pid, errno); - - wait_stub_done(pid); - } - - /* - * proc_data->err will be negative if there was an (unexpected) error. - * In that case, syscall_data_len points to the last executed syscall, - * otherwise it will be zero (but we do not need to rely on that). - */ - if (proc_data->err < 0) { - syscall_stub_dump_error(mm_idp); - - /* Store error code in case someone tries to add more syscalls */ - mm_idp->syscall_data_len =3D proc_data->err; - } else { - mm_idp->syscall_data_len =3D 0; - } - - if (using_seccomp) - mm_idp->syscall_fd_num =3D 0; - - return mm_idp->syscall_data_len; -} - -int syscall_stub_flush(struct mm_id *mm_idp) -{ - int res; - - if (mm_idp->syscall_data_len =3D=3D 0) - return 0; - - /* If an error happened already, report it and reset the state. */ - if (mm_idp->syscall_data_len < 0) { - res =3D mm_idp->syscall_data_len; - mm_idp->syscall_data_len =3D 0; - return res; - } - - res =3D do_syscall_stub(mm_idp); - mm_idp->syscall_data_len =3D 0; - - return res; -} - -struct stub_syscall *syscall_stub_alloc(struct mm_id *mm_idp) -{ - struct stub_syscall *sc; - struct stub_data *proc_data =3D (struct stub_data *) mm_idp->stack; - - if (mm_idp->syscall_data_len > 0 && - mm_idp->syscall_data_len =3D=3D ARRAY_SIZE(proc_data->syscall_data)) - do_syscall_stub(mm_idp); - - if (mm_idp->syscall_data_len < 0) { - /* Return dummy to retain error state. */ - sc =3D &proc_data->syscall_data[0]; - } else { - sc =3D &proc_data->syscall_data[mm_idp->syscall_data_len]; - mm_idp->syscall_data_len +=3D 1; - } - memset(sc, 0, sizeof(*sc)); - - return sc; -} - -static struct stub_syscall *syscall_stub_get_previous(struct mm_id *mm_idp, - int syscall_type, - unsigned long virt) -{ - if (mm_idp->syscall_data_len > 0) { - struct stub_data *proc_data =3D (void *) mm_idp->stack; - struct stub_syscall *sc; - - sc =3D &proc_data->syscall_data[mm_idp->syscall_data_len - 1]; - - if (sc->syscall =3D=3D syscall_type && - sc->mem.addr + sc->mem.length =3D=3D virt) - return sc; - } - - return NULL; -} - -static int get_stub_fd(struct mm_id *mm_idp, int fd) -{ - int i; - - /* Find an FD slot (or flush and use first) */ - if (!using_seccomp) - return fd; - - /* Already crashed, value does not matter */ - if (mm_idp->syscall_data_len < 0) - return 0; - - /* Find existing FD in map if we can allocate another syscall */ - if (mm_idp->syscall_data_len < - ARRAY_SIZE(((struct stub_data *)NULL)->syscall_data)) { - for (i =3D 0; i < mm_idp->syscall_fd_num; i++) { - if (mm_idp->syscall_fd_map[i] =3D=3D fd) - return i; - } - - if (mm_idp->syscall_fd_num < STUB_MAX_FDS) { - i =3D mm_idp->syscall_fd_num; - mm_idp->syscall_fd_map[i] =3D fd; - - mm_idp->syscall_fd_num++; - - return i; - } - } - - /* FD map full or no syscall space available, continue after flush */ - do_syscall_stub(mm_idp); - mm_idp->syscall_fd_map[0] =3D fd; - mm_idp->syscall_fd_num =3D 1; - - return 0; -} - +/* + * Install (map) or remove (unmap) a guest mapping in the stub's address s= pace. + * Both seccomp and ptrace modes now go through pidfd_mmap()/pidfd_munmap(= ): + * the monitor resolves the physmem fd in its own fd table and installs the + * mapping into the stub's mm directly, so the stub never has to execute + * mmap/munmap itself. This makes the ptrace stub-syscall batcher unnecess= ary + * for memory management and shows the primitive works uniformly across mo= des. + */ int map(struct mm_id *mm_idp, unsigned long virt, unsigned long len, int p= rot, int phys_fd, unsigned long long offset) { - struct stub_syscall *sc; - - if (using_seccomp) { - struct pidfd_mmap_args args =3D { - .size =3D sizeof(args), - .addr =3D virt, - .len =3D len, - .prot =3D prot, - .flags =3D MAP_SHARED | MAP_FIXED, - .pgoff =3D offset >> UM_KERN_PAGE_SHIFT, - .fd =3D phys_fd, - }; - long res =3D syscall(__NR_pidfd_mmap, mm_idp->stub_pidfd, &args, 0); - - return res < 0 ? -errno : 0; - } + struct pidfd_mmap_args args =3D { + .size =3D sizeof(args), + .addr =3D virt, + .len =3D len, + .prot =3D prot, + .flags =3D MAP_SHARED | MAP_FIXED, + .pgoff =3D offset >> UM_KERN_PAGE_SHIFT, + .fd =3D phys_fd, + }; + long res =3D syscall(__NR_pidfd_mmap, mm_idp->stub_pidfd, &args, 0); =20 - /* Compress with previous syscall if that is possible */ - sc =3D syscall_stub_get_previous(mm_idp, STUB_SYSCALL_MMAP, virt); - if (sc && sc->mem.prot =3D=3D prot && - sc->mem.offset =3D=3D MMAP_OFFSET(offset - sc->mem.length)) { - int prev_fd =3D sc->mem.fd; - - if (using_seccomp) - prev_fd =3D mm_idp->syscall_fd_map[sc->mem.fd]; - - if (phys_fd =3D=3D prev_fd) { - sc->mem.length +=3D len; - return 0; - } - } - - phys_fd =3D get_stub_fd(mm_idp, phys_fd); - - sc =3D syscall_stub_alloc(mm_idp); - sc->syscall =3D STUB_SYSCALL_MMAP; - sc->mem.addr =3D virt; - sc->mem.length =3D len; - sc->mem.prot =3D prot; - sc->mem.fd =3D phys_fd; - sc->mem.offset =3D MMAP_OFFSET(offset); - - return 0; + return res < 0 ? -errno : 0; } =20 int unmap(struct mm_id *mm_idp, unsigned long addr, unsigned long len) { - struct stub_syscall *sc; - - if (using_seccomp) { - long res =3D syscall(__NR_pidfd_munmap, mm_idp->stub_pidfd, - addr, len); - - return res < 0 ? -errno : 0; - } - - /* Compress with previous syscall if that is possible */ - sc =3D syscall_stub_get_previous(mm_idp, STUB_SYSCALL_MUNMAP, addr); - if (sc) { - sc->mem.length +=3D len; - return 0; - } - - sc =3D syscall_stub_alloc(mm_idp); - sc->syscall =3D STUB_SYSCALL_MUNMAP; - sc->mem.addr =3D addr; - sc->mem.length =3D len; + long res =3D syscall(__NR_pidfd_munmap, mm_idp->stub_pidfd, addr, len); =20 - return 0; + return res < 0 ? -errno : 0; } diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/proces= s.c index 3dd97ca7999a..cd6e40451576 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -157,39 +157,7 @@ void wait_stub_done_seccomp(struct mm_id *mm_idp, int = running, int wait_sigsys) int ret; =20 do { - const char byte =3D 0; - struct iovec iov =3D { - .iov_base =3D (void *)&byte, - .iov_len =3D sizeof(byte), - }; - union { - char data[CMSG_SPACE(sizeof(mm_idp->syscall_fd_map))]; - struct cmsghdr align; - } ctrl; - struct msghdr msgh =3D { - .msg_iov =3D &iov, - .msg_iovlen =3D 1, - }; - if (!running) { - if (mm_idp->syscall_fd_num) { - unsigned int fds_size =3D - sizeof(int) * mm_idp->syscall_fd_num; - struct cmsghdr *cmsg; - - msgh.msg_control =3D ctrl.data; - msgh.msg_controllen =3D CMSG_SPACE(fds_size); - cmsg =3D CMSG_FIRSTHDR(&msgh); - cmsg->cmsg_level =3D SOL_SOCKET; - cmsg->cmsg_type =3D SCM_RIGHTS; - cmsg->cmsg_len =3D CMSG_LEN(fds_size); - memcpy(CMSG_DATA(cmsg), mm_idp->syscall_fd_map, - fds_size); - - CATCH_EINTR(syscall(__NR_sendmsg, mm_idp->sock, - &msgh, 0)); - } - data->signal =3D 0; data->futex =3D FUTEX_IN_CHILD; CATCH_EINTR(syscall(__NR_futex, &data->futex, @@ -482,15 +450,13 @@ int start_userspace(struct mm_id *mm_id) proc_data->futex =3D FUTEX_IN_CHILD; =20 /* - * In SECCOMP mode, acquire a pidfd to the stub via CLONE_PIDFD (it is - * returned through the legacy-clone parent_tid argument). The monitor - * installs guest mappings into the stub's mm directly via pidfd_mmap(), - * so the stub itself never needs the mmap capability. The ptrace mode - * does not use it and drives the stub directly. + * Acquire a pidfd to the stub via CLONE_PIDFD (it is returned through + * the legacy-clone parent_tid argument). The monitor installs guest + * mappings into the stub's mm directly via pidfd_mmap() in both seccomp + * and ptrace modes, so it needs the pidfd regardless of mode. */ mm_id->pid =3D clone(userspace_tramp, (void *) sp, - CLONE_VFORK | CLONE_VM | (using_seccomp ? CLONE_PIDFD : 0) | - SIGCHLD, + CLONE_VFORK | CLONE_VM | CLONE_PIDFD | SIGCHLD, (void *)&tramp_data, &stub_pidfd); if (mm_id->pid < 0) { err =3D -errno; @@ -498,8 +464,7 @@ int start_userspace(struct mm_id *mm_id) __func__, errno); goto out_close; } - if (using_seccomp) - mm_id->stub_pidfd =3D stub_pidfd; + mm_id->stub_pidfd =3D stub_pidfd; =20 if (using_seccomp) { wait_stub_done_seccomp(mm_id, 1, 1); @@ -539,10 +504,7 @@ int start_userspace(struct mm_id *mm_id) } =20 close(tramp_data.sockpair[0]); - if (using_seccomp) - mm_id->sock =3D tramp_data.sockpair[1]; - else - close(tramp_data.sockpair[1]); + close(tramp_data.sockpair[1]); =20 return 0; =20 @@ -620,28 +582,15 @@ void userspace(struct uml_pt_regs *regs) fatal_sigsegv(); } =20 - /* Must have been reset by the syscall caller */ - if (proc_data->restart_wait !=3D 0) - panic("Programming error: Flag to only run syscalls in child was not c= leared!"); - - /* Mark pending syscalls for flushing */ - proc_data->syscall_data_len =3D mm_id->syscall_data_len; - + /* + * SECCOMP mode no longer batches stub syscalls (mmap is + * installed by the monitor via pidfd_mmap()); this only + * relays the guest trap to the monitor. + */ wait_stub_done_seccomp(mm_id, 0, 0); =20 sig =3D proc_data->signal; =20 - if (sig =3D=3D SIGTRAP && proc_data->err !=3D 0) { - printk(UM_KERN_ERR "%s - Error flushing stub syscalls", - __func__); - syscall_stub_dump_error(mm_id); - mm_id->syscall_data_len =3D proc_data->err; - fatal_sigsegv(); - } - - mm_id->syscall_data_len =3D 0; - mm_id->syscall_fd_num =3D 0; - err =3D get_stub_state(regs, proc_data, NULL); if (err) { printk(UM_KERN_ERR "%s - failed to get regs: %d", @@ -667,17 +616,6 @@ void userspace(struct uml_pt_regs *regs) } else { int pid =3D mm_id->pid; =20 - /* Flush out any pending syscalls */ - err =3D syscall_stub_flush(mm_id); - if (err) { - if (err =3D=3D -ENOMEM) - report_enomem(); - - printk(UM_KERN_ERR "%s - Error flushing stub syscalls: %d", - __func__, -err); - fatal_sigsegv(); - } - /* * This can legitimately fail if the process loads a * bogus value into a segment register. It will diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c index 054ac03bbf5e..518107622a2d 100644 --- a/arch/um/os-Linux/start_up.c +++ b/arch/um/os-Linux/start_up.c @@ -311,9 +311,11 @@ static bool __init init_seccomp(void) PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, 0, 0); =20 - /* Use the syscall data area as stack, we just need something */ - sp =3D (unsigned long)&seccomp_test_stub_data->syscall_data + - sizeof(seccomp_test_stub_data->syscall_data) - + /* + * We just need some valid stack; use the first page of the shared + * stub_data region (sigstack occupies the following page). + */ + sp =3D (unsigned long)&seccomp_test_stub_data->sigstack[0] - sizeof(void *); pid =3D clone(seccomp_helper, (void *)sp, CLONE_VFORK | CLONE_VM, NULL); =20 --=20 2.43.0 From nobody Mon Jul 27 02:14:44 2026 Received: from mail-pg1-f177.google.com (mail-pg1-f177.google.com [209.85.215.177]) (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 CD01D3B2D1F for ; Fri, 10 Jul 2026 20:54:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716849; cv=none; b=pNRpLbyAEWH4N3IZuCv+kJqqWKBObhf5GVYEVbtg6E45N4EKGvx7sLhw9iwZdD1oDw5yuZVJfKjZcKQhoGcsQcOj781GHHQRFqPBAKCQlRzhBQ+oLweH+VzV+MBFVrA08bBQlX6iUDzNktl3L+FAC1EHV7S9Dv6T7ei3cEEt1xU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783716849; c=relaxed/simple; bh=zDHTtK7hS3Azj7M2spkwLakykltE+uxGEI8M/RWs8G8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NLsvP2rnuLNsPbCiZOSKUjfEn4eD62ONADjF4O4/f3umryCzTeaDAzS6GAMgvS1gPkHXK/xCv3tbBVDeppbsayzp3V8JBbgQssXFgCBttYDc/abmXbIyk5u7HQTQ1uIFO0uteuh3neyT+1RUpFx1yboMzppiyaaKx3mDLi7Lua8= 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=lZR0ieem; arc=none smtp.client-ip=209.85.215.177 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="lZR0ieem" Received: by mail-pg1-f177.google.com with SMTP id 41be03b00d2f7-c96c92c0980so808611a12.3 for ; Fri, 10 Jul 2026 13:54:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783716845; x=1784321645; 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=RNCHCTp2HRwWejyccEraGcBIln8xR0gwETjSA5NC5cY=; b=lZR0ieem3ZWnIH+9Lf5cyqgfZyw1nNstTbj3FxZ3I/ASwzVwf1KySR6lzfqPJGe+Bc hT4mWkcfA0ZnxCUZGBk4WgZZePeM17MuIZ02h4T7othzPw1L1yOtn4AZtHtVRrU2+V2r oIfG5dNLH6xgBEe6cpWKG30t1EHPnEopIo5z2hrbFACDzi7qff535ok1e6bVCLPqfWqN 80BOpjkeRg2qOPB1GMkar3L3sTX3lXp1h4VsrVyOw1tdvGQOcfNk3DpaJVROYs+5Y5wX JwvUx6bX85p4EAiuGWy1SWR4GZ+Aq4bvz3YfzVksV3d3V4Xo42C8LFzFYE05PPZaCd0T XGHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783716845; x=1784321645; 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=RNCHCTp2HRwWejyccEraGcBIln8xR0gwETjSA5NC5cY=; b=pTrECrQJ2+3jKUj4xIKfvUV8eT2NKBJ0JHAzgOEewaAx5QFpuknG32zei6IFUa1LrX hZr+uUhBteuQaIie/tCW860yqpbBa/ImbnoyFsbTLr+ALEmD3J6zuFWyrvYwFicw2sVj h0UArp3RGXLJbNWCL66fV88/+/pz82yJioy6KyIAW6JB3dRPZcn9xldrRy782tuRhCiJ VPdDe5fgXHX1ICcuP8O2O5sSGGXSYalej0M6VqPPvyq4mPgEzP8hW3psIAKCfLB5gRQ1 fxQYYHWfleHh7zAraU9sgYspc2WodVai13WpoIuX2Avi5mrMnTJ5sxkGaKx1Z0gdoy/g hG/w== X-Forwarded-Encrypted: i=1; AHgh+RpDmWNvHxEvbGHGMpDVcjBw87aCQjFoeNyR0+OtqSAMWBjxwdVD4aeItx9kLzSlRZM5vfJKnKLBV0k/ZCQ=@vger.kernel.org X-Gm-Message-State: AOJu0YyRVxr9AzV4bH5OV86WDy2kYEegZ4m/HLTDaxQYzkM42z6eAA10 vSyKKsrzVqCJg2rUz3GWkjREyB6dRPsSScHbLNMtZOxq/KQqcVXFOnQzu6QMYw== X-Gm-Gg: AfdE7ckJGSbJTmtCqOkkVfAHDRZgSJdreMTfSi9x26lKeQve3BaaGTfKr1PhYJqB0mm TQFMnUtczLFLL7ljjApAErj0pp82ziqiagS0NzYMBjVS8vUTw3szC/qbD9qaZufdxit1gzfdsLm LifNojQAsKZyV/mM9wyv8u+tb1Nn51btOVP+hA5ssMvapLj+O69y1bQyFXPusBAHUkb0YcuXYOW hc9hl3dwmmIWrbGD/Z3C+BaWAtlqOzF14weY1ohV2MJGUFIchTW7MOs0i5lX1suvsHLw4pMIo3L PU4O+Y9yxZFFI5zT0n/WjK5oF2xZJ3juyg5jBNX1al5iGNTbROf4BxwxT30vEpXQzVl1hlBEXVy JGNIx8vcYOMIrXUIQNJeT9wCe3HN7QC6YVWeW+OZdqy7WVZihU0dkGULXtaOdeCBR0SceJRR8wn vAoLVITSdkuMfLCXBronUyl2nGTbqlYdE2nkmLn4dlwr0uTn+suodMrj9O1bk+aAmvQw== X-Received: by 2002:a05:6a20:3d11:b0:3bd:4698:e7c4 with SMTP id adf61e73a8af0-3c110b166e1mr591264637.42.1783716844587; Fri, 10 Jul 2026 13:54:04 -0700 (PDT) Received: from pop-os.scu.edu ([129.210.115.107]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3119c2a7bb5sm21724371eec.25.2026.07.10.13.54.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 13:54:03 -0700 (PDT) From: Cong Wang To: Richard Weinberger , Anton Ivanov , Johannes Berg Cc: Benjamin Berg , linux-um@lists.infradead.org, linux-kernel@vger.kernel.org, Cong Wang Subject: [RFC PATCH 6/6] selftests/pidfd: add pidfd_mmap()/pidfd_munmap() tests Date: Fri, 10 Jul 2026 13:53:24 -0700 Message-ID: <20260710205324.1343217-7-xiyou.wangcong@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260710205324.1343217-1-xiyou.wangcong@gmail.com> References: <20260710205324.1343217-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: quoted-printable Content-Type: text/plain; charset="utf-8" From: Cong Wang Exercise the new syscalls against a forked target reached by pidfd: - install_and_read: map a parent-owned memfd into the target, verify the target's new mapping shows the memfd contents (read via process_vm_readv), that MAP_SHARED writes propagate, and that pidfd_munmap() removes it. This also covers the key semantic that the backing fd is resolved in the *caller's* fd table (the target never sees the memfd). - anonymous: MAP_ANONYMOUS install + write-back round-trip. - bad_args: __spare !=3D 0, undersized @size, non-zero reserved flags, and a bad backing fd are all rejected. - no_ptrace_access: a non-dumpable target (no ptrace access) yields -EPERM, matching the ptrace_may_access() gate. The suite skips cleanly (TAP "SKIP") when the kernel lacks the syscall. The pidfd_mmap ABI definitions are kept in pidfd.h alongside the existing local uapi copies (struct pidfd_info, etc.). Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cong Wang --- tools/testing/selftests/pidfd/Makefile | 3 +- tools/testing/selftests/pidfd/pidfd.h | 34 +++ .../testing/selftests/pidfd/pidfd_mmap_test.c | 234 ++++++++++++++++++ 3 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/pidfd/pidfd_mmap_test.c diff --git a/tools/testing/selftests/pidfd/Makefile b/tools/testing/selftes= ts/pidfd/Makefile index 4211f91e9af8..f1c421bf725a 100644 --- a/tools/testing/selftests/pidfd/Makefile +++ b/tools/testing/selftests/pidfd/Makefile @@ -4,7 +4,8 @@ CFLAGS +=3D -g $(KHDR_INCLUDES) $(TOOLS_INCLUDES) -pthread = -Wall TEST_GEN_PROGS :=3D pidfd_test pidfd_fdinfo_test pidfd_open_test \ pidfd_poll_test pidfd_wait pidfd_getfd_test pidfd_setns_test \ pidfd_file_handle_test pidfd_bind_mount pidfd_info_test \ - pidfd_xattr_test pidfd_setattr_test pidfd_autoreap_test + pidfd_xattr_test pidfd_setattr_test pidfd_autoreap_test \ + pidfd_mmap_test =20 TEST_GEN_PROGS_EXTENDED :=3D pidfd_exec_helper =20 diff --git a/tools/testing/selftests/pidfd/pidfd.h b/tools/testing/selftest= s/pidfd/pidfd.h index 5a4e78c10f43..91e65283d5fe 100644 --- a/tools/testing/selftests/pidfd/pidfd.h +++ b/tools/testing/selftests/pidfd/pidfd.h @@ -203,6 +203,28 @@ struct pidfd_info { __u64 supported_mask; }; =20 +#ifndef __NR_pidfd_mmap +#define __NR_pidfd_mmap 472 +#endif + +#ifndef __NR_pidfd_munmap +#define __NR_pidfd_munmap 473 +#endif + +#ifndef PIDFD_MMAP_ARGS_SIZE_VER0 +struct pidfd_mmap_args { + __u64 size; + __u64 addr; + __u64 len; + __u64 prot; + __u64 flags; + __u64 pgoff; + __s32 fd; + __u32 __spare; +}; +#define PIDFD_MMAP_ARGS_SIZE_VER0 56 +#endif + /* * The kernel reserves 300 pids via RESERVED_PIDS in kernel/pid.c * That means, when it wraps around any pid < 300 will be skipped. @@ -267,6 +289,18 @@ static inline int sys_pidfd_getfd(int pidfd, int fd, i= nt flags) return syscall(__NR_pidfd_getfd, pidfd, fd, flags); } =20 +static inline long sys_pidfd_mmap(int pidfd, struct pidfd_mmap_args *args, + unsigned int flags) +{ + return syscall(__NR_pidfd_mmap, pidfd, args, flags); +} + +static inline long sys_pidfd_munmap(int pidfd, unsigned long addr, + unsigned long len) +{ + return syscall(__NR_pidfd_munmap, pidfd, addr, len); +} + static inline int sys_memfd_create(const char *name, unsigned int flags) { return syscall(__NR_memfd_create, name, flags); diff --git a/tools/testing/selftests/pidfd/pidfd_mmap_test.c b/tools/testin= g/selftests/pidfd/pidfd_mmap_test.c new file mode 100644 index 000000000000..39d623ce7b9d --- /dev/null +++ b/tools/testing/selftests/pidfd/pidfd_mmap_test.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pidfd.h" +#include "kselftest_harness.h" + +#define UID_NOBODY 65535 +#define PATTERN 0xab +#define MAP_LEN (4 * 1024) + +static struct pidfd_mmap_args mmap_args(int fd) +{ + struct pidfd_mmap_args args =3D { + .size =3D sizeof(args), + .addr =3D 0, /* let the kernel choose */ + .len =3D MAP_LEN, + .prot =3D PROT_READ | PROT_WRITE, + .flags =3D MAP_SHARED, + .pgoff =3D 0, + .fd =3D fd, + }; + + return args; +} + +static int read_remote(pid_t pid, unsigned long addr, void *buf, size_t le= n) +{ + struct iovec local =3D { .iov_base =3D buf, .iov_len =3D len }; + struct iovec remote =3D { .iov_base =3D (void *)addr, .iov_len =3D len }; + + return process_vm_readv(pid, &local, 1, &remote, 1, 0) =3D=3D (ssize_t)le= n ? + 0 : -1; +} + +static int target(int sk) +{ + char buf; + int ret; + + prctl(PR_SET_PDEATHSIG, SIGKILL); + + if (send(sk, "R", 1, 0) !=3D 1) /* ready */ + return -1; + + while ((ret =3D recv(sk, &buf, sizeof(buf), 0)) > 0) { + if (buf =3D=3D 'P' && prctl(PR_SET_DUMPABLE, 0) < 0) + return -1; + if (send(sk, &buf, 1, 0) !=3D 1) + return -1; + } + + return ret < 0 ? -1 : 0; +} + +FIXTURE(pidfd_mmap) +{ + pid_t pid; + int pidfd; + int sk; /* parent side of the socketpair */ + int memfd; /* lives in the PARENT fd table */ + void *local; /* the parent's own mapping of memfd */ +}; + +FIXTURE_SETUP(pidfd_mmap) +{ + struct pidfd_mmap_args probe; + int sk_pair[2]; + char c; + + /* + * The backing fd is resolved in the *caller's* (parent's) fd table, + * so the target never needs to see it. + */ + self->memfd =3D sys_memfd_create("pidfd_mmap", 0); + ASSERT_GE(self->memfd, 0); + ASSERT_EQ(0, ftruncate(self->memfd, MAP_LEN)); + + self->local =3D mmap(NULL, MAP_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, + self->memfd, 0); + ASSERT_NE(MAP_FAILED, self->local); + memset(self->local, PATTERN, MAP_LEN); + + ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair)); + self->sk =3D sk_pair[0]; + + self->pid =3D fork(); + ASSERT_GE(self->pid, 0); + if (self->pid =3D=3D 0) { + close(sk_pair[0]); + _exit(target(sk_pair[1]) ? EXIT_FAILURE : EXIT_SUCCESS); + } + close(sk_pair[1]); + + self->pidfd =3D sys_pidfd_open(self->pid, 0); + ASSERT_GE(self->pidfd, 0); + + ASSERT_EQ(1, recv(self->sk, &c, 1, 0)); /* wait for "R" */ + + /* Skip the whole suite if the kernel does not have pidfd_mmap(). */ + probe =3D mmap_args(self->memfd); + sys_pidfd_mmap(self->pidfd, &probe, 1); /* reserved flags -> EINVAL */ + if (errno =3D=3D ENOSYS) + SKIP(return, "pidfd_mmap() is not supported"); +} + +FIXTURE_TEARDOWN(pidfd_mmap) +{ + EXPECT_EQ(0, close(self->pidfd)); + EXPECT_EQ(0, close(self->sk)); /* tells the target to exit */ + munmap(self->local, MAP_LEN); + close(self->memfd); + EXPECT_EQ(0, wait_for_pid(self->pid)); +} + +TEST_F(pidfd_mmap, install_and_read) +{ + struct pidfd_mmap_args args =3D mmap_args(self->memfd); + char buf[MAP_LEN]; + long addr; + int i; + + addr =3D sys_pidfd_mmap(self->pidfd, &args, 0); + ASSERT_GE(addr, 0); + ASSERT_EQ(0, addr & (sysconf(_SC_PAGESIZE) - 1)); /* page aligned */ + + /* The target's new mapping must show the memfd contents. */ + ASSERT_EQ(0, read_remote(self->pid, addr, buf, MAP_LEN)); + for (i =3D 0; i < MAP_LEN; i++) + ASSERT_EQ((unsigned char)PATTERN, (unsigned char)buf[i]); + + /* MAP_SHARED: a write the parent makes to memfd is visible remotely. */ + memset(self->local, 0xcd, MAP_LEN); + ASSERT_EQ(0, read_remote(self->pid, addr, buf, sizeof(buf))); + ASSERT_EQ((unsigned char)0xcd, (unsigned char)buf[0]); + + /* pidfd_munmap() removes it. */ + ASSERT_EQ(0, sys_pidfd_munmap(self->pidfd, addr, MAP_LEN)); + EXPECT_EQ(-1, read_remote(self->pid, addr, buf, MAP_LEN)); +} + +TEST_F(pidfd_mmap, anonymous) +{ + struct pidfd_mmap_args args =3D mmap_args(-1); + struct iovec local, remote; + char in[64], out[64]; + long addr; + + args.flags =3D MAP_PRIVATE | MAP_ANONYMOUS; + + addr =3D sys_pidfd_mmap(self->pidfd, &args, 0); + ASSERT_GE(addr, 0); + + memset(out, 0x5a, sizeof(out)); + local.iov_base =3D out; + local.iov_len =3D sizeof(out); + remote.iov_base =3D (void *)addr; + remote.iov_len =3D sizeof(out); + ASSERT_EQ((ssize_t)sizeof(out), + process_vm_writev(self->pid, &local, 1, &remote, 1, 0)); + + ASSERT_EQ(0, read_remote(self->pid, addr, in, sizeof(in))); + ASSERT_EQ((unsigned char)0x5a, (unsigned char)in[0]); + + ASSERT_EQ(0, sys_pidfd_munmap(self->pidfd, addr, MAP_LEN)); +} + +TEST_F(pidfd_mmap, bad_args) +{ + struct pidfd_mmap_args args; + + /* The reserved padding must be zero. */ + args =3D mmap_args(self->memfd); + args.__spare =3D 1; + EXPECT_EQ(-1, sys_pidfd_mmap(self->pidfd, &args, 0)); + EXPECT_EQ(EINVAL, errno); + + /* @size below the first published version is rejected. */ + args =3D mmap_args(self->memfd); + args.size =3D 8; + EXPECT_EQ(-1, sys_pidfd_mmap(self->pidfd, &args, 0)); + EXPECT_EQ(EINVAL, errno); + + /* The reserved flags argument must be zero. */ + args =3D mmap_args(self->memfd); + EXPECT_EQ(-1, sys_pidfd_mmap(self->pidfd, &args, 1)); + EXPECT_EQ(EINVAL, errno); + + /* A bad backing fd (resolved in the caller's table) is -EBADF. */ + args =3D mmap_args(-1); + EXPECT_EQ(-1, sys_pidfd_mmap(self->pidfd, &args, 0)); + EXPECT_EQ(EBADF, errno); + + /* A bad pidfd fails. */ + args =3D mmap_args(self->memfd); + EXPECT_EQ(-1, sys_pidfd_mmap(-1, &args, 0)); +} + +TEST_F(pidfd_mmap, no_ptrace_access) +{ + struct pidfd_mmap_args args =3D mmap_args(self->memfd); + int uid =3D getuid(); + char c; + + /* Drop privilege so CAP_SYS_PTRACE can't bypass the dumpable check. */ + if (uid =3D=3D 0) + ASSERT_EQ(0, seteuid(UID_NOBODY)); + + ASSERT_EQ(1, send(self->sk, "P", 1, 0)); /* target: PR_SET_DUMPABLE 0 */ + ASSERT_EQ(1, recv(self->sk, &c, 1, 0)); + + EXPECT_EQ(-1, sys_pidfd_mmap(self->pidfd, &args, 0)); + EXPECT_EQ(EPERM, errno); + + if (uid =3D=3D 0) + ASSERT_EQ(0, seteuid(0)); +} + +TEST_HARNESS_MAIN --=20 2.43.0