From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 609053B2FEB; Thu, 28 May 2026 09:53:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962038; cv=pass; b=oGYkV5BXnkiCw2RyPciYwAPlP1uUAjlcYmFzLcqh0QXRyj32HfIi4n5ny8JoDpQGdWQcl6ZFrwGT/uFnjfnV4jlyCECn9RYHpY0VuzK/E36EhvPl0NJyYeJHeOMlbhW/GsPsbWHozu3FuQpIehSBzlgGXe6cIJb/DJjRLPRBn6A= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962038; c=relaxed/simple; bh=bM7VdoNzlN0OoaRFNHua4ms2D7EuHg+aAiw2Dpk0NZY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LvQIXIIH8iERvpF7RXWMJKrw071Cv6O46Vp2TZH7hfQQfaG+bJnhWx4WmIws5iNgUFc6A0PLNBIHhQ13yaoZerxIZWNDDJJptkK66HlPZveRIv1kGpgl1JF/hKl5h3B7CeP6XuZ260SWd18ENsttjyXpzHw/p7+QzwIl03CfSsI= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=sIdbe8n9; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="sIdbe8n9" ARC-Seal: i=1; a=rsa-sha256; t=1779961986; cv=none; d=zohomail.com; s=zohoarc; b=X7dnCemyRqEyut9V0SvVWbBJ81qtmnAvbGRlrccs1zrEKYNR8zMqZkq5SW8+5RzWC11E8wC4sZiczegs1Fas45T8ZhWzAjaGr3TvcGTGjvaMOtFwP2kUb+1F7sJK1gRm0iPRq80xbxUVeM1icNvoozlNjNl4qLYSHZUPx0ArOw8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779961986; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=TW9xzJaDheLzGIs2ITQXktnE3vsoHg7TqlJgpHRDctk=; b=ezVqXRV9miOfrjvwjSocUZyJf7ailaKqN+vVFlYzR/LoY1z7uDyNUwlRaK1Yr+XHjGxTDdFDKLmzuoxi8Jy4mp9jEByQP7kLMbnPlWaxgqAFxTWMzd/PUp/rGedmQoYbWUmX9YAoEvfwwvgmZ2Q0Z+VpiAno2m8ySfrPePBNY58= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779961986; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=TW9xzJaDheLzGIs2ITQXktnE3vsoHg7TqlJgpHRDctk=; b=sIdbe8n96YbY8VT5gwJ3g34/YwKwOlA+a+er8pX2jqmL1D6m+0PNAASmIKC2OVBe zmwW8CsIeGTvHbiPGhNvxdMby31wyE2juw4jNWa12UsRCrdHzolJfQcCCcI2jOniGy6 oi/6XcU1WwLsWQn/y7xrIXUj9C5/43otDghx22Ek= Received: by mx.zohomail.com with SMTPS id 1779961982944561.1060190300934; Thu, 28 May 2026 02:53:02 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 01/13] exec: factor argument setup out of do_execveat_common() Date: Thu, 28 May 2026 17:52:22 +0800 Message-ID: <20260528095235.2491226-2-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Move the common userspace argv and envp counting and stack setup code into do_execveat_common_bprm(). Keep do_execveat_common() responsible for the existing RLIMIT_NPROC check, bprm allocation, and error path. This is a mechanical refactor for later opened-file exec users. It does not change execve or execveat behavior. Signed-off-by: Li Chen --- fs/exec.c | 53 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 2889b7cf808d7..53f7b18d2b1ea 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1775,31 +1775,12 @@ static int bprm_execve(struct linux_binprm *bprm) return retval; } =20 -static int do_execveat_common(int fd, struct filename *filename, - struct user_arg_ptr argv, - struct user_arg_ptr envp, - int flags) +static int do_execveat_common_bprm(struct linux_binprm *bprm, + struct user_arg_ptr argv, + struct user_arg_ptr envp) { int retval; =20 - /* - * We move the actual failure in case of RLIMIT_NPROC excess from - * set*uid() to execve() because too many poorly written programs - * don't check setuid() return code. Here we additionally recheck - * whether NPROC limit is still exceeded. - */ - if ((current->flags & PF_NPROC_EXCEEDED) && - is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RL= IMIT_NPROC))) - return -EAGAIN; - - /* We're below the limit (still or again), so we don't want to make - * further execve() calls fail. */ - current->flags &=3D ~PF_NPROC_EXCEEDED; - - CLASS(bprm, bprm)(fd, filename, flags); - if (IS_ERR(bprm)) - return PTR_ERR(bprm); - retval =3D count(argv, MAX_ARG_STRINGS); if (retval < 0) return retval; @@ -1846,6 +1827,34 @@ static int do_execveat_common(int fd, struct filenam= e *filename, return bprm_execve(bprm); } =20 +static int do_execveat_common(int fd, struct filename *filename, + struct user_arg_ptr argv, + struct user_arg_ptr envp, + int flags) +{ + /* + * We move the actual failure in case of RLIMIT_NPROC excess from + * set*uid() to execve() because too many poorly written programs + * don't check setuid() return code. Here we additionally recheck + * whether NPROC limit is still exceeded. + */ + if ((current->flags & PF_NPROC_EXCEEDED) && + is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RL= IMIT_NPROC))) + return -EAGAIN; + + /* + * We're below the limit (still or again), so we don't want to make + * further execve() calls fail. + */ + current->flags &=3D ~PF_NPROC_EXCEEDED; + + CLASS(bprm, bprm)(fd, filename, flags); + if (IS_ERR(bprm)) + return PTR_ERR(bprm); + + return do_execveat_common_bprm(bprm, argv, envp); +} + int kernel_execve(const char *kernel_filename, const char *const *argv, const char *const *envp) { --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 69D5339021A; Thu, 28 May 2026 09:54:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962061; cv=pass; b=bx7X6ECKkaWOWEdml/4G9QdnRySfqOCwIkFOKqdzNb8qPzoAMosB1guhrJAI+wdwoZPsoHc59sksTuf33Eox2/xAGqkTewzra3rKWPU11x4F25PqCh7DolPNgONqNoDqE7p8ZKL0eNsl2W8b4eDMOgIf48nYQkVR9JlVhFYhfRg= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962061; c=relaxed/simple; bh=FhZzqTfOmRQA2ulX9Er1KOrTVIj/mj1vt5ktr+JES04=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kh3F7H7P8zvGiqyLz0ZcxnNSDBL55PVsrwHMZcTdqusg/3F4ybaLFiTehGOaqIa4h1V2DAboCnlY+MRxWO9FAmjkmd36RAClkbDLyhJRAwvceTCLn1lCuUdRLgfRccJRr6aNIaICvpuEDZbgfIebg38yGkhv0pC2kfFzmVKtyl4= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=FKKbwuBs; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="FKKbwuBs" ARC-Seal: i=1; a=rsa-sha256; t=1779961994; cv=none; d=zohomail.com; s=zohoarc; b=fDeGWFaxxrrcS1qDKS05XqYcZzPAF01/g6Ak+RwWXKG6FYNj6BVMIC2k1nfJlv9eCj/s0FbAfJpHieo3i1Swx5WlwiqRPBKygY5640jds3ncz/DYai8sGCYUrmxYIFgCE3yLzx5waC19lcH9Snx1b+7P7scuC6flfGDqY7Tn7gw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779961994; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=uH9yEaUapIEKI2lfJ5G4DMQBlkpv8yAmGkbHJEgDnPY=; b=i70+iVLUuxtknOMvAep6Ruv5rjjsgfLk1c82C16zVQh0uezSHlhefm7AlpayC8ym7VXJTsrbLgKE5mVPBifuDHpZOxFtAUIGqP190L18NuoWkmxR5oUvtGSlH315OORF34vvoWlGUaqpDsAws5MP5zQjsKir/NjNZVEuTQ+d+iY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779961994; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=uH9yEaUapIEKI2lfJ5G4DMQBlkpv8yAmGkbHJEgDnPY=; b=FKKbwuBsJ5SfWsLpJFnsM7vEveFkJV6vaylgo+rqg2tpHfGqwr57fWuWpcJm7bbJ qBZMhrSsxhYOglTquuejNbzWJlrPcY4tno/duHtpdTwL/pNStDGh69hDDzmS4J6rnm9 b621M0QFh8R5OtuzBHeDxesjLPgFqUEPx3PnfBio= Received: by mx.zohomail.com with SMTPS id 1779961991167186.94687791742058; Thu, 28 May 2026 02:53:11 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 02/13] exec: add an internal helper for opened executables Date: Thu, 28 May 2026 17:52:23 +0800 Message-ID: <20260528095235.2491226-3-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Split alloc_bprm_file() from alloc_bprm() so internal callers can build a linux_binprm from an executable file that they already opened. Add kernel_execveat_file() for in-kernel users that need to execute an opened file while still using the normal execve credential, LSM, and binary-format path. Signed-off-by: Li Chen --- fs/exec.c | 78 +++++++++++++++++++++++++++++++++++------ include/linux/binfmts.h | 4 +++ 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 53f7b18d2b1ea..5b91a9b208a77 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1392,16 +1392,13 @@ static void free_bprm(struct linux_binprm *bprm) kfree(bprm); } =20 -static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, = int flags) +static struct linux_binprm *alloc_bprm_file(struct file *file, + struct filename *filename, + int fd, int flags) { struct linux_binprm *bprm; - struct file *file; int retval =3D -ENOMEM; =20 - file =3D do_open_execat(fd, filename, flags); - if (IS_ERR(file)) - return ERR_CAST(file); - bprm =3D kzalloc_obj(*bprm); if (!bprm) { do_close_execat(file); @@ -1463,6 +1460,17 @@ static struct linux_binprm *alloc_bprm(int fd, struc= t filename *filename, int fl return ERR_PTR(retval); } =20 +static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, = int flags) +{ + struct file *file; + + file =3D do_open_execat(fd, filename, flags); + if (IS_ERR(file)) + return ERR_CAST(file); + + return alloc_bprm_file(file, filename, fd, flags); +} + DEFINE_CLASS(bprm, struct linux_binprm *, if (!IS_ERR(_T)) free_bprm(_T), alloc_bprm(fd, name, flags), int fd, struct filename *name, int flags) =20 @@ -1901,6 +1909,59 @@ int kernel_execve(const char *kernel_filename, return bprm_execve(bprm); } =20 +static inline struct user_arg_ptr native_arg(const char __user *const __us= er *p) +{ + return (struct user_arg_ptr){.ptr.native =3D p}; +} + +static int do_execveat_file_common(struct file *file, struct filename *fil= ename, + struct user_arg_ptr argv, + struct user_arg_ptr envp, int flags) +{ + struct linux_binprm *bprm; + struct file *exec_file; + int retval; + + if (flags & ~AT_EMPTY_PATH) + return -EINVAL; + + if ((current->flags & PF_NPROC_EXCEEDED) && + is_rlimit_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RL= IMIT_NPROC))) + return -EAGAIN; + + current->flags &=3D ~PF_NPROC_EXCEEDED; + + retval =3D exe_file_deny_write_access(file); + if (retval) + return retval; + exec_file =3D get_file(file); + + bprm =3D alloc_bprm_file(exec_file, filename, AT_FDCWD, flags); + if (IS_ERR(bprm)) + return PTR_ERR(bprm); + + retval =3D do_execveat_common_bprm(bprm, argv, envp); + free_bprm(bprm); + return retval; +} + +int kernel_execveat_file(struct file *file, const char *filename, + const void __user *argv, + const void __user *envp, + int flags) +{ + const char __user *const __user *user_argv; + const char __user *const __user *user_envp; + + CLASS(filename_kernel, name)(filename); + + user_argv =3D (const char __user *const __user *)argv; + user_envp =3D (const char __user *const __user *)envp; + + return do_execveat_file_common(file, name, native_arg(user_argv), + native_arg(user_envp), flags); +} + void set_binfmt(struct linux_binfmt *new) { struct mm_struct *mm =3D current->mm; @@ -1925,11 +1986,6 @@ void set_dumpable(struct mm_struct *mm, int value) __mm_flags_set_mask_dumpable(mm, value); } =20 -static inline struct user_arg_ptr native_arg(const char __user *const __us= er *p) -{ - return (struct user_arg_ptr){.ptr.native =3D p}; -} - SYSCALL_DEFINE3(execve, const char __user *, filename, const char __user *const __user *, argv, diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 65abd5ab8836c..c0715678c9a06 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -141,6 +141,10 @@ extern int transfer_args_to_stack(struct linux_binprm = *bprm, unsigned long *sp_location); extern int bprm_change_interp(const char *interp, struct linux_binprm *bpr= m); int copy_string_kernel(const char *arg, struct linux_binprm *bprm); +int kernel_execveat_file(struct file *file, const char *filename, + const void __user *argv, + const void __user *envp, + int flags); extern void set_binfmt(struct linux_binfmt *new); extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t); =20 --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5C54D3B19CD; Thu, 28 May 2026 09:54:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962089; cv=pass; b=nOBkKObhVYUWxnFN1rug3HCIMteF1fnj9Dwl1otGO37qjwJQyp0tHIo8tQYXYlZaPPEeBzbdVT5xnq8yf+7lHnmM6Yt+FcnWiTOEezfl48SuT06/795XYIwDEwPbZgpUPbovqHlbHo3j2nu326xQ9qyDJgykmVwn1d+BbrbxTVI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962089; c=relaxed/simple; bh=p8JwGfGwuQJSTehFaGTsZoPBVvd78o6xdHJNlYzgKGM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AtHJq7bz8wfywG0z8JQNFjmGOlDp+45ppHj93llKjSsTGb/iO88Cl12hZr9FEmL5txt1O6uwkf8u1U02k/mzuY5moX3FQRybDuzdKPINsJkxOaBjPkDHH1Kl9aRQCZnDV2/Ti/Lb66v397KTF2PBYJvw6VPj8mK4iAChRiGYEoc= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=ibP9/gpb; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="ibP9/gpb" ARC-Seal: i=1; a=rsa-sha256; t=1779962002; cv=none; d=zohomail.com; s=zohoarc; b=BjY+rrmWLeXJJjQO8PJHtywe0UJQ7E2pjwCx5wcNHnA4egGvITx/6i2EtDKw/2QqNijDBOqEimBC9n8mh1z5puanqcp1ql2SmS9gCg70wze3E/yaOE/v+ub7FWNQ0WbpSi79NU88eYotN8TMyyRvKJAxy3KuyMrG9rZROYEWuH8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779962002; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=MC4dHb80t6/BIqcUh3zzA0OufX3r53dp1BU6A+dxByg=; b=DWGfOBX55kPaMTFI0EZNeE/PGV94CywIhXgl+CCRnmAWmdgsCpA+SHASORGhm79ZvFWpdOf9NuVZOIUf9dkHGoN6ZrYu0cc035jAag50/MWLMdB2yg13Rf3ey+sXW7HzVr87mVyVmb8hNeEtVnClLGL+QfoVZ5Mf20d21GDY/EE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779962002; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=MC4dHb80t6/BIqcUh3zzA0OufX3r53dp1BU6A+dxByg=; b=ibP9/gpbHz+8ZyfY8Yxk0qP/vQNgBX0rzt1bGyckd4RNeJkQqw5x+v2ABym4GyET SZA91nuGCe9SpX2YTxy/S6LA8HV00AM10poCIJaWS8/YHddtqYafv4m2Lfng6Nv29FI XdRjwESCU5nRsJaVHk7lPHNvHbMljrXf7abRlHGo= Received: by mx.zohomail.com with SMTPS id 1779961999561196.25957869706974; Thu, 28 May 2026 02:53:19 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 03/13] file: expose helpers for in-kernel fd actions Date: Thu, 28 May 2026 17:52:24 +0800 Message-ID: <20260528095235.2491226-4-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Split do_close_range() from the close_range syscall wrapper and make ksys_dup3() available to in-kernel callers. Later spawn-template fd actions use these helpers instead of duplicating close and dup logic. Signed-off-by: Li Chen --- fs/file.c | 11 ++++++++--- include/linux/fdtable.h | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/fs/file.c b/fs/file.c index e5c75b22e0c7c..a9f4b4e2dcd45 100644 --- a/fs/file.c +++ b/fs/file.c @@ -815,8 +815,7 @@ static inline void __range_close(struct files_struct *f= iles, unsigned int fd, * from @fd up to and including @max_fd are closed. * Currently, errors to close a given file descriptor are ignored. */ -SYSCALL_DEFINE3(close_range, unsigned int, fd, unsigned int, max_fd, - unsigned int, flags) +int do_close_range(unsigned int fd, unsigned int max_fd, unsigned int flag= s) { struct task_struct *me =3D current; struct files_struct *cur_fds =3D me->files, *fds =3D NULL; @@ -867,6 +866,12 @@ SYSCALL_DEFINE3(close_range, unsigned int, fd, unsigne= d int, max_fd, return 0; } =20 +SYSCALL_DEFINE3(close_range, unsigned int, fd, unsigned int, max_fd, + unsigned int, flags) +{ + return do_close_range(fd, max_fd, flags); +} + /** * file_close_fd - return file associated with fd * @fd: file descriptor to retrieve file for @@ -1421,7 +1426,7 @@ int receive_fd_replace(int new_fd, struct file *file,= unsigned int o_flags) return new_fd; } =20 -static int ksys_dup3(unsigned int oldfd, unsigned int newfd, int flags) +int ksys_dup3(unsigned int oldfd, unsigned int newfd, int flags) { int err =3D -EBADF; struct file *file; diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h index c45306a9f0072..7f852fcc082a4 100644 --- a/include/linux/fdtable.h +++ b/include/linux/fdtable.h @@ -112,6 +112,8 @@ int iterate_fd(struct files_struct *, unsigned, =20 extern int close_fd(unsigned int fd); extern struct file *file_close_fd(unsigned int fd); +int do_close_range(unsigned int fd, unsigned int max_fd, unsigned int flag= s); +int ksys_dup3(unsigned int oldfd, unsigned int newfd, int flags); =20 extern struct kmem_cache *files_cachep; =20 --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AA20C3B47EC; Thu, 28 May 2026 09:55:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962116; cv=pass; b=hSSdyRuoCMD6pP7PFdYSdjB/LoeTid+IS+i9X6qZsqwU4wgeaCj1KQt4gyAWFOVd8dubAJjxzGMCDbbds806Ir/Z7mDvkfibNubhOrlZxf7eZuBX1xgcIfoamXug3nbaAKgze910V1W1aA6+5/T+2xNCLHjUh6O6bV5T8V2J378= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962116; c=relaxed/simple; bh=qwZ2vfx9YEVsfDJKQyu1dN9dxJddEvj8lgpCxBFjuq8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kA0H5RAneeo+PHw6WLA/1YV97J2OpJbLTFoFX88ZUB29WJ31JE0pRX/XsWRihoHqM9PD3ktoMrC8InT1oP8EhP4Ls3NOuNY73m+HgFGABaRv6Y9xsKurW72XGbBXxqBmy+LubSFdGtkYeeL8p0l0Q88brQhW+44I19R7Do9VFS4= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=AZwYKVo1; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="AZwYKVo1" ARC-Seal: i=1; a=rsa-sha256; t=1779962010; cv=none; d=zohomail.com; s=zohoarc; b=UJp74+WitwbRvyhSh1bqMHpblUO0MWbE7UlDr3begnbJB617OdDR2gfunQADEykfa5B0CE+3TWDnE40e3516+m/qPveTsAPszR0aajiboyNqWDPimeaX4e4lNNSkev3dWBbu2hyF2ts3NeqWS3N+u/DIbVIRjbgWGgs0eeTrIMQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779962010; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=Np1g+dFsamPmY41icpk+vIarybQaXo3ssE2KjyjvCss=; b=F7bPn+uWftD68aLJkbRNnr397A+9eEYyvV2g2crEX+VJTQfeKVSDBvtI9IDVgcDvS193Ze2dUHGrKym8ulTYxWTaMq0lxaUcC2l3XndZUAvIGrG6ZwDXCNjXgaIYNJaLISjUYbh0DV9akOZOUQVclWuU4l0b866uCKCa1cB85Mc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779962010; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=Np1g+dFsamPmY41icpk+vIarybQaXo3ssE2KjyjvCss=; b=AZwYKVo12OW8IbAxRwRL7CFelimmkb7ycFOc6bfBdDKMfCWY/i7OrbyNEOJs02Gs EOFTusw+Sg+ZbqsMzB5Gz1dhRJ44nSHYnWrGTYjBw7Y9et5/2bGYYQ2uKbWeDPygQAh cOZyCXAyXl72jvaM82H6qpYb8hKnfM0GxlXMO8wo= Received: by mx.zohomail.com with SMTPS id 1779962008110241.98298131178922; Thu, 28 May 2026 02:53:28 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 04/13] exec: add spawn template UAPI definitions Date: Thu, 28 May 2026 17:52:25 +0800 Message-ID: <20260528095235.2491226-5-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Add the userspace ABI structures and flags for creating a spawn template and spawning a process from it. The ABI carries argv, envp, and per-spawn fd actions while leaving policy decisions in userspace. Signed-off-by: Li Chen --- MAINTAINERS | 1 + include/uapi/linux/spawn_template.h | 62 +++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 include/uapi/linux/spawn_template.h diff --git a/MAINTAINERS b/MAINTAINERS index 3dd58a16f06a9..d7b1191e33ca0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9739,6 +9739,7 @@ F: include/linux/elf.h F: include/uapi/linux/auxvec.h F: include/uapi/linux/binfmts.h F: include/uapi/linux/elf.h +F: include/uapi/linux/spawn_template.h F: kernel/fork.c F: mm/vma_exec.c F: tools/testing/selftests/exec/ diff --git a/include/uapi/linux/spawn_template.h b/include/uapi/linux/spawn= _template.h new file mode 100644 index 0000000000000..84f026fdf9090 --- /dev/null +++ b/include/uapi/linux/spawn_template.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_SPAWN_TEMPLATE_H +#define _UAPI_LINUX_SPAWN_TEMPLATE_H + +#include +#include + +#define SPAWN_TEMPLATE_CREATE_CLOEXEC (1ULL << 0) +#define SPAWN_TEMPLATE_SPAWN_INHERIT_FDS (1ULL << 0) + +enum spawn_template_action_type { + SPAWN_TEMPLATE_ACTION_CLOSE =3D 0, + SPAWN_TEMPLATE_ACTION_DUP2 =3D 1, + SPAWN_TEMPLATE_ACTION_FCHDIR =3D 2, + SPAWN_TEMPLATE_ACTION_OPEN =3D 3, + SPAWN_TEMPLATE_ACTION_CLOSE_RANGE =3D 4, + SPAWN_TEMPLATE_ACTION_SIGMASK =3D 5, + SPAWN_TEMPLATE_ACTION_SIGDEFAULT =3D 6, +}; + +struct spawn_template_action { + __u32 type; + __u32 flags; + __s32 fd; + __s32 newfd; + __aligned_u64 arg; +}; + +struct spawn_template_open { + __aligned_u64 path; + struct open_how how; +}; + +struct spawn_template_sigset { + __aligned_u64 sigset; + __u64 sigsetsize; +}; + +struct spawn_template_create_args { + __aligned_u64 flags; + __s32 execfd; + __u32 exec_flags; + __aligned_u64 filename; + __aligned_u64 actions; + __aligned_u64 actions_len; + __aligned_u64 reserved[4]; +}; + +struct spawn_template_spawn_args { + __aligned_u64 flags; + __aligned_u64 pidfd; + __aligned_u64 argv; + __aligned_u64 envp; + __aligned_u64 actions; + __aligned_u64 actions_len; + __aligned_u64 reserved[4]; +}; + +#define SPAWN_TEMPLATE_CREATE_ARGS_SIZE_VER0 72 +#define SPAWN_TEMPLATE_SPAWN_ARGS_SIZE_VER0 80 + +#endif /* _UAPI_LINUX_SPAWN_TEMPLATE_H */ --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 378223AFAEB; Thu, 28 May 2026 09:55:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962140; cv=pass; b=YfpjERDFeNa42LEOlL9wxNDsWZh0/yAxwu072niX3QG+kFBBBoBMijAgLSg1dGIlIOWi6xnVmtE1fKfpvURruwf4q3Bt+0igarWKc+suI56zd9vUAOFm05St60qz+KOik6CQvyfW8beubFrhuJanoUgqep9gu1aXGW3TgIrpfzQ= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962140; c=relaxed/simple; bh=Z7R6eJu4Gb+4VpauSNgDK23s4COyzAAcBqccuhca1E4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WC7RkdggI1Q+eWmtPKeC09MANqpOpA2MQOWXEqdHhOFOdVDJNbZ7nEosOa6GTCP3G2C3Oq2W8yVrEI/HZQWZu23zVwm3HnSpJCJCyFcnZ+7vJtSueZqTq+0UzdqCNFQums72m3Wu2xeZ9KbwJ9Tb/sdJ/TnRNMV38hdPtl3I/mM= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=gooSu8WX; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="gooSu8WX" ARC-Seal: i=1; a=rsa-sha256; t=1779962018; cv=none; d=zohomail.com; s=zohoarc; b=NiCRIb5eW2EmjWumorNbTFqVTFWRMvbiMFgX0JVJ5UqxeQOoKeglBqPoUS7pU3aoY5wZNnHmTpOZIcK7Ufuccb93ogSCXbqDto2C+AyqCPY1YhZz495SeTWoBlR7iy4hu1RtwLt43urc4771wo8s9KVDA7WpNOozpJ++dl2/bf8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779962018; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=yP+tXDAHbI+vpJXGx0g74A5vKG5a06odoAVil7kxPRc=; b=SaIsF/fNbDe3s/8mTIYMUA+BvDn7XG6Wb3FtDrEQQjybgIPqA2CBpLUcSvS2GZ4QSOJNHjrfsAFsqPZv/E6GiKzg0s0cSz+DNYZ15dpIHV+i5aMCQrDUota3fasPBEEXqBBH323qPN6IHBQCfP2HofnF9WY+7NX0hXOCH719gSM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779962018; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=yP+tXDAHbI+vpJXGx0g74A5vKG5a06odoAVil7kxPRc=; b=gooSu8WXAV0meD4y8CEBu6Z5WLJkTZ/h/TUb/2xCGsyM/FNiDRZUCqvSs5GXNv4I RxoWXdxQout4ibJuz3HBRTz1mr/EIcxMS4i9nA8NV9whW9/FnXDMyabvuRQ8nILdG9s d+vdx4iRNwGk6Qag8SgUQ+z39unNW37PfT2eN6tc= Received: by mx.zohomail.com with SMTPS id 1779962016151343.1095475450736; Thu, 28 May 2026 02:53:36 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 05/13] exec: add spawn template file descriptors Date: Thu, 28 May 2026 17:52:26 +0800 Message-ID: <20260528095235.2491226-6-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Add spawn_template_create() and back each template with an anon-inode fd. Creation records the per-template state that later spawns reuse: the opened executable file, optional absolute path, creator credential, and deny-write state. Keep write access denied until the template fd is released so cached state cannot race with writers. This patch only creates and releases template fds. Spawning and ELF metadata caching are added separately. Signed-off-by: Li Chen --- MAINTAINERS | 1 + arch/x86/entry/syscalls/syscall_64.tbl | 1 - fs/Makefile | 2 +- fs/spawn_template.c | 180 +++++++++++++++++++++++++ include/linux/syscalls.h | 3 + 5 files changed, 185 insertions(+), 2 deletions(-) create mode 100644 fs/spawn_template.c diff --git a/MAINTAINERS b/MAINTAINERS index d7b1191e33ca0..d5441812825c3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9732,6 +9732,7 @@ F: Documentation/userspace-api/ELF.rst F: fs/*binfmt_*.c F: fs/Kconfig.binfmt F: fs/exec.c +F: fs/spawn_template.c F: fs/tests/binfmt_*_kunit.c F: fs/tests/exec_kunit.c F: include/linux/binfmts.h diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscal= ls/syscall_64.tbl index 524155d655da1..d6c1667e8f3b8 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -396,7 +396,6 @@ 469 common file_setattr sys_file_setattr 470 common listns sys_listns 471 common rseq_slice_yield sys_rseq_slice_yield - # # Due to a historical design error, certain syscalls are numbered differen= tly # in x32 as compared to native x86_64. These syscalls have numbers 512-54= 7. diff --git a/fs/Makefile b/fs/Makefile index ae1b07f9c6a0c..796eb4ae143e5 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -8,7 +8,7 @@ =20 =20 obj-y :=3D open.o read_write.o file_table.o super.o \ - char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ + char_dev.o stat.o exec.o spawn_template.o pipe.o namei.o fcntl.o \ ioctl.o readdir.o select.o dcache.o inode.o \ attr.o bad_inode.o file.o filesystems.o namespace.o \ seq_file.o xattr.o libfs.o fs-writeback.o \ diff --git a/fs/spawn_template.c b/fs/spawn_template.c new file mode 100644 index 0000000000000..280a1038cc45e --- /dev/null +++ b/fs/spawn_template.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define SPAWN_TEMPLATE_MAX_ACTIONS 256 + +struct spawn_template { + struct file *exec_file; + const struct cred *creator_cred; + char *filename; + bool deny_write; +}; + +static const struct file_operations spawn_template_fops; + +static bool spawn_template_file_exec_allowed(struct file *file) +{ + if (!S_ISREG(file_inode(file)->i_mode)) + return false; + if (path_noexec(&file->f_path)) + return false; + if (file_permission(file, MAY_EXEC)) + return false; + return can_mmap_file(file); +} + +static int spawn_template_release(struct inode *inode, struct file *file) +{ + struct spawn_template *tmpl =3D file->private_data; + + if (tmpl->deny_write) + exe_file_allow_write_access(tmpl->exec_file); + fput(tmpl->exec_file); + put_cred(tmpl->creator_cred); + kfree(tmpl->filename); + kfree(tmpl); + return 0; +} + +static const struct file_operations spawn_template_fops =3D { + .release =3D spawn_template_release, + .llseek =3D noop_llseek, +}; + +static int spawn_template_open_execfd(int execfd, struct file **file, + bool *deny_write) +{ + int ret; + + if (execfd < 0) + return -EINVAL; + + CLASS(fd, f)(execfd); + if (fd_empty(f)) + return -EBADF; + + if (!spawn_template_file_exec_allowed(fd_file(f))) + return -EACCES; + + ret =3D exe_file_deny_write_access(fd_file(f)); + if (ret) + return ret; + + *file =3D get_file(fd_file(f)); + *deny_write =3D true; + return 0; +} + +static int spawn_template_open_filename(u64 filename, struct file **file, + char **path, + bool *deny_write) +{ + char *kfilename __free(kfree) =3D NULL; + struct file *exec __free(fput) =3D NULL; + struct file *tmp_file; + char *tmp; + + if (!filename) + return -EINVAL; + + tmp =3D strndup_user(u64_to_user_ptr(filename), PATH_MAX); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + kfilename =3D tmp; + + tmp_file =3D open_exec(kfilename); + if (IS_ERR(tmp_file)) + return PTR_ERR(tmp_file); + exec =3D tmp_file; + if (!spawn_template_file_exec_allowed(exec)) { + exe_file_allow_write_access(exec); + return -EACCES; + } + + *file =3D no_free_ptr(exec); + *path =3D no_free_ptr(kfilename); + *deny_write =3D true; + return 0; +} + +SYSCALL_DEFINE2(spawn_template_create, + struct spawn_template_create_args __user *, uargs, + size_t, usize) +{ + struct spawn_template_create_args args; + struct spawn_template *tmpl; + int fd_flags =3D 0; + int ret; + + BUILD_BUG_ON(sizeof(struct spawn_template_create_args) !=3D + SPAWN_TEMPLATE_CREATE_ARGS_SIZE_VER0); + + if (usize < SPAWN_TEMPLATE_CREATE_ARGS_SIZE_VER0) + return -EINVAL; + if (usize > PAGE_SIZE) + return -E2BIG; + + ret =3D copy_struct_from_user(&args, sizeof(args), uargs, usize); + if (ret) + return ret; + + if (args.flags & ~SPAWN_TEMPLATE_CREATE_CLOEXEC) + return -EINVAL; + if (args.exec_flags || args.reserved[0] || args.reserved[1] || + args.reserved[2] || args.reserved[3]) + return -EINVAL; + if (args.actions || args.actions_len) + return -EINVAL; + if ((args.execfd < 0 && !args.filename) || + (args.execfd >=3D 0 && args.filename)) + return -EINVAL; + + tmpl =3D kzalloc_obj(*tmpl, GFP_KERNEL); + if (!tmpl) + return -ENOMEM; + tmpl->creator_cred =3D get_current_cred(); + + if (args.filename) + ret =3D spawn_template_open_filename(args.filename, + &tmpl->exec_file, + &tmpl->filename, + &tmpl->deny_write); + else + ret =3D spawn_template_open_execfd(args.execfd, + &tmpl->exec_file, + &tmpl->deny_write); + if (ret) + goto out_free_tmpl; + + if (args.flags & SPAWN_TEMPLATE_CREATE_CLOEXEC) + fd_flags |=3D O_CLOEXEC; + + ret =3D anon_inode_getfd("spawn_template", &spawn_template_fops, tmpl, + fd_flags); + if (ret < 0) + goto out_put_exec; + + return ret; + +out_put_exec: + if (tmpl->deny_write) + exe_file_allow_write_access(tmpl->exec_file); + fput(tmpl->exec_file); +out_free_tmpl: + put_cred(tmpl->creator_cred); + kfree(tmpl->filename); + kfree(tmpl); + return ret; +} diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index f3dfc3269188a..4b41950488bd6 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -67,6 +67,7 @@ struct rseq; union bpf_attr; struct io_uring_params; struct clone_args; +struct spawn_template_create_args; struct open_how; struct mount_attr; struct landlock_ruleset_attr; @@ -821,6 +822,8 @@ asmlinkage long sys_clone(unsigned long, unsigned long,= int __user *, #endif =20 asmlinkage long sys_clone3(struct clone_args __user *uargs, size_t size); +asmlinkage long sys_spawn_template_create(struct spawn_template_create_arg= s __user *uargs, + size_t size); =20 asmlinkage long sys_execve(const char __user *filename, const char __user *const __user *argv, --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 07EA23B19CD; Thu, 28 May 2026 09:56:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962167; cv=pass; b=Q2J/aGmNviCrLVnjgt/cNA+f2uKNGD8jxcxDgRJRcPV3iGgbIL0qFev5RwzpImaDRegTtGBJJ0XOB33T7C90Wp0Tk9NAYkOdogEa/YYdPQX9LBrENnQVIaXnXYlI3NYWrkk8e8sngl8jyCy8utvzf75wnLef0VdRBA3NlSf7B9E= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962167; c=relaxed/simple; bh=zU/A8x6ocbt5zoW/Y2Xg0XHztJTqyk2lWZiQH2md5UQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=s176Jo7/C500VaZ0m6c/H4z9bEHWxfYEVnsYqbJ209xj3Mr2kC40p3bua7jK7wL3aYMqA5j+myWaZdDPpOK2krhXCRhT3T3GmuJ0K/BPXf/w4TaBl9n375okTMdXCtzH2FaGa6f77PaLLXL55PTngEUwFq1NduAqRm0cfoH59Zs= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=M7mMVARh; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="M7mMVARh" ARC-Seal: i=1; a=rsa-sha256; t=1779962026; cv=none; d=zohomail.com; s=zohoarc; b=XQZq3+OEqfGlF9Cs3b3S85VSu6bPQ9oXwSEANCfOq4wMuGKDtilDyajnpC1Z4uLqyzBziU/Ke6iqKGaMa/cFro7xOuhGkoWJlDX1QFtr1Vgwtk6R0ueugPBNiFPayxsBqa1glj0AcPSC4l//Mp01m0JFTBq1To2ppF47zxxbFsE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779962026; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=LhfFQvdxoQHQc1XvZzl4k0UIxef+VVV/sdLhZpmWwr0=; b=Uc0co1eQ7iD1xTV9AihTzQs/f7HXDjsJ/HnHwefn9A9VOPa3OynYr6QIKQnuRPMONv5JL9iL9JBm5++F0EJu1OnuaC5Z3OA5nh77npZt9Yd5LcvSKN/jjtB9oFiRmiUMmbLYRkkZLCGXbU0x9SEt4KtYJYGPqMqBYu5IVLmsTqo= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779962026; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=LhfFQvdxoQHQc1XvZzl4k0UIxef+VVV/sdLhZpmWwr0=; b=M7mMVARhBBhy6KWTWF+UDlSoGVh6NZ6LFSeX4jEUuEVHOCX6UNmPLDk7Phuww3su PGr5l0txatogltgY0VoEDovHmVmNz7k3jw1PA/A0qeXDtTExXKnlgl7/cKnORlUkFst LM3BAtkczo/5fEeByO3W/LfsVdnIBr6jvOHdz7U8= Received: by mx.zohomail.com with SMTPS id 1779962024044536.5261694330434; Thu, 28 May 2026 02:53:44 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 06/13] exec: add spawn_template_spawn() Date: Thu, 28 May 2026 17:52:27 +0800 Message-ID: <20260528095235.2491226-7-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Add spawn_template_spawn() to start a child from a template fd. The child uses the template's pinned executable file, runs per-spawn fd, cwd, and signal actions, closes non-stdio fds by default, and then executes through the normal opened-file exec path. Return a pidfd for the child so userspace can wait or signal it without racy pid reuse. Keep fd inheritance opt-in with SPAWN_TEMPLATE_SPAWN_INHERIT_FDS. This patch consumes cached template state but does not add ELF metadata caching; executable identity and ELF metadata caching are added separately. Signed-off-by: Li Chen --- fs/spawn_template.c | 346 +++++++++++++++++++++++++++++++++++++++ include/linux/syscalls.h | 4 + 2 files changed, 350 insertions(+) diff --git a/fs/spawn_template.c b/fs/spawn_template.c index 280a1038cc45e..8c3711929cffb 100644 --- a/fs/spawn_template.c +++ b/fs/spawn_template.c @@ -1,14 +1,24 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include +#include #include #include #include +#include #include #include +#include #include +#include +#include +#include +#include #include +#include #include #include +#include #include =20 #include "internal.h" @@ -22,8 +32,262 @@ struct spawn_template { bool deny_write; }; =20 +struct spawn_template_spawn_context { + struct spawn_template *tmpl; + struct spawn_template_spawn_args args; + struct spawn_template_action *actions; +}; + static const struct file_operations spawn_template_fops; =20 +static int spawn_template_exit_status(int err) +{ + switch (err) { + case -ENOENT: + return 127; + case -EACCES: + case -ENOEXEC: + return 126; + default: + return 1; + } +} + +static bool spawn_template_cred_matches(struct spawn_template *tmpl) +{ + return current_cred() =3D=3D tmpl->creator_cred; +} + +static int spawn_template_copy_signal_set(const struct spawn_template_acti= on *action, + sigset_t *mask) +{ + struct spawn_template_sigset sigset; + + if (!action->arg) + return -EINVAL; + if (copy_from_user(&sigset, u64_to_user_ptr(action->arg), + sizeof(sigset))) + return -EFAULT; + if (sigset.sigsetsize !=3D sizeof(sigset_t)) + return -EINVAL; + if (copy_from_user(mask, u64_to_user_ptr(sigset.sigset), sizeof(*mask))) + return -EFAULT; + sigdelsetmask(mask, sigmask(SIGKILL) | sigmask(SIGSTOP)); + + return 0; +} + +static int spawn_template_apply_open(const struct spawn_template_action *a= ction) +{ + struct spawn_template_open open; + struct file *file __free(fput) =3D NULL; + struct file *tmp; + struct open_flags op; + int ret; + + if (action->fd < AT_FDCWD || action->newfd < 0 || action->flags || + !action->arg) + return -EINVAL; + + if (copy_from_user(&open, u64_to_user_ptr(action->arg), sizeof(open))) + return -EFAULT; + + ret =3D build_open_flags(&open.how, &op); + if (ret) + return ret; + + CLASS(filename_flags, name)(u64_to_user_ptr(open.path), op.lookup_flags); + tmp =3D do_file_open(action->fd, name, &op); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + file =3D tmp; + + return replace_fd(action->newfd, file, open.how.flags & O_CLOEXEC); +} + +static int spawn_template_apply_sigmask(const struct spawn_template_action= *action) +{ + sigset_t mask; + int ret; + + if (action->fd || action->newfd || action->flags) + return -EINVAL; + + ret =3D spawn_template_copy_signal_set(action, &mask); + if (ret) + return ret; + + set_current_blocked(&mask); + return 0; +} + +static int spawn_template_apply_sigdefault(const struct spawn_template_act= ion *action) +{ + sigset_t mask; + struct k_sigaction sa =3D {}; + int ret; + int sig; + + if (action->fd || action->newfd || action->flags) + return -EINVAL; + + ret =3D spawn_template_copy_signal_set(action, &mask); + if (ret) + return ret; + + sa.sa.sa_handler =3D SIG_DFL; + sigemptyset(&sa.sa.sa_mask); + + for (sig =3D 1; sig < _NSIG; sig++) { + if (!sigismember(&mask, sig)) + continue; + ret =3D do_sigaction(sig, &sa, NULL); + if (ret) + return ret; + } + + return 0; +} + +static int spawn_template_apply_action(const struct spawn_template_action = *action) +{ + switch (action->type) { + case SPAWN_TEMPLATE_ACTION_CLOSE: + return close_fd(action->fd); + case SPAWN_TEMPLATE_ACTION_DUP2: + if (action->fd =3D=3D action->newfd) { + if (action->flags) + return -EINVAL; + CLASS(fd, f)(action->fd); + + if (fd_empty(f)) + return -EBADF; + return 0; + } + return ksys_dup3(action->fd, action->newfd, action->flags); + case SPAWN_TEMPLATE_ACTION_FCHDIR: { + CLASS(fd, f)(action->fd); + int ret; + + if (fd_empty(f)) + return -EBADF; + if (!d_can_lookup(fd_file(f)->f_path.dentry)) + return -ENOTDIR; + + ret =3D file_permission(fd_file(f), MAY_EXEC | MAY_CHDIR); + if (!ret) + set_fs_pwd(current->fs, &fd_file(f)->f_path); + return ret; + } + case SPAWN_TEMPLATE_ACTION_OPEN: + return spawn_template_apply_open(action); + case SPAWN_TEMPLATE_ACTION_CLOSE_RANGE: + return do_close_range(action->fd, action->newfd, action->flags); + case SPAWN_TEMPLATE_ACTION_SIGMASK: + return spawn_template_apply_sigmask(action); + case SPAWN_TEMPLATE_ACTION_SIGDEFAULT: + return spawn_template_apply_sigdefault(action); + default: + return -EINVAL; + } +} + +static int spawn_template_copy_actions(struct spawn_template_action **out_= actions, + u64 count, u64 uaddr) +{ + struct spawn_template_action __user *uactions; + struct spawn_template_action *actions __free(kfree) =3D NULL; + struct spawn_template_action *tmp; + u64 i; + + *out_actions =3D NULL; + if (!count) + return 0; + if (count > SPAWN_TEMPLATE_MAX_ACTIONS) + return -E2BIG; + if (!uaddr) + return -EINVAL; + + uactions =3D u64_to_user_ptr(uaddr); + tmp =3D memdup_array_user(uactions, count, sizeof(*actions)); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + actions =3D tmp; + + for (i =3D 0; i < count; i++) { + switch (actions[i].type) { + case SPAWN_TEMPLATE_ACTION_CLOSE: + if (actions[i].fd < 0 || actions[i].flags || + actions[i].newfd || actions[i].arg) + return -EINVAL; + break; + case SPAWN_TEMPLATE_ACTION_DUP2: + if (actions[i].fd < 0 || actions[i].newfd < 0 || + (actions[i].flags & ~O_CLOEXEC) || actions[i].arg) + return -EINVAL; + break; + case SPAWN_TEMPLATE_ACTION_FCHDIR: + if (actions[i].fd < 0 || actions[i].flags || + actions[i].newfd || actions[i].arg) + return -EINVAL; + break; + case SPAWN_TEMPLATE_ACTION_OPEN: + if (actions[i].fd < AT_FDCWD || actions[i].newfd < 0 || + actions[i].flags || !actions[i].arg) + return -EINVAL; + break; + case SPAWN_TEMPLATE_ACTION_CLOSE_RANGE: + if (actions[i].fd < 0 || actions[i].newfd < 0 || + actions[i].fd > actions[i].newfd || + (actions[i].flags & + ~(CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC)) || + actions[i].arg) + return -EINVAL; + break; + case SPAWN_TEMPLATE_ACTION_SIGMASK: + case SPAWN_TEMPLATE_ACTION_SIGDEFAULT: + if (actions[i].fd || actions[i].newfd || + actions[i].flags || !actions[i].arg) + return -EINVAL; + break; + default: + return -EINVAL; + } + } + + *out_actions =3D no_free_ptr(actions); + return 0; +} + +static int spawn_template_child(void *data) +{ + struct spawn_template_spawn_context *ctx =3D data; + struct spawn_template *tmpl =3D ctx->tmpl; + int ret; + u64 i; + + for (i =3D 0; i < ctx->args.actions_len; i++) { + ret =3D spawn_template_apply_action(&ctx->actions[i]); + if (ret < 0) + goto out_exec_error; + } + + if (!(ctx->args.flags & SPAWN_TEMPLATE_SPAWN_INHERIT_FDS)) { + ret =3D do_close_range(3, ~0U, 0); + if (ret < 0) + goto out_exec_error; + } + + ret =3D kernel_execveat_file(tmpl->exec_file, "", + u64_to_user_ptr(ctx->args.argv), + u64_to_user_ptr(ctx->args.envp), + AT_EMPTY_PATH); +out_exec_error: + if (ret < 0) + do_exit(spawn_template_exit_status(ret)); + return 0; +} + static bool spawn_template_file_exec_allowed(struct file *file) { if (!S_ISREG(file_inode(file)->i_mode)) @@ -53,6 +317,18 @@ static const struct file_operations spawn_template_fops= =3D { .llseek =3D noop_llseek, }; =20 +static struct file *spawn_template_file_from_fd(int fd) +{ + CLASS(fd, f)(fd); + + if (fd_empty(f)) + return ERR_PTR(-EBADF); + if (fd_file(f)->f_op !=3D &spawn_template_fops) + return ERR_PTR(-EINVAL); + + return get_file(fd_file(f)); +} + static int spawn_template_open_execfd(int execfd, struct file **file, bool *deny_write) { @@ -178,3 +454,73 @@ SYSCALL_DEFINE2(spawn_template_create, kfree(tmpl); return ret; } + +SYSCALL_DEFINE3(spawn_template_spawn, int, template_fd, + struct spawn_template_spawn_args __user *, uargs, + size_t, usize) +{ + struct spawn_template_spawn_context *ctx; + struct kernel_clone_args kargs; + struct file *template_file; + int ret; + + BUILD_BUG_ON(sizeof(struct spawn_template_spawn_args) !=3D + SPAWN_TEMPLATE_SPAWN_ARGS_SIZE_VER0); + + if (usize < SPAWN_TEMPLATE_SPAWN_ARGS_SIZE_VER0) + return -EINVAL; + if (usize > PAGE_SIZE) + return -E2BIG; + + template_file =3D spawn_template_file_from_fd(template_fd); + if (IS_ERR(template_file)) + return PTR_ERR(template_file); + + if (!spawn_template_cred_matches(template_file->private_data)) { + ret =3D -EACCES; + goto out_put_template; + } + + ctx =3D kzalloc_obj(*ctx, GFP_KERNEL); + if (!ctx) { + ret =3D -ENOMEM; + goto out_put_template; + } + + ctx->tmpl =3D template_file->private_data; + + ret =3D copy_struct_from_user(&ctx->args, sizeof(ctx->args), uargs, + usize); + if (ret) + goto out_free_ctx; + + if ((ctx->args.flags & ~SPAWN_TEMPLATE_SPAWN_INHERIT_FDS) || + !ctx->args.pidfd || ctx->args.reserved[0] || + ctx->args.reserved[1] || ctx->args.reserved[2] || + ctx->args.reserved[3]) { + ret =3D -EINVAL; + goto out_free_ctx; + } + + ret =3D spawn_template_copy_actions(&ctx->actions, ctx->args.actions_len, + ctx->args.actions); + if (ret) + goto out_free_ctx; + + kargs =3D (struct kernel_clone_args) { + .flags =3D CLONE_VM | CLONE_VFORK | CLONE_PIDFD, + .pidfd =3D u64_to_user_ptr(ctx->args.pidfd), + .exit_signal =3D SIGCHLD, + .fn =3D spawn_template_child, + .fn_arg =3D ctx, + }; + + ret =3D kernel_clone(&kargs); + + kfree(ctx->actions); +out_free_ctx: + kfree(ctx); +out_put_template: + fput(template_file); + return ret; +} diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 4b41950488bd6..df7368edf6778 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -68,6 +68,7 @@ union bpf_attr; struct io_uring_params; struct clone_args; struct spawn_template_create_args; +struct spawn_template_spawn_args; struct open_how; struct mount_attr; struct landlock_ruleset_attr; @@ -824,6 +825,9 @@ asmlinkage long sys_clone(unsigned long, unsigned long,= int __user *, asmlinkage long sys_clone3(struct clone_args __user *uargs, size_t size); asmlinkage long sys_spawn_template_create(struct spawn_template_create_arg= s __user *uargs, size_t size); +asmlinkage long sys_spawn_template_spawn(int template_fd, + struct spawn_template_spawn_args __user *uargs, + size_t size); =20 asmlinkage long sys_execve(const char __user *filename, const char __user *const __user *argv, --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6F53F3B2FE7; Thu, 28 May 2026 09:56:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962192; cv=pass; b=TnBGvdYP/88zULMWEsYqphuOu7x3+dxGTsno0p3JE6H1VZTZJVWLQu8dtiKy7u4t+XJICD9o40GRuRv4xwBzy8iFqZ9c/OtexSyeWhei9vNLs71SkUALPg/Hf2f/cZSnoN7s4SShWlyYvcOWZskCLgIHV9lKswYLFUIiCuzjIA0= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962192; c=relaxed/simple; bh=4TII/AO692VsaNBmDYt+MddTcJzDnf8i/gccwjHmGhw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DgRm/gCszUYEsHXLJbciFAQ0Khs31zq7vLc/zrFNQTmHcQwESRr0WPqE1/YsyDFD4IiWN0n8TgeMNCkHkgbM3scEb1WI1CZ1+A08fIqgWPA9C2hcHcENXy2jhE3BJZ7FKnYLjkQcEmzcItvDXtFUuAg6XUPqm78yoG0ztGtmKWQ= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=HwEjpNFq; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="HwEjpNFq" ARC-Seal: i=1; a=rsa-sha256; t=1779962035; cv=none; d=zohomail.com; s=zohoarc; b=mmg76vlXUMu7dlawi6NDHCp6O0oTFsQmfTVuLFxqSdUMbKfjEZbCh0KyTqCVHrmMYsde1TRtQGy3wcqhSv16ptcHBg7nYepm3KNUx8enEz/Pg9luF0HGRmje6Ffk3ChonbxRp8amRuih7xfjIR2x4Z2vO6iJ1E8b63xbnWSIMyQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779962035; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=S3VNKxy6o+qZDgK0d9YYafZFNdtvAg1gG3GqRA4oDgQ=; b=OzARIUo5Ve2WqOMw+nknkyJkrJSsCGrZPjykF/ig+lqyb/HwRdF9n0fix4/EF2gA8V4cCriSANsXKoMsZkB2ALbSAP2O22hTk9FSjSU16Jw0sOgHR8gOnw+ggT3GCA6dP2S8RyCkIP4bMmOO5yZxobeAbFp/ngZNWXbjSMVr07k= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779962035; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=S3VNKxy6o+qZDgK0d9YYafZFNdtvAg1gG3GqRA4oDgQ=; b=HwEjpNFqDcXQlndFpGcTxqSRdxdCivlSsAwWArDbUHbdDJDZ+pBR+x3Ams3MWFNm Q6KM6gmwzkFMLwIFWBsQhzwojPzLcDA8IoFAhesrq7q+JRiq3AhM0YIPVJdCxKZ10kR VzYR8BXEsUxAphMcHjtw/1e8rzidyACy8KlBd9Ps= Received: by mx.zohomail.com with SMTPS id 1779962032692104.2223552851018; Thu, 28 May 2026 02:53:52 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 07/13] exec: validate spawn template executable identity Date: Thu, 28 May 2026 17:52:28 +0800 Message-ID: <20260528095235.2491226-8-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Record a conservative executable identity key when a template is created: device, inode, size, mode, owner, ctime, and mtime. Recheck it before each spawn. For path-created templates, also reopen the path so a replaced executable cannot silently reuse the old template fd. Reject stale templates with ESTALE. Keep the check conservative by also rechecking that the file remains a regular executable mapping target. Signed-off-by: Li Chen --- MAINTAINERS | 1 + fs/spawn_template.c | 75 ++++++++++++++++++++++++++++++++++ include/linux/spawn_template.h | 25 ++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 include/linux/spawn_template.h diff --git a/MAINTAINERS b/MAINTAINERS index d5441812825c3..ea4134a188779 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9737,6 +9737,7 @@ F: fs/tests/binfmt_*_kunit.c F: fs/tests/exec_kunit.c F: include/linux/binfmts.h F: include/linux/elf.h +F: include/linux/spawn_template.h F: include/uapi/linux/auxvec.h F: include/uapi/linux/binfmts.h F: include/uapi/linux/elf.h diff --git a/fs/spawn_template.c b/fs/spawn_template.c index 8c3711929cffb..268f804227987 100644 --- a/fs/spawn_template.c +++ b/fs/spawn_template.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ =20 struct spawn_template { struct file *exec_file; + struct spawn_template_file_key exec_key; const struct cred *creator_cred; char *filename; bool deny_write; @@ -40,6 +42,46 @@ struct spawn_template_spawn_context { =20 static const struct file_operations spawn_template_fops; =20 +static bool spawn_template_file_exec_allowed(struct file *file); + +void spawn_template_fill_file_key(struct file *file, + struct spawn_template_file_key *key) +{ + struct inode *inode =3D file_inode(file); + struct timespec64 ctime =3D inode_get_ctime(inode); + struct timespec64 mtime =3D inode_get_mtime(inode); + + key->dev =3D inode->i_sb->s_dev; + key->ino =3D inode->i_ino; + key->size =3D i_size_read(inode); + key->mode =3D READ_ONCE(inode->i_mode); + key->uid =3D inode->i_uid; + key->gid =3D inode->i_gid; + key->ctime_sec =3D ctime.tv_sec; + key->ctime_nsec =3D ctime.tv_nsec; + key->mtime_sec =3D mtime.tv_sec; + key->mtime_nsec =3D mtime.tv_nsec; +} + +bool spawn_template_file_key_matches(struct file *file, + const struct spawn_template_file_key *key) +{ + struct spawn_template_file_key cur; + + spawn_template_fill_file_key(file, &cur); + + return cur.dev =3D=3D key->dev && + cur.ino =3D=3D key->ino && + cur.size =3D=3D key->size && + cur.mode =3D=3D key->mode && + uid_eq(cur.uid, key->uid) && + gid_eq(cur.gid, key->gid) && + cur.ctime_sec =3D=3D key->ctime_sec && + cur.ctime_nsec =3D=3D key->ctime_nsec && + cur.mtime_sec =3D=3D key->mtime_sec && + cur.mtime_nsec =3D=3D key->mtime_nsec; +} + static int spawn_template_exit_status(int err) { switch (err) { @@ -58,6 +100,32 @@ static bool spawn_template_cred_matches(struct spawn_te= mplate *tmpl) return current_cred() =3D=3D tmpl->creator_cred; } =20 +static bool spawn_template_key_matches(struct spawn_template *tmpl) +{ + bool matches; + + if (tmpl->filename) { + struct file *file __free(fput) =3D NULL; + struct file *tmp; + + tmp =3D open_exec(tmpl->filename); + if (IS_ERR(tmp)) + return false; + file =3D tmp; + + matches =3D spawn_template_file_key_matches(file, + &tmpl->exec_key); + matches =3D matches && spawn_template_file_exec_allowed(file); + exe_file_allow_write_access(file); + if (!matches) + return false; + } + + return spawn_template_file_exec_allowed(tmpl->exec_file) && + spawn_template_file_key_matches(tmpl->exec_file, + &tmpl->exec_key); +} + static int spawn_template_copy_signal_set(const struct spawn_template_acti= on *action, sigset_t *mask) { @@ -433,6 +501,7 @@ SYSCALL_DEFINE2(spawn_template_create, &tmpl->deny_write); if (ret) goto out_free_tmpl; + spawn_template_fill_file_key(tmpl->exec_file, &tmpl->exec_key); =20 if (args.flags & SPAWN_TEMPLATE_CREATE_CLOEXEC) fd_flags |=3D O_CLOEXEC; @@ -507,6 +576,11 @@ SYSCALL_DEFINE3(spawn_template_spawn, int, template_fd, if (ret) goto out_free_ctx; =20 + if (!spawn_template_key_matches(ctx->tmpl)) { + ret =3D -ESTALE; + goto out_free_actions; + } + kargs =3D (struct kernel_clone_args) { .flags =3D CLONE_VM | CLONE_VFORK | CLONE_PIDFD, .pidfd =3D u64_to_user_ptr(ctx->args.pidfd), @@ -517,6 +591,7 @@ SYSCALL_DEFINE3(spawn_template_spawn, int, template_fd, =20 ret =3D kernel_clone(&kargs); =20 +out_free_actions: kfree(ctx->actions); out_free_ctx: kfree(ctx); diff --git a/include/linux/spawn_template.h b/include/linux/spawn_template.h new file mode 100644 index 0000000000000..f14a7749fe55b --- /dev/null +++ b/include/linux/spawn_template.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_SPAWN_TEMPLATE_H +#define _LINUX_SPAWN_TEMPLATE_H + +#include + +struct spawn_template_file_key { + dev_t dev; + ino_t ino; + loff_t size; + umode_t mode; + kuid_t uid; + kgid_t gid; + u64 ctime_sec; + u64 ctime_nsec; + u64 mtime_sec; + u64 mtime_nsec; +}; + +void spawn_template_fill_file_key(struct file *file, + struct spawn_template_file_key *key); +bool spawn_template_file_key_matches(struct file *file, + const struct spawn_template_file_key *key); + +#endif /* _LINUX_SPAWN_TEMPLATE_H */ --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0FF713B4EA1; Thu, 28 May 2026 09:56:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962222; cv=pass; b=S2BzuDEjDyMgvZMlDt6GCaHIGEXmHRMvDdTY/98Bm2LL8K3VEN0a597coKIr1XjHmAU+qT8WyMIPdSwFCcffMf7ZqMfx7nYhDtn8JlmmafJmT69fOQHhkUXifN5kKYtXDMCr97Xahb8HwbPFuGPd6rUynLWG2ea/arPcEWLTjGY= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962222; c=relaxed/simple; bh=rZBETlh1Itou/PYkHOUZfg2/d01S64VPomuKbb+POXE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=u1Kt0vU12kMWVMonhNy/xf6I19lQXr819mfXbLtS5hyTHLL43NrqWh9Wm40I3Lb4M87z4CP9fxLbHCyQbCaQN1QJZjE0QlYa6DG7+331Le152MJ1Bwt397+Obc63XbOP21LJ8+feI1cmEH7e47m6kLHwAS/ni10csoRlsFc78ck= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=gBc4gfP8; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="gBc4gfP8" ARC-Seal: i=1; a=rsa-sha256; t=1779962043; cv=none; d=zohomail.com; s=zohoarc; b=BnNntCmbUKf4NUh48Z2r0u2IDbjj/w0iXW15YeIPR3N1HywAeYCyfFnZd5x83MhJrXQmz7PV4E6mGhhUm474JPLKdAnmgsmbVmC6By4b04WqT5OrersWRpczrrTDx/O3ktho+0gMD/OK1tsjg/AAbcccCD0pCVyRT7ajLvzwSZE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779962043; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=gAsVDJgosvCSWBsDL3XZWyaqruHWdvlD2MHsizjjyu4=; b=Q6Y6o7FZPjZGdtqwAn7eoC+2aoaKOs70AFdyL2Pzv8tlY90T2Bja4pYJhKJqAPXvAUdAAEJngbsEldQdSYU5YElGtavfVHbDNF5sg0FRSnFQqJLX2sm04hy7kvtDXxs7+70au3M4MxAQdCRfb6eGrgMz0kfTX6170NjHpCnW4Ms= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779962043; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=gAsVDJgosvCSWBsDL3XZWyaqruHWdvlD2MHsizjjyu4=; b=gBc4gfP8vmf5W9WDs+8sX3GZrDS/QdsZE+7Y0xITrnMA3OpX9BSGes5iHF5x2Cgo clnpmBsdfo1m3nEvdS5hROyLCqwETTgs/BhyxYlCqjHJIWd8rKGqaSTOVitsQIoLJur o+I1htuKRtuAucOk60r19tVvY3gPfCLzAdT0sIz8= Received: by mx.zohomail.com with SMTPS id 1779962041120952.1663272372527; Thu, 28 May 2026 02:54:01 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 08/13] binfmt_elf: cache ELF metadata for spawn templates Date: Thu, 28 May 2026 17:52:29 +0800 Message-ID: <20260528095235.2491226-9-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Spawn templates keep an opened executable and revalidate its file identity before every spawn. Add an ELF-side template object for the main executable. It caches the executable identity key, ELF header, program header table, and program header count so repeated spawns can reuse validated metadata. Do not cache interpreter metadata, shared-library dependency state, or derived mapping-layout state in this RFC. Keep the normal exec security path intact. The child still executes through bprm_execve(), credentials, permissions, and LSM hooks. This only avoids rereading immutable main-executable metadata after template creation and revalidation. Signed-off-by: Li Chen --- fs/binfmt_elf.c | 104 ++++++++++++++++++++++++++++++++- fs/exec.c | 37 +++++++++++- fs/spawn_template.c | 38 +++++++----- include/linux/binfmts.h | 6 ++ include/linux/spawn_template.h | 47 +++++++++++++++ 5 files changed, 213 insertions(+), 19 deletions(-) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 16a56b6b3f6ca..631dd029aeee7 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include =20 @@ -552,6 +553,89 @@ static struct elf_phdr *load_elf_phdrs(const struct el= fhdr *elf_ex, return elf_phdata; } =20 +#if !ELF_COMPAT +void spawn_exec_template_put(struct spawn_exec_template *tmpl) +{ + if (!tmpl) + return; + if (!refcount_dec_and_test(&tmpl->refcount)) + return; + kfree(tmpl->exec_phdrs); + kfree(tmpl); +} + +struct spawn_exec_template * +spawn_exec_template_get(struct spawn_exec_template *tmpl) +{ + refcount_inc(&tmpl->refcount); + return tmpl; +} + +bool spawn_exec_template_matches(struct spawn_exec_template *tmpl, + struct file *file) +{ + if (!tmpl) + return false; + if (!spawn_template_file_key_matches(file, &tmpl->exec_key)) + return false; + if (!can_mmap_file(file)) + return false; + return true; +} + +int spawn_exec_template_create(struct file *file, + struct spawn_exec_template **out) +{ + struct spawn_exec_template *tmpl; + loff_t pos =3D 0; + ssize_t nread; + int retval; + + *out =3D NULL; + + tmpl =3D kzalloc_obj(*tmpl, GFP_KERNEL); + if (!tmpl) + return -ENOMEM; + refcount_set(&tmpl->refcount, 1); + + spawn_template_fill_file_key(file, &tmpl->exec_key); + + nread =3D kernel_read(file, &tmpl->exec_ehdr, sizeof(tmpl->exec_ehdr), + &pos); + if (nread < 0) { + retval =3D nread; + goto out_put_template; + } + + retval =3D -ENOEXEC; + if (nread !=3D sizeof(tmpl->exec_ehdr)) + goto out_put_template; + if (memcmp(tmpl->exec_ehdr.e_ident, ELFMAG, SELFMAG) !=3D 0) + goto out_put_template; + if (tmpl->exec_ehdr.e_type !=3D ET_EXEC && + tmpl->exec_ehdr.e_type !=3D ET_DYN) + goto out_put_template; + if (!elf_check_arch(&tmpl->exec_ehdr)) + goto out_put_template; + if (elf_check_fdpic(&tmpl->exec_ehdr)) + goto out_put_template; + if (!can_mmap_file(file)) + goto out_put_template; + + tmpl->exec_phdrs =3D load_elf_phdrs(&tmpl->exec_ehdr, file); + if (!tmpl->exec_phdrs) + goto out_put_template; + tmpl->exec_phnum =3D tmpl->exec_ehdr.e_phnum; + + *out =3D tmpl; + return 0; + +out_put_template: + spawn_exec_template_put(tmpl); + return retval; +} +#endif + #ifndef CONFIG_ARCH_BINFMT_ELF_STATE =20 /** @@ -832,6 +916,7 @@ static int parse_elf_properties(struct file *f, const s= truct elf_phdr *phdr, static int load_elf_binary(struct linux_binprm *bprm) { struct file *interpreter =3D NULL; /* to shut gcc up */ + struct spawn_exec_template *spawn_tmpl =3D bprm->spawn_template; unsigned long load_bias =3D 0, phdr_addr =3D 0; int first_pt_load =3D 1; unsigned long error; @@ -851,6 +936,12 @@ static int load_elf_binary(struct linux_binprm *bprm) struct arch_elf_state arch_state =3D INIT_ARCH_ELF_STATE; struct mm_struct *mm; struct pt_regs *regs; + bool use_spawn_tmpl =3D spawn_exec_template_matches(spawn_tmpl, bprm->fil= e); + bool free_elf_phdata =3D true; + + if (use_spawn_tmpl) + memcpy(bprm->buf, &spawn_tmpl->exec_ehdr, + sizeof(spawn_tmpl->exec_ehdr)); =20 retval =3D -ENOEXEC; /* First of all, some simple consistency checks */ @@ -866,7 +957,12 @@ static int load_elf_binary(struct linux_binprm *bprm) if (!can_mmap_file(bprm->file)) goto out; =20 - elf_phdata =3D load_elf_phdrs(elf_ex, bprm->file); + if (use_spawn_tmpl) + elf_phdata =3D spawn_tmpl->exec_phdrs; + else + elf_phdata =3D load_elf_phdrs(elf_ex, bprm->file); + if (use_spawn_tmpl) + free_elf_phdata =3D false; if (!elf_phdata) goto out; =20 @@ -1283,7 +1379,8 @@ static int load_elf_binary(struct linux_binprm *bprm) } } =20 - kfree(elf_phdata); + if (free_elf_phdata) + kfree(elf_phdata); =20 set_binfmt(&elf_format); =20 @@ -1390,7 +1487,8 @@ static int load_elf_binary(struct linux_binprm *bprm) if (interpreter) fput(interpreter); out_free_ph: - kfree(elf_phdata); + if (free_elf_phdata) + kfree(elf_phdata); goto out; } =20 diff --git a/fs/exec.c b/fs/exec.c index 5b91a9b208a77..96b6f6274e0d3 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1914,9 +1914,12 @@ static inline struct user_arg_ptr native_arg(const c= har __user *const __user *p) return (struct user_arg_ptr){.ptr.native =3D p}; } =20 -static int do_execveat_file_common(struct file *file, struct filename *fil= ename, - struct user_arg_ptr argv, - struct user_arg_ptr envp, int flags) +static int do_execveat_file_template_common(struct file *file, + struct filename *filename, + struct user_arg_ptr argv, + struct user_arg_ptr envp, + int flags, + struct spawn_exec_template *tmpl) { struct linux_binprm *bprm; struct file *exec_file; @@ -1940,11 +1943,20 @@ static int do_execveat_file_common(struct file *fil= e, struct filename *filename, if (IS_ERR(bprm)) return PTR_ERR(bprm); =20 + bprm->spawn_template =3D tmpl; retval =3D do_execveat_common_bprm(bprm, argv, envp); free_bprm(bprm); return retval; } =20 +static int do_execveat_file_common(struct file *file, struct filename *fil= ename, + struct user_arg_ptr argv, + struct user_arg_ptr envp, int flags) +{ + return do_execveat_file_template_common(file, filename, argv, envp, + flags, NULL); +} + int kernel_execveat_file(struct file *file, const char *filename, const void __user *argv, const void __user *envp, @@ -1962,6 +1974,25 @@ int kernel_execveat_file(struct file *file, const ch= ar *filename, native_arg(user_envp), flags); } =20 +int kernel_execveat_file_template(struct file *file, const char *filename, + const void __user *argv, + const void __user *envp, int flags, + struct spawn_exec_template *tmpl) +{ + const char __user *const __user *user_argv; + const char __user *const __user *user_envp; + + CLASS(filename_kernel, name)(filename); + + user_argv =3D (const char __user *const __user *)argv; + user_envp =3D (const char __user *const __user *)envp; + + return do_execveat_file_template_common(file, name, + native_arg(user_argv), + native_arg(user_envp), + flags, tmpl); +} + void set_binfmt(struct linux_binfmt *new) { struct mm_struct *mm =3D current->mm; diff --git a/fs/spawn_template.c b/fs/spawn_template.c index 268f804227987..a11a7ed676416 100644 --- a/fs/spawn_template.c +++ b/fs/spawn_template.c @@ -28,7 +28,7 @@ =20 struct spawn_template { struct file *exec_file; - struct spawn_template_file_key exec_key; + struct spawn_exec_template *exec_template; const struct cred *creator_cred; char *filename; bool deny_write; @@ -36,6 +36,7 @@ struct spawn_template { =20 struct spawn_template_spawn_context { struct spawn_template *tmpl; + struct spawn_exec_template *exec_template; struct spawn_template_spawn_args args; struct spawn_template_action *actions; }; @@ -114,16 +115,16 @@ static bool spawn_template_key_matches(struct spawn_t= emplate *tmpl) file =3D tmp; =20 matches =3D spawn_template_file_key_matches(file, - &tmpl->exec_key); + &tmpl->exec_template->exec_key); matches =3D matches && spawn_template_file_exec_allowed(file); exe_file_allow_write_access(file); if (!matches) return false; } =20 - return spawn_template_file_exec_allowed(tmpl->exec_file) && - spawn_template_file_key_matches(tmpl->exec_file, - &tmpl->exec_key); + if (!spawn_template_file_exec_allowed(tmpl->exec_file)) + return false; + return spawn_exec_template_matches(tmpl->exec_template, tmpl->exec_file); } =20 static int spawn_template_copy_signal_set(const struct spawn_template_acti= on *action, @@ -331,26 +332,29 @@ static int spawn_template_child(void *data) { struct spawn_template_spawn_context *ctx =3D data; struct spawn_template *tmpl =3D ctx->tmpl; + struct spawn_exec_template *exec_template =3D ctx->exec_template; int ret; u64 i; =20 for (i =3D 0; i < ctx->args.actions_len; i++) { ret =3D spawn_template_apply_action(&ctx->actions[i]); if (ret < 0) - goto out_exec_error; + goto out_put_exec_template; } =20 if (!(ctx->args.flags & SPAWN_TEMPLATE_SPAWN_INHERIT_FDS)) { ret =3D do_close_range(3, ~0U, 0); if (ret < 0) - goto out_exec_error; + goto out_put_exec_template; } =20 - ret =3D kernel_execveat_file(tmpl->exec_file, "", - u64_to_user_ptr(ctx->args.argv), - u64_to_user_ptr(ctx->args.envp), - AT_EMPTY_PATH); -out_exec_error: + ret =3D kernel_execveat_file_template(tmpl->exec_file, "", + u64_to_user_ptr(ctx->args.argv), + u64_to_user_ptr(ctx->args.envp), + AT_EMPTY_PATH, + exec_template); +out_put_exec_template: + spawn_exec_template_put(exec_template); if (ret < 0) do_exit(spawn_template_exit_status(ret)); return 0; @@ -373,6 +377,7 @@ static int spawn_template_release(struct inode *inode, = struct file *file) =20 if (tmpl->deny_write) exe_file_allow_write_access(tmpl->exec_file); + spawn_exec_template_put(tmpl->exec_template); fput(tmpl->exec_file); put_cred(tmpl->creator_cred); kfree(tmpl->filename); @@ -501,7 +506,10 @@ SYSCALL_DEFINE2(spawn_template_create, &tmpl->deny_write); if (ret) goto out_free_tmpl; - spawn_template_fill_file_key(tmpl->exec_file, &tmpl->exec_key); + + ret =3D spawn_exec_template_create(tmpl->exec_file, &tmpl->exec_template); + if (ret) + goto out_put_exec; =20 if (args.flags & SPAWN_TEMPLATE_CREATE_CLOEXEC) fd_flags |=3D O_CLOEXEC; @@ -514,6 +522,7 @@ SYSCALL_DEFINE2(spawn_template_create, return ret; =20 out_put_exec: + spawn_exec_template_put(tmpl->exec_template); if (tmpl->deny_write) exe_file_allow_write_access(tmpl->exec_file); fput(tmpl->exec_file); @@ -580,6 +589,7 @@ SYSCALL_DEFINE3(spawn_template_spawn, int, template_fd, ret =3D -ESTALE; goto out_free_actions; } + ctx->exec_template =3D spawn_exec_template_get(ctx->tmpl->exec_template); =20 kargs =3D (struct kernel_clone_args) { .flags =3D CLONE_VM | CLONE_VFORK | CLONE_PIDFD, @@ -590,6 +600,8 @@ SYSCALL_DEFINE3(spawn_template_spawn, int, template_fd, }; =20 ret =3D kernel_clone(&kargs); + if (ret < 0) + spawn_exec_template_put(ctx->exec_template); =20 out_free_actions: kfree(ctx->actions); diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index c0715678c9a06..4e76a94d331a8 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -9,6 +9,7 @@ =20 struct filename; struct coredump_params; +struct spawn_exec_template; =20 #define CORENAME_MAX_SIZE 128 =20 @@ -53,6 +54,7 @@ struct linux_binprm { struct file *executable; /* Executable to pass to the interpreter */ struct file *interpreter; struct file *file; + struct spawn_exec_template *spawn_template; struct cred *cred; /* new credentials */ int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */ unsigned int per_clear; /* bits to clear in current->personality */ @@ -145,6 +147,10 @@ int kernel_execveat_file(struct file *file, const char= *filename, const void __user *argv, const void __user *envp, int flags); +int kernel_execveat_file_template(struct file *file, const char *filename, + const void __user *argv, + const void __user *envp, int flags, + struct spawn_exec_template *tmpl); extern void set_binfmt(struct linux_binfmt *new); extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t); =20 diff --git a/include/linux/spawn_template.h b/include/linux/spawn_template.h index f14a7749fe55b..426413bc11eea 100644 --- a/include/linux/spawn_template.h +++ b/include/linux/spawn_template.h @@ -2,7 +2,9 @@ #ifndef _LINUX_SPAWN_TEMPLATE_H #define _LINUX_SPAWN_TEMPLATE_H =20 +#include #include +#include =20 struct spawn_template_file_key { dev_t dev; @@ -17,9 +19,54 @@ struct spawn_template_file_key { u64 mtime_nsec; }; =20 +struct spawn_exec_template { + refcount_t refcount; + struct spawn_template_file_key exec_key; + struct elfhdr exec_ehdr; + struct elf_phdr *exec_phdrs; + unsigned int exec_phnum; +}; + void spawn_template_fill_file_key(struct file *file, struct spawn_template_file_key *key); bool spawn_template_file_key_matches(struct file *file, const struct spawn_template_file_key *key); =20 +#ifdef CONFIG_BINFMT_ELF +int spawn_exec_template_create(struct file *file, + struct spawn_exec_template **out); +struct spawn_exec_template * +spawn_exec_template_get(struct spawn_exec_template *tmpl); +void spawn_exec_template_put(struct spawn_exec_template *tmpl); +bool spawn_exec_template_matches(struct spawn_exec_template *tmpl, + struct file *file); +#else +static inline int spawn_exec_template_create(struct file *file, + struct spawn_exec_template **out) +{ + (void)file; + (void)out; + return -ENOEXEC; +} + +static inline void spawn_exec_template_put(struct spawn_exec_template *tmp= l) +{ + (void)tmpl; +} + +static inline struct spawn_exec_template * +spawn_exec_template_get(struct spawn_exec_template *tmpl) +{ + return tmpl; +} + +static inline bool spawn_exec_template_matches(struct spawn_exec_template = *tmpl, + struct file *file) +{ + (void)tmpl; + (void)file; + return false; +} +#endif + #endif /* _LINUX_SPAWN_TEMPLATE_H */ --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 44CFE3B5847; Thu, 28 May 2026 09:57:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962252; cv=pass; b=jyb9OaFk8s8xcqZ2jegxUG1ubtBAVsyekoxWiScWz2eR7IFSpl2k44nzKIgufcaJKHazm8UsHuU91Wpj+JYsHc4CMkuLQLKd84ttGGrmG9x/3NXE+PBTa7ffnbKIFgcMmq3Mi4WA2GpkmqGPki/hBYO2bmi3UPz9EpXsL4qIgv8= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962252; c=relaxed/simple; bh=/vloOMuix9ghwkZ6SfHmjkehS2eNqsjjTmfJz4yakoE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sDuuPQDPsqJm/89Io3x9mrjftNbVny/qY2tOTSEQzIh0m8sCJ2auTF3qb7Rdils2zV3lC+4ZCc+NrZgBewMZ2JHUXPL32Ih69Wl6zUK69XKpTHntsIRkUvHp5TV1mfT+HIpx8YWa0RXEaIucHkfHDV93igNev56VqxGYhbRapGw= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=DhSwfSV5; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="DhSwfSV5" ARC-Seal: i=1; a=rsa-sha256; t=1779962051; cv=none; d=zohomail.com; s=zohoarc; b=mcw8OsKXKqqUZExZTfgJV8uxJ+tTUcrqPFqDJc+RjTr6dwtZ8YBIsocF7BTtYSH76cjUpBrpaD2CMCi5+pPs3lWv0bUFxthgQRyEActcciCWbq9vd4FKLoFjCF2kiVJclqdwe276aU9yuyKr1P3YGCtsU5I/OerV9S5jFp0+tdM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779962051; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=daeNay/B7krhkGaOXcpEKi+TINPHP/xa1Qaj59GhpT8=; b=KSi0NYjp+lU4rA8L1T6YaXxF0xn8z5PMQELBeMPsQQmEHeOXDceccXAYVtWVaFkHbMcUPjxPJH/ha/27fGRl4XlZMTJaSGEKsnVkOaOmijyrDmlXL3doS0Q9DG6FfMDa6Qkl1BRA6Tr7+BfTHwJrk6CNIxHVy4qoduoBhmWOpqw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779962051; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=daeNay/B7krhkGaOXcpEKi+TINPHP/xa1Qaj59GhpT8=; b=DhSwfSV5fxHbEYfKY8w+TuhtwRb0Pn50kcNXlaE1NvCJZDMBaPSi2c2/30I7VZzb gm05H/saGHdvUT0KvMDvopE6rQAU9a5sd3LSdf0K9qFEu3T85aWpTitVZNTUsXlI+7s DiiWTbt81HFYOFZbCChrAJ2XuzjwiDn0/qf0p6bc= Received: by mx.zohomail.com with SMTPS id 1779962049658625.4597262662478; Thu, 28 May 2026 02:54:09 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 09/13] Documentation: describe spawn templates Date: Thu, 28 May 2026 17:52:30 +0800 Message-ID: <20260528095235.2491226-10-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Document the spawn_template userspace ABI, fd lifetime, per-spawn actions, default fd-closing behavior, security model, invalidation, and cached ELF metadata. Keep workload-specific benchmark details out of the kernel documentation. Add the spawn template files to the exec/binfmt MAINTAINERS entry so the documentation, UAPI, internal header, and implementation are covered in the same patch. Signed-off-by: Li Chen --- Documentation/userspace-api/index.rst | 1 + .../userspace-api/spawn_template.rst | 141 ++++++++++++++++++ MAINTAINERS | 2 + 3 files changed, 144 insertions(+) create mode 100644 Documentation/userspace-api/spawn_template.rst diff --git a/Documentation/userspace-api/index.rst b/Documentation/userspac= e-api/index.rst index a68b1bea57a85..28520d16d3862 100644 --- a/Documentation/userspace-api/index.rst +++ b/Documentation/userspace-api/index.rst @@ -22,6 +22,7 @@ System calls ioctl/index mseal rseq + spawn_template =20 Security-related interfaces =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D diff --git a/Documentation/userspace-api/spawn_template.rst b/Documentation= /userspace-api/spawn_template.rst new file mode 100644 index 0000000000000..0396d292fd17d --- /dev/null +++ b/Documentation/userspace-api/spawn_template.rst @@ -0,0 +1,141 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Spawn templates +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +``spawn_template`` is a userspace-controlled interface for workloads that +repeatedly start the same executable with different arguments, environment= , and +file-descriptor setup. + +Userspace creates a template fd for an executable with +``spawn_template_create()``. Later calls to ``spawn_template_spawn()`` cr= eate a +new child from that template and return both a pid and a pidfd. The child= still +executes through the normal ``execve`` path. The template only lets the k= ernel +reuse metadata that is safe to reuse after revalidation. + +This is intended for launchers, shells, and agent runtimes that already kn= ow +which tools are hot. The kernel does not decide policy for names such as +``rg``, ``git``, or ``sed``. Userspace should keep its existing spawn pat= h as a +fallback for unsupported files, invalidated templates, and policy decision= s. + +This RFC version supports ELF executable templates only. Scripts, binfmt_= misc +targets, and other non-ELF formats are expected to use the fallback path. + +Template lifetime +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +``spawn_template_create()`` takes ``struct spawn_template_create_args`` and +returns a template fd. The fd is an ordinary file descriptor backed by an +anonymous inode. Closing the fd releases the template. + +Userspace can identify the executable either by an existing executable fd = or by +path. Exactly one of ``execfd`` and ``filename`` must be supplied. Passi= ng +``SPAWN_TEMPLATE_CREATE_CLOEXEC`` sets ``O_CLOEXEC`` on the returned templ= ate +fd. + +Creating a template for an unsupported executable format fails. For this = RFC +that means non-ELF executables fail template creation rather than becoming= a +partially cached template. + +Create-time fd actions are not supported. ``actions`` and ``actions_len``= in +``struct spawn_template_create_args`` are reserved and must be zero. File +descriptor numbers are per-process state, so reusable fd actions would be +ambiguous once the creating process changes its fd table. + +Spawning +=3D=3D=3D=3D=3D=3D=3D=3D + +``spawn_template_spawn()`` takes a template fd and +``struct spawn_template_spawn_args``. ``argv`` and ``envp`` point to the = normal +userspace argument and environment vectors for the new image. ``pidfd`` p= oints +to an ``int`` in userspace where the kernel stores the new pidfd. The sys= call +return value is the new pid on success. + +A successful ``spawn_template_spawn()`` return means the child has been cr= eated +and the pidfd has been installed. After that point, per-spawn action fail= ures +or exec failures are reported by the child exit status, not by changing the +syscall return value. The syscall itself returns a negative errno only for +errors detected before child creation, such as bad arguments, a bad templa= te +fd, stale executable identity, or clone failure. + +Per-spawn actions run in the child before exec. They are intended for the= same +kind of setup that ``posix_spawn_file_actions_t`` commonly performs: + +``SPAWN_TEMPLATE_ACTION_CLOSE`` + Close one fd. + +``SPAWN_TEMPLATE_ACTION_DUP2`` + Duplicate one fd to another fd, optionally with ``O_CLOEXEC``. + +``SPAWN_TEMPLATE_ACTION_FCHDIR`` + Change the child's current working directory to an open directory fd. + +``SPAWN_TEMPLATE_ACTION_OPEN`` + Open a path using ``struct open_how`` and install it at ``newfd``. + +``SPAWN_TEMPLATE_ACTION_CLOSE_RANGE`` + Apply ``close_range()`` to a child fd range. + +``SPAWN_TEMPLATE_ACTION_SIGMASK`` + Set the child signal mask. + +``SPAWN_TEMPLATE_ACTION_SIGDEFAULT`` + Reset selected signal dispositions to ``SIG_DFL``. + +By default, the child closes all inherited file descriptors above standard +error after the requested actions have run. Passing +``SPAWN_TEMPLATE_SPAWN_INHERIT_FDS`` keeps the traditional inheritance mod= el. +Launchers for untrusted or secret-bearing workloads should prefer the defa= ult. + +Security model +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +``spawn_template_spawn()`` is not a shortcut around ``execve`` security. = Each +spawn still reaches the normal binary handler and credential commit path, = so +permission checks, LSM hooks, secure-exec handling, and ``no_new_privs`` r= emain +part of execution. + +The template fd does not grant ambient authority to unrelated tasks. The +current implementation requires the caller to have the same credential obj= ect +that created the template. Passing the fd with ``SCM_RIGHTS`` is therefor= e not +enough to delegate spawn authority after credentials have changed. + +The kernel pins the executable inode against writes while the template exi= sts. +An in-place writer therefore fails while a template fd is alive. A package +manager can still replace a tool with a rename; a path-created template th= en +sees that the absolute path resolves to a different executable and spawn f= ails +before creating a child. Userspace can close the old template fd and crea= te a +new one after such an update. + +Each spawn revalidates cached identity metadata before using template meta= data. +The key includes device, inode, size, mode, owner, ctime, and mtime. +Path-created templates re-open the path before child creation and reject r= euse +if the path now names a different executable. + +Cached metadata +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +For ELF executables, the template caches only the main executable ELF head= er, +program headers, and executable identity key. The cached program headers = are +used to avoid repeated metadata reads for hot executables after the execut= able +identity has been revalidated. + +The cache does not include the shared-library dependency graph. Shared +libraries are found by the userspace dynamic linker after exec and depend = on +userspace policy such as ``LD_LIBRARY_PATH``, ``RPATH``, ``RUNPATH``, +``/etc/ld.so.cache``, mount namespaces, and secure-exec state. The kernel +therefore does not try to duplicate dynamic-linker policy in a spawn templ= ate. + +Errors and fallback +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +If template creation reports an unsupported format, or if spawn reports a = stale +template before child creation, the caller should use its existing spawn +implementation. A launcher may also drop the template fd and create a new +template after a failure. Once spawn has returned a pid, the caller should +observe child success or failure by waiting on the pid or pidfd. + +The interface is designed so ordinary tools do not need to be modified. +Runtimes that already centralize process launch can opt in one executable = at a +time and preserve their existing fallback behavior. diff --git a/MAINTAINERS b/MAINTAINERS index ea4134a188779..3e737097940f9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9728,7 +9728,9 @@ M: Kees Cook L: linux-mm@kvack.org S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-ne= xt/execve +F: arch/x86/entry/syscalls/syscall_64.tbl F: Documentation/userspace-api/ELF.rst +F: Documentation/userspace-api/spawn_template.rst F: fs/*binfmt_*.c F: fs/Kconfig.binfmt F: fs/exec.c --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 02F813B47C4; Thu, 28 May 2026 09:57:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962278; cv=pass; b=DkwOpEyM81iUn1LK88hHbxkiO+O4eUJEGtneHSaGTr9OgEyuxoPxC0v1u/4iPdgRebBdz4+Q5uA0yq3PSf3djb+tLRWnjQqYMfL+YrpWp4ojyZh/qGZsthyxKO9gvses33k6r0QOyifo7qm3AeljAJEVGuB68ygdn+iRxD1oDpE= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962278; c=relaxed/simple; bh=z+oC9ewpPw1wqe8Lxu1OmdVUASLXJvs/4YmKTwjOah0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IWBUAJ4bl3LaikUqYYdpvelYfFjE/Jx4Lmvu9jxuYT1FIG21zHUKLygvcOgd2u0HN8e5IGQApcnfEIigoQGsfT8QX/mJ+yeYoWcdp4XhEt+uzpRypusfbylJL6YiRW/NgpSV+ztY9sAiW+G8Pz0gkDFXf45rYEovhLYeWTl8fps= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=O3C9UCiZ; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="O3C9UCiZ" ARC-Seal: i=1; a=rsa-sha256; t=1779962061; cv=none; d=zohomail.com; s=zohoarc; b=ZSD0dV6lu2Vs+GZU/uLmqBVKLcZQTGeGjy2Gjh6UBP4213jrrG+4btwTtJgRBcLfDVNWnhfpATXTDqCtCqF0rKfy6gp+ZLTdaIrKrlSPzsYPh1HbbI1LZSJzSTLDLL5MGTqbyQQafwD93JyAq1SKIsIPDkivvJ5Bblsdq5bnJoc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779962061; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=WEVp4I6koulmeQRg+AIvYnO6/dhMP9EJtXGNcBGqGsg=; b=FNBcgu95moxMmh04dwtDpU3B829r1xYZ9iRBsf/YE6S4c4QtVhaVzAMDUlUX4MWCoroEvqNGPXjXHfsio9lyXB4WiERWwyU2li6Uulk7E3TTLnhNi4S9eeHahOa+4KtIrWHk8aj4Hp4pUI8Mac3K85dj2rnbcMePE5zV/cT06mk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779962061; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=WEVp4I6koulmeQRg+AIvYnO6/dhMP9EJtXGNcBGqGsg=; b=O3C9UCiZqPS7hLT7Zuq6qgdvMasGpu8vLibnOglnjFiWu1gjTzYAPeAftjYYrShv 6MpmBwRW6piWgJpWqTBAdKclQQ4VVK8DZFPDtnzMhg8BCSBfKuNyRkGcj26edyh+O7V Rplpyc9maRQnBeYk571pu4AG/nEK3MuPZtKRUFBo= Received: by mx.zohomail.com with SMTPS id 1779962058247658.000025721033; Thu, 28 May 2026 02:54:18 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 10/13] exec: require absolute paths for path-created templates Date: Thu, 28 May 2026 17:52:31 +0800 Message-ID: <20260528095235.2491226-11-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Path-created spawn templates re-open the stored path during spawn-time revalidation. A relative path would be interpreted against the caller cwd at spawn time, not necessarily the cwd used when the template was created. Reject relative paths for now. Userspace can resolve the executable first or create the template from an executable fd when it needs cwd-relative lookup. Signed-off-by: Li Chen --- Documentation/userspace-api/spawn_template.rst | 17 ++++++++++++++--- fs/spawn_template.c | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Documentation/userspace-api/spawn_template.rst b/Documentation= /userspace-api/spawn_template.rst index 0396d292fd17d..afe215e51db6f 100644 --- a/Documentation/userspace-api/spawn_template.rst +++ b/Documentation/userspace-api/spawn_template.rst @@ -30,9 +30,20 @@ returns a template fd. The fd is an ordinary file descr= iptor backed by an anonymous inode. Closing the fd releases the template. =20 Userspace can identify the executable either by an existing executable fd = or by -path. Exactly one of ``execfd`` and ``filename`` must be supplied. Passi= ng -``SPAWN_TEMPLATE_CREATE_CLOEXEC`` sets ``O_CLOEXEC`` on the returned templ= ate -fd. +an absolute path. Exactly one of ``execfd`` and ``filename`` must be supp= lied. +Passing ``SPAWN_TEMPLATE_CREATE_CLOEXEC`` sets ``O_CLOEXEC`` on the return= ed +template fd. + +Relative paths are rejected for path-created templates. The kernel stores= the +filename and re-opens it at spawn time to check that the path still names = the +same executable. A relative filename would be resolved against the caller= 's +current working directory at spawn time, not the directory that was current +when the template was created. For example, a template created for ``bin/= tool`` +while the caller is in ``/repo-a`` could later be spawned after the caller= has +changed to ``/repo-b``. Revalidating ``bin/tool`` would then look under +``/repo-b`` and give different semantics from the executable that was +originally templated. Userspace that wants directory-relative lookup shou= ld +open the executable itself and create the template from ``execfd``. =20 Creating a template for an unsupported executable format fails. For this = RFC that means non-ELF executables fail template creation rather than becoming= a diff --git a/fs/spawn_template.c b/fs/spawn_template.c index a11a7ed676416..6430a6645fb57 100644 --- a/fs/spawn_template.c +++ b/fs/spawn_template.c @@ -441,6 +441,8 @@ static int spawn_template_open_filename(u64 filename, s= truct file **file, tmp =3D strndup_user(u64_to_user_ptr(filename), PATH_MAX); if (IS_ERR(tmp)) return PTR_ERR(tmp); + if (tmp[0] !=3D '/') + return -EINVAL; kfilename =3D tmp; =20 tmp_file =3D open_exec(kfilename); --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0E70F3ACEF6; Thu, 28 May 2026 09:58:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962305; cv=pass; b=oQJqUPjThrddSf1N8mUl4H4mPa8RKl37cdnp6cop14zKqz//sKpCEWgnRlHX32AxvIGf8BckKYLSrNyfXN5HTD0gCpoz19b4/o7xM70SAcY8PBw3o6QYlJL/ErwASc7MKZHFbZ91rYXjTxS8N2rY5u24ZGxdIqx8S+ENwH7Lcv4= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962305; c=relaxed/simple; bh=TNQhlKE5ncbDbax/x2D0txr5da9b+jQxZpuKlwO+Tdg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Cq3kefsREVKzazq4+kX1BJ3wWGPw0ZI/cP+cjLJH/tXiCRHLYK+liTXO3dAGaemKU5q9ae+bF1BY+SCuy02yBKZk8RC2DEofoCPyzK+WRH9HlXSGeWKGhBHs7H44+GtofQZOe/GSEq705wIfy+tL4vWS8Nz3cqyifDkjuxMqs5w= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=YUJuo6pz; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="YUJuo6pz" ARC-Seal: i=1; a=rsa-sha256; t=1779962067; cv=none; d=zohomail.com; s=zohoarc; b=dB9yOHSNdn+pWFMuwv0a5OAZPV6rGF6mxWBxJYpmxnEQhcE9PL2qzlDtn+HCW4P572tQZIbnSjl1UThemLDSlHOb1ox6eGvNTl2c30rdT7plQX0/sSxhEynNGE244uzmVMwUwGIUYH+I0SGH+lM+u5tpjxoWeVbb1D1BLn/sFLw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779962067; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=IHFIJ1vwh+H9qkGl5p1gj4xNKLC/W2D7/bdfKg3hdPk=; b=mYzWwm9UB/Ibj317Bb1PC9rRaGISOA2iVqGm0hi+RK+JmoPPQIbXazZ02brt9atvBtr7mcXQkRWqrtiUSXokiG/HC+l1oUn8LBEK9bORfoWQ6/qwCPn0NTi2lKR4hfTze889JeRBdk4JJsTnf1qTswL7XgVnizMzdl5FMmSrGSM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779962067; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=IHFIJ1vwh+H9qkGl5p1gj4xNKLC/W2D7/bdfKg3hdPk=; b=YUJuo6pzThKZZRmNDytEoVG8XxhsJEgV2A64AF9QCP+z4KxxS9ZpHYsTi5cgWN+m xbowODJ26oN67r27JDk1J0H00zzU65hURcQW+e5i88PENqjWcJCO40KgZkUszKdfKLF QCRs14tqi6O2lD4jNAljNciyS8r70RPa3Ie1MejE= Received: by mx.zohomail.com with SMTPS id 1779962065945647.9310517427938; Thu, 28 May 2026 02:54:25 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 11/13] exec: let close-range actions target the max fd Date: Thu, 28 May 2026 17:52:32 +0800 Message-ID: <20260528095235.2491226-12-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Allow CLOSE_RANGE actions to pass newfd =3D=3D -1 to mean the largest possible fd. This gives userspace a compact way to request the common close_range(first, ~0U, flags) pattern even though the UAPI action uses signed fd fields so OPEN actions can still carry AT_FDCWD. Signed-off-by: Li Chen --- Documentation/userspace-api/spawn_template.rst | 3 ++- fs/spawn_template.c | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Documentation/userspace-api/spawn_template.rst b/Documentation= /userspace-api/spawn_template.rst index afe215e51db6f..be66be20d4fde 100644 --- a/Documentation/userspace-api/spawn_template.rst +++ b/Documentation/userspace-api/spawn_template.rst @@ -86,7 +86,8 @@ kind of setup that ``posix_spawn_file_actions_t`` commonl= y performs: Open a path using ``struct open_how`` and install it at ``newfd``. =20 ``SPAWN_TEMPLATE_ACTION_CLOSE_RANGE`` - Apply ``close_range()`` to a child fd range. + Apply ``close_range()`` to a child fd range. Passing ``newfd =3D=3D -1`= ` means + the range extends to the largest possible fd. =20 ``SPAWN_TEMPLATE_ACTION_SIGMASK`` Set the child signal mask. diff --git a/fs/spawn_template.c b/fs/spawn_template.c index 6430a6645fb57..82b833bc9865a 100644 --- a/fs/spawn_template.c +++ b/fs/spawn_template.c @@ -220,6 +220,8 @@ static int spawn_template_apply_sigdefault(const struct= spawn_template_action *a =20 static int spawn_template_apply_action(const struct spawn_template_action = *action) { + unsigned int max_fd; + switch (action->type) { case SPAWN_TEMPLATE_ACTION_CLOSE: return close_fd(action->fd); @@ -251,7 +253,8 @@ static int spawn_template_apply_action(const struct spa= wn_template_action *actio case SPAWN_TEMPLATE_ACTION_OPEN: return spawn_template_apply_open(action); case SPAWN_TEMPLATE_ACTION_CLOSE_RANGE: - return do_close_range(action->fd, action->newfd, action->flags); + max_fd =3D action->newfd =3D=3D -1 ? ~0U : action->newfd; + return do_close_range(action->fd, max_fd, action->flags); case SPAWN_TEMPLATE_ACTION_SIGMASK: return spawn_template_apply_sigmask(action); case SPAWN_TEMPLATE_ACTION_SIGDEFAULT: @@ -306,8 +309,9 @@ static int spawn_template_copy_actions(struct spawn_tem= plate_action **out_action return -EINVAL; break; case SPAWN_TEMPLATE_ACTION_CLOSE_RANGE: - if (actions[i].fd < 0 || actions[i].newfd < 0 || - actions[i].fd > actions[i].newfd || + if (actions[i].fd < 0 || actions[i].newfd < -1 || + (actions[i].newfd >=3D 0 && + actions[i].fd > actions[i].newfd) || (actions[i].flags & ~(CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC)) || actions[i].arg) --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1BC0E38A73B; Thu, 28 May 2026 09:58:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962331; cv=pass; b=AspiLSh9k7OGfr1c3Tde71D6nP3CLCtzgeJeQSY473lu6W1Jx+9gkY9tjWvOR3N2RpYOF99K9Bl6I5UdOePe1/omKHOok0u19CsN/iT0FSRDytTO98W0dFCZ24h6991CPhpP5JMnoDFh4WCDRCrAF0WcAGvtv+vRvEPRdN5EPug= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962331; c=relaxed/simple; bh=NQuwJ0bwOiDTJ90aRko1yQGQwq034q6tsfagPPUJkvk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BOq5PuWbholjPhkfLxo/VH1DUJxFEO/afUxyw63s14lMmzfEFzmZ8MmJNKlbMjHXBsgZAEq2UJ2Bl/cxPThzsVTeESbH4uDNiFbc8zQ4TvS8vz8BOCQoQXk4RELau9JzQ2k3ZxILJPbMO129CmwtX5W2zh/BvW1mgQ2ROFFMieA= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=hs9jAnFc; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="hs9jAnFc" ARC-Seal: i=1; a=rsa-sha256; t=1779962078; cv=none; d=zohomail.com; s=zohoarc; b=W0w/mn2J1AYfM3IS7vZfMX0gJ8ZaymQK2XDEVIjpTpvOwgMEYhvZLwobodhV5irOmS8XeUKpxQfYjDUJIVqUB2STeoXKQYIai/08OX5v/5YaeyHDG6HdfNJMqG/CrlNQ5bbUIBEQdNIjhxZDHpU7XvDI0/CjZ7T/fG8z0cLkbxM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779962078; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=LBP3jruN2RkIXP5r6tCnATnUH33LnY6Ig4uYaIj36zY=; b=FnNdIqrmJXESp6ZdHOgHw7hif7sa6TuZIIpNebGnePeOrG8CiOZubbu0gMUo5fcjwkbLIJkzBWfYg54nVwE+8RvVYJRaIkFuH3cI9vrlNvR8d+FuiG9NrfchomBszf2xtYQcMNqKXRmnLL31MPkcg02cts6MI7MmtHCdyy4EstE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779962078; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=LBP3jruN2RkIXP5r6tCnATnUH33LnY6Ig4uYaIj36zY=; b=hs9jAnFcSis+pCZhFYCvTa0bWXgdCxYbVdlMupKZJ8yIlMw7s6ao28m9iC0adpex kxSyjfM4rCkyLSSGivfV3lc0J6qF85zRgXHX6GkmKKCPsUq+en4GUudCpv7v3oT21VC +m4xFhBxhOqNUd9SgBpgmr2mxUcTzWwh8/8Q/fwM= Received: by mx.zohomail.com with SMTPS id 1779962074218341.93123023468877; Thu, 28 May 2026 02:54:34 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 12/13] syscalls: add generic spawn template entries Date: Thu, 28 May 2026 17:52:33 +0800 Message-ID: <20260528095235.2491226-13-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Add spawn_template_create() and spawn_template_spawn() to the generic syscall table and asm-generic UAPI numbering. This lets architectures using the generic table pick up the spawn-template ABI instead of leaving the mechanism x86-only. Signed-off-by: Li Chen --- arch/x86/entry/syscalls/syscall_64.tbl | 2 ++ include/uapi/asm-generic/unistd.h | 7 ++++++- scripts/syscall.tbl | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscal= ls/syscall_64.tbl index d6c1667e8f3b8..e9dcfc6de79bc 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 64 spawn_template_create sys_spawn_template_create +473 64 spawn_template_spawn sys_spawn_template_spawn # # Due to a historical design error, certain syscalls are numbered differen= tly # in x32 as compared to native x86_64. These syscalls have numbers 512-54= 7. diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/u= nistd.h index a627acc8fb5fe..8589f2b9696a7 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -863,8 +863,13 @@ __SYSCALL(__NR_listns, sys_listns) #define __NR_rseq_slice_yield 471 __SYSCALL(__NR_rseq_slice_yield, sys_rseq_slice_yield) =20 +#define __NR_spawn_template_create 472 +__SYSCALL(__NR_spawn_template_create, sys_spawn_template_create) +#define __NR_spawn_template_spawn 473 +__SYSCALL(__NR_spawn_template_spawn, sys_spawn_template_spawn) + #undef __NR_syscalls -#define __NR_syscalls 472 +#define __NR_syscalls 474 =20 /* * 32 bit systems traditionally used different diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl index 7a42b32b65776..7f8e74e866e48 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 spawn_template_create sys_spawn_template_create +473 common spawn_template_spawn sys_spawn_template_spawn --=20 2.52.0 From nobody Sun Jun 7 20:00:27 2026 Received: from sender4-op-o15.zoho.com (sender4-op-o15.zoho.com [136.143.188.15]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9EE263B5E01; Thu, 28 May 2026 09:59:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.15 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962357; cv=pass; b=U66jaQBaFYanWmq68SKk3Mbn2gmVcxqrHHerAPVJX8MAZpk1LiTDom30gbMN2JkPQ74PdFcrs70coIQNbjUZFZ8kMXWKezda8cVzkN+tmw/a1wwCVJL+YWpKSHHcXqPMqD/F8slIXumPPpNE0O/dtWhv0BxHkMXHr2MQV+NcLsA= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779962357; c=relaxed/simple; bh=jF3AWoihXMBHsrmmEySe48csTm7qCIg0CUpl56qJ89k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EWITcgfLQczGDNNnaLxkIVeD6f59ma2rS45SofPDmcWzB+xqZC4V4PqTsxSZ0xI4N432YsRmz+OK9V5ekeespUhMxCRMbMsvXrb6W8MhO//X8yEyukPOIVct3VSFYm7IsBOm+LmVnK5YKx0e6nDf+8hBsHQMF96cII8OuAl0LDc= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty; spf=pass smtp.mailfrom=linux.beauty; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b=fB2poWTT; arc=pass smtp.client-ip=136.143.188.15 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.beauty Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.beauty Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.beauty header.i=me@linux.beauty header.b="fB2poWTT" ARC-Seal: i=1; a=rsa-sha256; t=1779962086; cv=none; d=zohomail.com; s=zohoarc; b=IV+H3sR/s4LGSiLXOx1PnR+Nukag6obH8xuIZbHHMcHnvSdvg/dKK9XVoQkzlQYJh8698Un01ZAi4nWycBICxxSSZlpVoDJvtZ6fBuqQaiZCvc+wpfnyybofe8sV8NNlYa4cffCrcEtJ0tXQKhz1D2hmyTNFzKCDkBJRr/lLm2M= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1779962086; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=FDRtEtjtIvSHNVwloa1d75aPePQb0NsWFeazlEaiOCE=; b=MeLLDTsLE+wCC14RD5Fk+oIlV0C7Zwn//LnPiBhw/z+RolOX+VofYUMJMKbVgmhD980fUygLGSfz0Z6r3nqwnjnzUcX9woTYpViTEPeSzfGDKpl5ddPB8eVDUB0HYV4LiG+uZodbDyby2EMJFPt0iEdFXXhDvR88fibKoQLLtEw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=linux.beauty; spf=pass smtp.mailfrom=me@linux.beauty; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1779962086; s=zmail; d=linux.beauty; i=me@linux.beauty; h=From:From:To:To:Cc:Cc:Subject:Subject:Date:Date:Message-ID:In-Reply-To:References:MIME-Version:Content-Transfer-Encoding:Message-Id:Reply-To; bh=FDRtEtjtIvSHNVwloa1d75aPePQb0NsWFeazlEaiOCE=; b=fB2poWTT4hqNMJvy4+J0IcPlguQBHa1w2BGiMHKSfCz4ps7V84meo3h85WjnwKLs arrm+GNyafNthjskyXUMfktGgv7nWUhtxDOhZrUocDO3eGg+xJ0a8dA8YBYLBOoJY5c r7Z9ETU8zav0wARgfeMz3M8f3V+Qb5x14QmYgcwk= Received: by mx.zohomail.com with SMTPS id 1779962083230365.44773868125844; Thu, 28 May 2026 02:54:43 -0700 (PDT) From: Li Chen To: Christian Brauner , Kees Cook , Alexander Viro Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, x86@kernel.org, Arnd Bergmann , Andy Lutomirski , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , "H. Peter Anvin" , Jan Kara , Jonathan Corbet , Shuah Khan , Li Chen Subject: [RFC PATCH v1 13/13] selftests/exec: cover spawn template basics Date: Thu, 28 May 2026 17:52:34 +0800 Message-ID: <20260528095235.2491226-14-me@linux.beauty> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260528095235.2491226-1-me@linux.beauty> References: <20260528095235.2491226-1-me@linux.beauty> 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 X-ZohoMailClient: External Content-Type: text/plain; charset="utf-8" Add exec selftests for the spawn_template ABI. Cover basic spawning, relative path rejection, execfd execute-permission checks, default fd closing, close-range actions using newfd -1, and stale path rejection after executable metadata changes. Also cover atomic path replacement while a template fd for an old path is still alive. The old template must reject the changed path with ESTALE, and a new template for the same path must execute the replacement. Signed-off-by: Li Chen --- MAINTAINERS | 1 + tools/testing/selftests/exec/Makefile | 1 + tools/testing/selftests/exec/spawn_template.c | 997 ++++++++++++++++++ 3 files changed, 999 insertions(+) create mode 100644 tools/testing/selftests/exec/spawn_template.c diff --git a/MAINTAINERS b/MAINTAINERS index 3e737097940f9..77b3da32b4d2a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9747,6 +9747,7 @@ F: include/uapi/linux/spawn_template.h F: kernel/fork.c F: mm/vma_exec.c F: tools/testing/selftests/exec/ +F: tools/testing/selftests/exec/spawn_template.c N: asm/elf.h N: binfmt =20 diff --git a/tools/testing/selftests/exec/Makefile b/tools/testing/selftest= s/exec/Makefile index 45a3cfc435cfd..cf39fe916b9ba 100644 --- a/tools/testing/selftests/exec/Makefile +++ b/tools/testing/selftests/exec/Makefile @@ -20,6 +20,7 @@ TEST_FILES :=3D Makefile TEST_GEN_PROGS +=3D recursion-depth TEST_GEN_PROGS +=3D null-argv TEST_GEN_PROGS +=3D check-exec +TEST_GEN_PROGS +=3D spawn_template =20 EXTRA_CLEAN :=3D $(OUTPUT)/subdir.moved $(OUTPUT)/execveat.moved $(OUTPUT)= /xxxxx* \ $(OUTPUT)/S_I*.test diff --git a/tools/testing/selftests/exec/spawn_template.c b/tools/testing/= selftests/exec/spawn_template.c new file mode 100644 index 0000000000000..26708143ac9dc --- /dev/null +++ b/tools/testing/selftests/exec/spawn_template.c @@ -0,0 +1,997 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "kselftest.h" + +#ifndef __NR_spawn_template_create +#define __NR_spawn_template_create 472 +#endif + +#ifndef __NR_spawn_template_spawn +#define __NR_spawn_template_spawn 473 +#endif + +#define SPAWN_TEMPLATE_MISSING_SYSCALL_ERRNO 38 +#define SPAWN_TEMPLATE_KERNEL_NSIG 64 +#define SPAWN_TEMPLATE_KERNEL_SIGSET_WORDS \ + (SPAWN_TEMPLATE_KERNEL_NSIG / (8 * sizeof(unsigned long))) + +static const char *true_path; +static char self_path[PATH_MAX]; + +struct spawn_template_kernel_sigset { + unsigned long sig[SPAWN_TEMPLATE_KERNEL_SIGSET_WORDS]; +}; + +static void spawn_template_kernel_sigempty(struct spawn_template_kernel_si= gset *set) +{ + memset(set, 0, sizeof(*set)); +} + +static void spawn_template_kernel_sigadd(struct spawn_template_kernel_sigs= et *set, + int sig) +{ + sig--; + set->sig[sig / (8 * sizeof(unsigned long))] |=3D + 1UL << (sig % (8 * sizeof(unsigned long))); +} + +static int read_fd_string(int fd, const char *expected) +{ + char buf[128]; + ssize_t nread; + + nread =3D read(fd, buf, sizeof(buf) - 1); + if (nread < 0) + return -errno; + + buf[nread] =3D '\0'; + return strcmp(buf, expected) ? -EINVAL : 0; +} + +static int write_file(const char *path, const char *data, mode_t mode) +{ + size_t left =3D strlen(data); + const char *p =3D data; + int fd; + int ret =3D 0; + + fd =3D open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode); + if (fd < 0) + return -errno; + + while (left) { + ssize_t written =3D write(fd, p, left); + + if (written < 0) { + ret =3D -errno; + break; + } + left -=3D written; + p +=3D written; + } + + close(fd); + return ret; +} + +static int create_template_path(const char *path) +{ + struct spawn_template_create_args args =3D { + .flags =3D SPAWN_TEMPLATE_CREATE_CLOEXEC, + .execfd =3D -1, + .filename =3D (uintptr_t)path, + }; + + return syscall(__NR_spawn_template_create, &args, sizeof(args)); +} + +static int create_template_fd(int execfd) +{ + struct spawn_template_create_args args =3D { + .flags =3D SPAWN_TEMPLATE_CREATE_CLOEXEC, + .execfd =3D execfd, + }; + + return syscall(__NR_spawn_template_create, &args, sizeof(args)); +} + +static int spawn_template_start(int template_fd, char *const argv[], + struct spawn_template_action *actions, + unsigned int actions_len, + unsigned long long flags, pid_t *pid_out, + int *pidfd_out) +{ + char *const envp[] =3D { "PATH=3D/usr/bin:/bin", NULL }; + struct spawn_template_spawn_args args =3D { + .flags =3D flags, + .argv =3D (uintptr_t)argv, + .envp =3D (uintptr_t)envp, + .actions =3D (uintptr_t)actions, + .actions_len =3D actions_len, + }; + int pidfd =3D -1; + pid_t pid; + int ret; + + args.pidfd =3D (uintptr_t)&pidfd; + + pid =3D syscall(__NR_spawn_template_spawn, template_fd, &args, + sizeof(args)); + if (pid < 0) { + ret =3D -errno; + if (pidfd >=3D 0) { + siginfo_t info; + + waitid(P_PIDFD, pidfd, &info, WEXITED); + close(pidfd); + } + return ret; + } + + *pid_out =3D pid; + *pidfd_out =3D pidfd; + return 0; +} + +static int spawn_template(int template_fd, char *const argv[], + struct spawn_template_action *actions, + unsigned int actions_len, unsigned long long flags) +{ + siginfo_t info =3D {}; + int pidfd; + pid_t pid; + int ret; + + ret =3D spawn_template_start(template_fd, argv, actions, actions_len, fla= gs, + &pid, &pidfd); + if (ret) + return ret; + (void)pid; + + ret =3D waitid(P_PIDFD, pidfd, &info, WEXITED); + if (ret < 0) { + ret =3D -errno; + goto out_close_pidfd; + } + + if (info.si_code !=3D CLD_EXITED) { + ret =3D -EINVAL; + goto out_close_pidfd; + } + + ret =3D info.si_status; + +out_close_pidfd: + if (pidfd >=3D 0) + close(pidfd); + return ret; +} + +static const char *find_true(void) +{ + static const char * const paths[] =3D { + "/usr/bin/true", + "/bin/true", + }; + unsigned int i; + + for (i =3D 0; i < ARRAY_SIZE(paths); i++) { + if (access(paths[i], X_OK) =3D=3D 0) + return paths[i]; + } + return NULL; +} + +static int copy_file(const char *src, const char *dst) +{ + char buf[8192]; + ssize_t nread; + int infd; + int outfd; + int ret =3D 0; + + infd =3D open(src, O_RDONLY | O_CLOEXEC); + if (infd < 0) + return -errno; + + outfd =3D open(dst, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0700); + if (outfd < 0) { + ret =3D -errno; + goto out_close_in; + } + + while ((nread =3D read(infd, buf, sizeof(buf))) > 0) { + char *p =3D buf; + ssize_t left =3D nread; + + while (left > 0) { + ssize_t written =3D write(outfd, p, left); + + if (written < 0) { + ret =3D -errno; + goto out_close_out; + } + left -=3D written; + p +=3D written; + } + } + if (nread < 0) + ret =3D -errno; + +out_close_out: + close(outfd); +out_close_in: + close(infd); + return ret; +} + +static int test_basic_spawn(void) +{ + char *const argv[] =3D { (char *)true_path, NULL }; + int template_fd; + int ret; + + template_fd =3D create_template_path(true_path); + if (template_fd < 0) + return -errno; + + ret =3D spawn_template(template_fd, argv, NULL, 0, 0); + close(template_fd); + return ret; +} + +static int test_relative_path_rejected(void) +{ + int template_fd; + + template_fd =3D create_template_path("true"); + if (template_fd >=3D 0) { + close(template_fd); + return -EINVAL; + } + + return errno =3D=3D EINVAL ? 0 : -errno; +} + +static int test_execfd_requires_execute(void) +{ + char path[] =3D "/tmp/spawn-template-noexec-XXXXXX"; + int template_fd; + int fd; + int ret =3D 0; + + fd =3D mkstemp(path); + if (fd < 0) + return -errno; + + if (fchmod(fd, 0600)) { + ret =3D -errno; + goto out; + } + + template_fd =3D create_template_fd(fd); + if (template_fd >=3D 0) { + close(template_fd); + ret =3D -EINVAL; + goto out; + } + + ret =3D errno =3D=3D EACCES ? 0 : -errno; + +out: + close(fd); + unlink(path); + return ret; +} + +static int test_default_closes_extra_fds(void) +{ + char fdarg[32]; + char *const argv[] =3D { + self_path, + "--check-fd-closed", + fdarg, + NULL, + }; + int template_fd; + int extra_fd; + int ret; + + extra_fd =3D open("/dev/null", O_RDONLY); + if (extra_fd < 0) + return -errno; + + snprintf(fdarg, sizeof(fdarg), "%d", extra_fd); + + template_fd =3D create_template_path(self_path); + if (template_fd < 0) { + ret =3D -errno; + goto out_close_extra; + } + + ret =3D spawn_template(template_fd, argv, NULL, 0, 0); + close(template_fd); + +out_close_extra: + close(extra_fd); + return ret; +} + +static int test_close_range_max_action(void) +{ + char fdarg[32]; + char *const argv[] =3D { + self_path, + "--check-fd-closed", + fdarg, + NULL, + }; + struct spawn_template_action action =3D { + .type =3D SPAWN_TEMPLATE_ACTION_CLOSE_RANGE, + .fd =3D -1, + .newfd =3D -1, + }; + int template_fd; + int extra_fd; + int ret; + + extra_fd =3D open("/dev/null", O_RDONLY | O_CLOEXEC); + if (extra_fd < 0) + return -errno; + + action.fd =3D extra_fd; + snprintf(fdarg, sizeof(fdarg), "%d", extra_fd); + + template_fd =3D create_template_path(self_path); + if (template_fd < 0) { + ret =3D -errno; + goto out_close_extra; + } + + ret =3D spawn_template(template_fd, argv, &action, 1, + SPAWN_TEMPLATE_SPAWN_INHERIT_FDS); + close(template_fd); + +out_close_extra: + close(extra_fd); + return ret; +} + +static int test_dup2_stdio_actions(void) +{ + char *const argv[] =3D { self_path, "--write-stdio", NULL }; + struct spawn_template_action actions[2]; + char out_buf[32]; + char err_buf[32]; + int out_pipe[2]; + int err_pipe[2]; + int template_fd; + int ret =3D 0; + + if (pipe2(out_pipe, O_CLOEXEC)) + return -errno; + if (pipe2(err_pipe, O_CLOEXEC)) { + ret =3D -errno; + goto out_close_out_pipe; + } + + actions[0] =3D (struct spawn_template_action) { + .type =3D SPAWN_TEMPLATE_ACTION_DUP2, + .fd =3D out_pipe[1], + .newfd =3D STDOUT_FILENO, + }; + actions[1] =3D (struct spawn_template_action) { + .type =3D SPAWN_TEMPLATE_ACTION_DUP2, + .fd =3D err_pipe[1], + .newfd =3D STDERR_FILENO, + }; + + template_fd =3D create_template_path(self_path); + if (template_fd < 0) { + ret =3D -errno; + goto out_close_err_pipe; + } + + ret =3D spawn_template(template_fd, argv, actions, ARRAY_SIZE(actions), 0= ); + close(template_fd); + if (ret) + goto out_close_err_pipe; + + close(out_pipe[1]); + out_pipe[1] =3D -1; + close(err_pipe[1]); + err_pipe[1] =3D -1; + + memset(out_buf, 0, sizeof(out_buf)); + memset(err_buf, 0, sizeof(err_buf)); + if (read(out_pipe[0], out_buf, sizeof(out_buf) - 1) < 0) { + ret =3D -errno; + goto out_close_err_pipe; + } + if (read(err_pipe[0], err_buf, sizeof(err_buf) - 1) < 0) { + ret =3D -errno; + goto out_close_err_pipe; + } + if (strcmp(out_buf, "stdout-token\n") || + strcmp(err_buf, "stderr-token\n")) + ret =3D -EINVAL; + +out_close_err_pipe: + if (err_pipe[1] >=3D 0) + close(err_pipe[1]); + close(err_pipe[0]); +out_close_out_pipe: + if (out_pipe[1] >=3D 0) + close(out_pipe[1]); + close(out_pipe[0]); + return ret; +} + +static int test_open_action_stdin(void) +{ + char dir[] =3D "/tmp/spawn-template-open-XXXXXX"; + char path[PATH_MAX]; + char *const argv[] =3D { + self_path, + "--check-fd-content", + "0", + "open-action-token\n", + NULL, + }; + struct spawn_template_open open_arg =3D { + .path =3D (uintptr_t)path, + .how =3D { + .flags =3D O_RDONLY, + }, + }; + struct spawn_template_action action =3D { + .type =3D SPAWN_TEMPLATE_ACTION_OPEN, + .fd =3D AT_FDCWD, + .newfd =3D STDIN_FILENO, + .arg =3D (uintptr_t)&open_arg, + }; + int template_fd; + int ret; + + if (!mkdtemp(dir)) + return -errno; + + snprintf(path, sizeof(path), "%s/input", dir); + ret =3D write_file(path, "open-action-token\n", 0600); + if (ret) + goto out_unlink; + + template_fd =3D create_template_path(self_path); + if (template_fd < 0) { + ret =3D -errno; + goto out_unlink; + } + + ret =3D spawn_template(template_fd, argv, &action, 1, 0); + close(template_fd); + +out_unlink: + unlink(path); + rmdir(dir); + return ret; +} + +static int test_fchdir_action(void) +{ + char dir[] =3D "/tmp/spawn-template-fchdir-XXXXXX"; + char resolved[PATH_MAX]; + char *const argv[] =3D { + self_path, + "--check-cwd", + resolved, + NULL, + }; + struct spawn_template_action action =3D { + .type =3D SPAWN_TEMPLATE_ACTION_FCHDIR, + }; + int template_fd; + int dirfd; + int ret; + + if (!mkdtemp(dir)) + return -errno; + if (!realpath(dir, resolved)) { + ret =3D -errno; + goto out_rmdir; + } + + dirfd =3D open(dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (dirfd < 0) { + ret =3D -errno; + goto out_rmdir; + } + action.fd =3D dirfd; + + template_fd =3D create_template_path(self_path); + if (template_fd < 0) { + ret =3D -errno; + goto out_close_dirfd; + } + + ret =3D spawn_template(template_fd, argv, &action, 1, 0); + close(template_fd); + +out_close_dirfd: + close(dirfd); +out_rmdir: + rmdir(dir); + return ret; +} + +static int test_sigmask_action(void) +{ + char sigarg[16]; + char *const argv[] =3D { + self_path, + "--check-sigmask", + sigarg, + NULL, + }; + struct spawn_template_kernel_sigset mask; + struct spawn_template_sigset sigset_arg =3D { + .sigset =3D (uintptr_t)&mask, + .sigsetsize =3D sizeof(mask), + }; + struct spawn_template_action action =3D { + .type =3D SPAWN_TEMPLATE_ACTION_SIGMASK, + .arg =3D (uintptr_t)&sigset_arg, + }; + int template_fd; + int ret; + + spawn_template_kernel_sigempty(&mask); + spawn_template_kernel_sigadd(&mask, SIGUSR1); + snprintf(sigarg, sizeof(sigarg), "%d", SIGUSR1); + + template_fd =3D create_template_path(self_path); + if (template_fd < 0) + return -errno; + + ret =3D spawn_template(template_fd, argv, &action, 1, 0); + close(template_fd); + return ret; +} + +static int test_sigdefault_action(void) +{ + char sigarg[16]; + char *const argv[] =3D { + self_path, + "--check-sigdefault", + sigarg, + NULL, + }; + struct spawn_template_kernel_sigset mask; + struct sigaction old_sa; + struct sigaction ignore_sa =3D { + .sa_handler =3D SIG_IGN, + }; + struct spawn_template_sigset sigset_arg =3D { + .sigset =3D (uintptr_t)&mask, + .sigsetsize =3D sizeof(mask), + }; + struct spawn_template_action action =3D { + .type =3D SPAWN_TEMPLATE_ACTION_SIGDEFAULT, + .arg =3D (uintptr_t)&sigset_arg, + }; + int template_fd; + int ret; + + spawn_template_kernel_sigempty(&mask); + spawn_template_kernel_sigadd(&mask, SIGUSR1); + snprintf(sigarg, sizeof(sigarg), "%d", SIGUSR1); + + if (sigaction(SIGUSR1, &ignore_sa, &old_sa)) + return -errno; + + template_fd =3D create_template_path(self_path); + if (template_fd < 0) { + ret =3D -errno; + goto out_restore_signal; + } + + ret =3D spawn_template(template_fd, argv, &action, 1, 0); + close(template_fd); + +out_restore_signal: + sigaction(SIGUSR1, &old_sa, NULL); + return ret; +} + +static int test_inherit_fds_flag(void) +{ + char fdarg[32]; + char *const argv[] =3D { + self_path, + "--check-fd-open", + fdarg, + NULL, + }; + int template_fd; + int extra_fd; + int ret; + + extra_fd =3D open("/dev/null", O_RDONLY); + if (extra_fd < 0) + return -errno; + snprintf(fdarg, sizeof(fdarg), "%d", extra_fd); + + template_fd =3D create_template_path(self_path); + if (template_fd < 0) { + ret =3D -errno; + goto out_close_extra; + } + + ret =3D spawn_template(template_fd, argv, NULL, 0, + SPAWN_TEMPLATE_SPAWN_INHERIT_FDS); + close(template_fd); + +out_close_extra: + close(extra_fd); + return ret; +} + +static int test_pidfd_waitid(void) +{ + char *const argv[] =3D { (char *)true_path, NULL }; + siginfo_t info =3D {}; + int template_fd; + int pidfd; + pid_t pid; + int ret; + + template_fd =3D create_template_path(true_path); + if (template_fd < 0) + return -errno; + + ret =3D spawn_template_start(template_fd, argv, NULL, 0, 0, &pid, &pidfd); + close(template_fd); + if (ret) + return ret; + + ret =3D waitid(P_PIDFD, pidfd, &info, WEXITED); + if (ret < 0) { + ret =3D -errno; + waitpid(pid, NULL, 0); + goto out_close_pidfd; + } + if (info.si_code !=3D CLD_EXITED || info.si_status) + ret =3D -EINVAL; + +out_close_pidfd: + close(pidfd); + return ret; +} + +static int test_create_actions_rejected(void) +{ + struct spawn_template_action action =3D { + .type =3D SPAWN_TEMPLATE_ACTION_CLOSE, + .fd =3D STDIN_FILENO, + }; + struct spawn_template_create_args args =3D { + .flags =3D SPAWN_TEMPLATE_CREATE_CLOEXEC, + .execfd =3D -1, + .filename =3D (uintptr_t)true_path, + .actions =3D (uintptr_t)&action, + .actions_len =3D 1, + }; + int template_fd; + + template_fd =3D syscall(__NR_spawn_template_create, &args, sizeof(args)); + if (template_fd >=3D 0) { + close(template_fd); + return -EINVAL; + } + + return errno =3D=3D EINVAL ? 0 : -errno; +} + +static int test_script_template_unsupported(void) +{ + char dir[] =3D "/tmp/spawn-template-script-XXXXXX"; + char path[PATH_MAX]; + int template_fd; + int ret; + + if (!mkdtemp(dir)) + return -errno; + + snprintf(path, sizeof(path), "%s/script", dir); + ret =3D write_file(path, "#!/bin/sh\nexit 0\n", 0700); + if (ret) + goto out_unlink; + + template_fd =3D create_template_path(path); + if (template_fd >=3D 0) { + close(template_fd); + ret =3D -EINVAL; + goto out_unlink; + } + ret =3D errno =3D=3D ENOEXEC ? 0 : -errno; + +out_unlink: + unlink(path); + rmdir(dir); + return ret; +} + +static int test_deny_write_while_template_alive(void) +{ + char dir[] =3D "/tmp/spawn-template-deny-write-XXXXXX"; + char path[PATH_MAX]; + int template_fd; + int write_fd; + int ret =3D 0; + + if (!mkdtemp(dir)) + return -errno; + + snprintf(path, sizeof(path), "%s/copy", dir); + ret =3D copy_file(self_path, path); + if (ret) + goto out_unlink; + + template_fd =3D create_template_path(path); + if (template_fd < 0) { + ret =3D -errno; + goto out_unlink; + } + + write_fd =3D open(path, O_WRONLY | O_TRUNC | O_CLOEXEC); + if (write_fd >=3D 0) { + close(write_fd); + ret =3D -EINVAL; + } else { + ret =3D errno =3D=3D ETXTBSY ? 0 : -errno; + } + + close(template_fd); +out_unlink: + unlink(path); + rmdir(dir); + return ret; +} + +static int test_stale_path_rejected(void) +{ + char dir[] =3D "/tmp/spawn-template-stale-XXXXXX"; + char path[PATH_MAX]; + char *const argv[] =3D { path, "--exit-zero", NULL }; + int template_fd; + int ret =3D 0; + + if (!mkdtemp(dir)) + return -errno; + + snprintf(path, sizeof(path), "%s/copy", dir); + ret =3D copy_file(self_path, path); + if (ret) + goto out_unlink; + + template_fd =3D create_template_path(path); + if (template_fd < 0) { + ret =3D -errno; + goto out_unlink; + } + + if (chmod(path, 0600)) { + ret =3D -errno; + goto out_close_template; + } + + ret =3D spawn_template(template_fd, argv, NULL, 0, 0); + if (ret >=3D 0) + ret =3D -EINVAL; + else + ret =3D ret =3D=3D -ESTALE ? 0 : ret; + +out_close_template: + close(template_fd); +out_unlink: + unlink(path); + rmdir(dir); + return ret; +} + +static int test_path_replacement_allows_tool_update(void) +{ + char dir[] =3D "/tmp/spawn-template-update-XXXXXX"; + char path[PATH_MAX]; + char new_path[PATH_MAX]; + char *const argv[] =3D { path, "--exit-zero", NULL }; + int new_template_fd =3D -1; + int template_fd =3D -1; + int ret; + + if (!mkdtemp(dir)) + return -errno; + + snprintf(path, sizeof(path), "%s/tool", dir); + snprintf(new_path, sizeof(new_path), "%s/tool.new", dir); + ret =3D copy_file(self_path, path); + if (ret) + goto out; + ret =3D copy_file(self_path, new_path); + if (ret) + goto out; + + template_fd =3D create_template_path(path); + if (template_fd < 0) { + ret =3D -errno; + goto out; + } + + if (rename(new_path, path)) { + ret =3D -errno; + goto out; + } + + ret =3D spawn_template(template_fd, argv, NULL, 0, 0); + if (ret !=3D -ESTALE) { + ret =3D ret < 0 ? ret : -EINVAL; + goto out; + } + + new_template_fd =3D create_template_path(path); + if (new_template_fd < 0) { + ret =3D -errno; + goto out; + } + + ret =3D spawn_template(new_template_fd, argv, NULL, 0, 0); + +out: + if (new_template_fd >=3D 0) + close(new_template_fd); + if (template_fd >=3D 0) + close(template_fd); + unlink(new_path); + unlink(path); + rmdir(dir); + return ret; +} + +static void run_test(const char *name, int (*fn)(void)) +{ + int ret =3D fn(); + + if (!ret) + ksft_test_result_pass("%s\n", name); + else + ksft_test_result_fail("%s failed: %s (%d)\n", + name, strerror(-ret), -ret); +} + +static void check_syscall_available(void) +{ + int template_fd; + + template_fd =3D create_template_path(true_path); + if (template_fd >=3D 0) { + close(template_fd); + return; + } + + if (errno =3D=3D SPAWN_TEMPLATE_MISSING_SYSCALL_ERRNO) + ksft_exit_skip("spawn_template syscalls are not available\n"); + + ksft_exit_fail_msg("spawn_template_create failed: %s (%d)\n", + strerror(errno), errno); +} + +int main(int argc, char **argv) +{ + ssize_t len; + + if (argc =3D=3D 2 && !strcmp(argv[1], "--exit-zero")) + return 0; + + if (argc =3D=3D 3 && !strcmp(argv[1], "--check-fd-closed")) { + int fd =3D atoi(argv[2]); + + return fcntl(fd, F_GETFD) < 0 && errno =3D=3D EBADF ? 0 : 1; + } + + if (argc =3D=3D 3 && !strcmp(argv[1], "--check-fd-open")) { + int fd =3D atoi(argv[2]); + + return fcntl(fd, F_GETFD) >=3D 0 ? 0 : 1; + } + + if (argc =3D=3D 4 && !strcmp(argv[1], "--check-fd-content")) + return read_fd_string(atoi(argv[2]), argv[3]) ? 1 : 0; + + if (argc =3D=3D 3 && !strcmp(argv[1], "--check-cwd")) { + char cwd[PATH_MAX]; + + if (!getcwd(cwd, sizeof(cwd))) + return 1; + return strcmp(cwd, argv[2]) ? 1 : 0; + } + + if (argc =3D=3D 3 && !strcmp(argv[1], "--check-sigmask")) { + sigset_t mask; + int sig =3D atoi(argv[2]); + + if (sigprocmask(SIG_BLOCK, NULL, &mask)) + return 1; + return sigismember(&mask, sig) =3D=3D 1 ? 0 : 1; + } + + if (argc =3D=3D 3 && !strcmp(argv[1], "--check-sigdefault")) { + struct sigaction sa; + int sig =3D atoi(argv[2]); + + if (sigaction(sig, NULL, &sa)) + return 1; + return sa.sa_handler =3D=3D SIG_DFL ? 0 : 1; + } + + if (argc =3D=3D 2 && !strcmp(argv[1], "--write-stdio")) { + if (write(STDOUT_FILENO, "stdout-token\n", 13) !=3D 13) + return 1; + if (write(STDERR_FILENO, "stderr-token\n", 13) !=3D 13) + return 1; + return 0; + } + + true_path =3D find_true(); + if (!true_path) + ksft_exit_skip("could not find true executable\n"); + + len =3D readlink("/proc/self/exe", self_path, sizeof(self_path) - 1); + if (len < 0) + ksft_exit_fail_msg("readlink(/proc/self/exe) failed: %s\n", + strerror(errno)); + self_path[len] =3D '\0'; + + check_syscall_available(); + + ksft_print_header(); + ksft_set_plan(17); + + run_test("basic spawn", test_basic_spawn); + run_test("relative path rejected", test_relative_path_rejected); + run_test("execfd execute permission checked", + test_execfd_requires_execute); + run_test("default fd close", test_default_closes_extra_fds); + run_test("close_range action max fd", test_close_range_max_action); + run_test("dup2 stdio actions", test_dup2_stdio_actions); + run_test("open action stdin", test_open_action_stdin); + run_test("fchdir action", test_fchdir_action); + run_test("sigmask action", test_sigmask_action); + run_test("sigdefault action", test_sigdefault_action); + run_test("inherit fds flag", test_inherit_fds_flag); + run_test("pidfd waitid", test_pidfd_waitid); + run_test("create-time actions rejected", test_create_actions_rejected); + run_test("script template unsupported", test_script_template_unsupported); + run_test("deny write while template alive", + test_deny_write_while_template_alive); + run_test("stale path rejected", test_stale_path_rejected); + run_test("path replacement allows tool update", + test_path_replacement_allows_tool_update); + + ksft_finished(); +} --=20 2.52.0