Re: [RFC PATCH 00/24] pidfd: add a minimal process spawn builder

Justin Suess <[email protected]>
Newsgroups org.kernel.vger.linux-api,org.kernel.vger.audit,org.kernel.vger.linux-arch,org.kernel.vger.linux-doc,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest,org.kernel.vger.linux-security-module,org.kvack.linux-mm
Message-ID <anJE8SfBLtkh6QSa@zenbox>
On Thu, Jul 16, 2026 at 10:31:26PM +0800, Li Chen wrote:
> Hi,
> 
> This RFC follows feedback on my earlier spawn_template RFC [1]. That
> proposal made caching the primary interface; this one starts with general
> process construction. Christian suggested a pidfd/pidfs exec builder
> modeled after fsconfig(), with enough semantics for userspace to implement
> posix_spawn() [2], and Kees agreed [3].
> 
> This RFC is based on linux-next next-20260710 and depends on two pidfs
> fixes that I sent separately:
> 
>   * pidfs: preserve thread pidfds reopened by file handle
>     https://lore.kernel.org/all/[email protected]/
>   * pidfs: handle FS_IOC32_GETVERSION in compat ioctl
>     https://lore.kernel.org/all/[email protected]/
> 
> The initial implementation is source-based. The executable path can be
> provided with the final run request:
> 
>     struct pidfd_spawn_run_args run = {
>         .path = (unsigned long)"/usr/bin/rg",
This should probably be an FD for the path.

This way it prevents race conditions over multiple configuration steps.
>         .argv = (unsigned long)argv,
>         .envp = (unsigned long)envp,
>     };
> 
>     fd = pidfd_open(0, PIDFD_EMPTY);
>     pidfd_spawn_run(fd, &run, sizeof(run));
> 
> Alternatively, the path can be staged before the final run step:
> 
>     struct pidfd_spawn_run_args run = {
>         .argv = (unsigned long)argv,
>         .envp = (unsigned long)envp,
>     };
> 
>     fd = pidfd_open(0, PIDFD_EMPTY);
>     pidfd_config(fd, PIDFD_CONFIG_SET_STRING,
>                  PIDFD_CONFIG_KEY_PATH, "/usr/bin/rg", 0);
Same here. Should probably be an FD to the binary instead.

>     pidfd_spawn_run(fd, &run, sizeof(run));
I'm worried this pidfd_spawn_run just adds another varient to the existing
myriad of exec* syscalls we already have. Would it be better to just have this
work through execveat(fd, "", argv, envp, AT_EMPTY_PATH) instead?

(i.e have execveat take a pidfd directly).

Then you can get rid of pidfd_spawn_run which looks almost structurally
identical to execveat (with the argv and envp collapsed).

Justin
> 
> pidfd_open(0, PIDFD_EMPTY) creates a taskless future pidfd with a stable
> pidfs inode, but no task, PID, or process-count charge. pidfd_spawn_run()
> creates the task and PID; after publication the same fd is the child pidfd,
> and a numeric pidfd resolves to the same inode. Live-task operations return
> -ESRCH before publication. A terminal pre-task failure wakes poll/epoll
> with POLLERR | POLLHUP.
> 
> Source-based mode follows posix_spawn-style defaults. At run time it
> samples the caller's cwd, root, umask, fd table, signal dispositions and
> blocked mask, and namespaces.
> Non-FD_CLOEXEC descriptors remain unless an action changes them; file and
> filesystem state are private child copies before actions. Configuration and
> run stay bound to the creating mm_struct, exact credential object, and
> child PID namespace, so SCM_RIGHTS does not delegate launch authority.
> 
> The first authorized run claims the builder before copying its payload, so
> later failures are terminal. Pre-task failure leaves the fd taskless;
> setup or exec failure leaves it as the child pidfd and exits the child with
> status 127. PIDFD_GET_INFO with PIDFD_INFO_EXIT distinguishes them.
> Success returns the positive child PID in the caller's PID namespace.
> 
> The RFC supports ordered DUP2, CLOSE_RANGE, and FCHDIR actions in
> extensible UAPI records. This exercises child-private fd and cwd setup, but
> is not the complete posix_spawn() action or attribute surface.
> 
> Between task publication and successful exec, the child is explicitly
> embryonic and may not have a valid userspace register frame. Ptrace and
> pidfd_getfd() are denied, procfs treats the PID as absent to other tasks,
> and coredump information reports PIDFD_COREDUMP_SKIP. The child can use its
> own proc entries during executable lookup. Setup runs as initial child task
> work, and successful exec releases this state before exec events are
> published.
> 
> Seccomp sees pidfd_spawn_run(), not separate file-action or exec syscalls,
> and cannot inspect the path or action records behind the run pointer. An
> exec-only denylist that allows unknown syscalls therefore does not block
> this initial exec; policy must filter the builder syscall as a unit. Should
> later expansion provide an immutable restriction profile or action mask
> that seccomp can reason about, or is the coarse syscall boundary
> preferable?
> 
> LSM exec checks and inherited seccomp state remain active. Child setup uses
> a dedicated AUDIT_PIDFD_SPAWN transaction, not a synthetic AUDIT_SYSCALL.
> Should it be selected through the source pidfd_spawn_run() exit rule? An
> existing rule naming only execve() or execveat() does not select it.
> For source/child correlation, could an auxiliary record carry
> the source PID plus the stable pidfs inode? An already traced source is
> rejected before the claim; ptrace auto-attach is not implemented.
> 
> The implementation still uses CLONE_VM | CLONE_VFORK plus exec internally.
> Mateusz suggested that an initial implementation might start with vfork to
> get the API off the ground [4]. That is what this RFC does. It does not yet
> construct a pristine target process without first inheriting source state.
> 
> Missing posix_spawn() pieces include open and close file actions, resetids,
> signal masks/defaults, process groups, sessions, scheduler attributes,
> affinity, cgroup placement, PATH lookup/posix_spawnp(), and exec by fd. The
> RFC also does not include pristine/no-source creation or the executable
> metadata/template cache from my earlier work.
> 
> John Ericson described a real, partially initialized process that remains
> unscheduled while callers install its state, and linked an exploratory
> FreeBSD proc_new()/proc_setfd()/proc_start() refactoring [5]. This RFC
> implements the source-based mode first; a lower-authority pristine/no-source
> mode would be explicit follow-up work.
> 
> Direct process construction is not unprecedented. XNU's posix_spawn path
> does not inherit the parent's address space [6], and Windows
> CreateProcess() accepts explicit startup state [7]. Josh's io_uring_spawn
> LPC slides list "set up process from scratch" as future work and provide
> useful performance context [8]; I am not using those numbers as a claim for
> this RFC.
> 
> If the direction is acceptable, I plan to continue toward:
> 
>   * the complete file-action and attribute set needed by posix_spawn();
>   * pristine target-process construction and an explicit no-source mode;
>   * PATH/posix_spawnp support, if it belongs on the kernel side; and
>   * an optional executable/template layer for workloads such as agent tool
>     calling and compiler drivers.
> 
> The exposed UAPI surface is intentionally limited so the state model and
> kernel/userspace boundary can be reviewed first. If maintainers would
> prefer more posix_spawn semantics or backend work in this RFC, please say
> so and I will adjust the split.
> 
> Codex GPT-5.5 and GPT-5.6-sol provided substantial assistance across design
> conceptualization, implementation, patch splitting, code review, development
> of self-test cases, and test planning and execution.
> 
> Thanks to Christian, Kees, Mateusz, Gabriel, Josh, Andy, John, and others
> for the review and direction.
> 
> [1]: https://patchew.org/linux/20260528095235.2491226-1-me%40linux.beauty/
> [2]: https://lore.kernel.org/all/20260528-madig-fachrichtung-fehlinformation-61117ba640da@brauner/
> [3]: https://lore.kernel.org/all/202606011254.5FCBD65@keescook/
> [4]: https://lore.kernel.org/all/vealb52tv5suireenkke4lul2l3wbnaul2rp3ea545ly5wa5ty@yk3aksvp7skt/
> [5]: https://lore.kernel.org/all/[email protected]/
> [6]: https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/bsd/kern/kern_exec.c#L4039
> [7]: https://learn.microsoft.com/en-us/windows/win32/procthread/creating-processes
> [8]: https://lpc.events/event/16/contributions/1213/attachments/1012/1945/io-uring-spawn.pdf
> 
> 
> Li Chen (24):
>   pidfd: add spawn builder uapi
>   libfs: allow custom validation of stashed inode data
>   pidfs: add taskless future pidfd inodes
>   pidfd: create taskless spawn builders
>   pidfd: add spawn builder path configuration
>   exec: expose execveat internals to process builders
>   fork: expose vfork completion helper
>   pidfs: attach pids to future pidfd files
>   fork: let process builders supply preallocated pids
>   pidfs: publish future pidfd files
>   pidfd: add spawn builder state tracking
>   fork: let kernel callers create embryonic tasks
>   fork: let new tasks start with task work
>   pidfd: create and execute spawn builder tasks
>   fork: keep embryonic tasks hidden until exec completes
>   audit: add pidfd spawn child contexts
>   pidfd: audit child spawn execution
>   pidfd: make spawn builder execution signal-safe
>   file: expose spawn file-action helpers
>   pidfd: add initial spawn file actions
>   pidfd: consume spawn builders on the first run attempt
>   pidfd: expose spawn builder system calls
>   selftests/pidfd: cover pidfd spawn builders
>   Documentation: describe pidfd spawn builders
> 
>  Documentation/userspace-api/index.rst         |    1 +
>  Documentation/userspace-api/pidfd_spawn.rst   |  247 ++++
>  MAINTAINERS                                   |    6 +
>  arch/alpha/kernel/syscalls/syscall.tbl        |    2 +
>  arch/arm/tools/syscall.tbl                    |    2 +
>  arch/arm64/tools/syscall_32.tbl               |    2 +
>  arch/m68k/kernel/syscalls/syscall.tbl         |    2 +
>  arch/microblaze/kernel/syscalls/syscall.tbl   |    2 +
>  arch/mips/kernel/syscalls/syscall_n32.tbl     |    2 +
>  arch/mips/kernel/syscalls/syscall_n64.tbl     |    2 +
>  arch/mips/kernel/syscalls/syscall_o32.tbl     |    2 +
>  arch/parisc/kernel/syscalls/syscall.tbl       |    2 +
>  arch/powerpc/kernel/syscalls/syscall.tbl      |    2 +
>  arch/s390/kernel/syscalls/syscall.tbl         |    2 +
>  arch/sh/kernel/syscalls/syscall.tbl           |    2 +
>  arch/sparc/kernel/syscalls/syscall.tbl        |    2 +
>  arch/x86/entry/syscalls/syscall_32.tbl        |    2 +
>  arch/x86/entry/syscalls/syscall_64.tbl        |    2 +
>  arch/xtensa/kernel/syscalls/syscall.tbl       |    2 +
>  fs/Makefile                                   |    2 +-
>  fs/coredump.c                                 |    4 +-
>  fs/exec.c                                     |   30 +-
>  fs/exec_internal.h                            |   40 +
>  fs/file.c                                     |   11 +-
>  fs/internal.h                                 |    3 +
>  fs/libfs.c                                    |    5 +-
>  fs/open.c                                     |    7 +-
>  fs/pidfd_spawn.c                              | 1095 +++++++++++++++
>  fs/pidfs.c                                    |  478 ++++++-
>  fs/proc/base.c                                |   11 +-
>  fs/proc/internal.h                            |   18 +-
>  include/linux/audit.h                         |   31 +
>  include/linux/pid.h                           |   13 +
>  include/linux/pidfd_spawn.h                   |    9 +
>  include/linux/pidfs.h                         |   28 +
>  include/linux/sched.h                         |   21 +
>  include/linux/sched/task.h                    |    6 +
>  include/linux/syscalls.h                      |    7 +
>  include/uapi/asm-generic/unistd.h             |    8 +-
>  include/uapi/linux/audit.h                    |    1 +
>  include/uapi/linux/pidfd.h                    |    1 +
>  include/uapi/linux/pidfd_spawn.h              |   49 +
>  kernel/audit.h                                |    1 +
>  kernel/auditsc.c                              |  105 +-
>  kernel/fork.c                                 |   26 +-
>  kernel/nsproxy.c                              |   11 +-
>  kernel/pid.c                                  |   41 +-
>  kernel/ptrace.c                               |    4 +
>  kernel/signal.c                               |    2 +-
>  scripts/syscall.tbl                           |    2 +
>  tools/include/uapi/asm-generic/unistd.h       |    8 +-
>  tools/include/uapi/linux/pidfd_spawn.h        |   49 +
>  .../arch/alpha/entry/syscalls/syscall.tbl     |    2 +
>  .../perf/arch/arm/entry/syscalls/syscall.tbl  |    2 +
>  .../arch/arm64/entry/syscalls/syscall_32.tbl  |   11 +
>  .../arch/mips/entry/syscalls/syscall_n64.tbl  |    2 +
>  .../arch/parisc/entry/syscalls/syscall.tbl    |    2 +
>  .../arch/powerpc/entry/syscalls/syscall.tbl   |    2 +
>  .../perf/arch/s390/entry/syscalls/syscall.tbl |    2 +
>  tools/perf/arch/sh/entry/syscalls/syscall.tbl |    2 +
>  .../arch/sparc/entry/syscalls/syscall.tbl     |    2 +
>  .../arch/x86/entry/syscalls/syscall_32.tbl    |    2 +
>  .../arch/x86/entry/syscalls/syscall_64.tbl    |    2 +
>  .../arch/xtensa/entry/syscalls/syscall.tbl    |    2 +
>  tools/scripts/syscall.tbl                     |    2 +
>  tools/testing/selftests/landlock/audit.h      |    6 +-
>  tools/testing/selftests/pidfd/.gitignore      |    9 +
>  tools/testing/selftests/pidfd/Makefile        |   25 +-
>  tools/testing/selftests/pidfd/config          |    6 +
>  .../pidfd/pidfd_spawn_accounting_test.c       |  428 ++++++
>  .../pidfd/pidfd_spawn_actions_test.c          |  474 +++++++
>  .../selftests/pidfd/pidfd_spawn_audit_test.c  |  521 +++++++
>  .../selftests/pidfd/pidfd_spawn_common.c      |  512 +++++++
>  .../selftests/pidfd/pidfd_spawn_common.h      |   59 +
>  .../selftests/pidfd/pidfd_spawn_compat.c      |  221 +++
>  .../selftests/pidfd/pidfd_spawn_exec_test.c   |  301 ++++
>  .../selftests/pidfd/pidfd_spawn_policy_test.c |  294 ++++
>  .../selftests/pidfd/pidfd_spawn_race_test.c   |  923 ++++++++++++
>  .../pidfd/pidfd_spawn_security_test.c         | 1242 +++++++++++++++++
>  .../selftests/pidfd/pidfd_spawn_test.c        |  550 ++++++++
>  80 files changed, 7938 insertions(+), 81 deletions(-)
>  create mode 100644 Documentation/userspace-api/pidfd_spawn.rst
>  create mode 100644 fs/exec_internal.h
>  create mode 100644 fs/pidfd_spawn.c
>  create mode 100644 include/linux/pidfd_spawn.h
>  create mode 100644 include/uapi/linux/pidfd_spawn.h
>  create mode 100644 tools/include/uapi/linux/pidfd_spawn.h
>  create mode 100644 tools/testing/selftests/pidfd/pidfd_spawn_accounting_test.c
>  create mode 100644 tools/testing/selftests/pidfd/pidfd_spawn_actions_test.c
>  create mode 100644 tools/testing/selftests/pidfd/pidfd_spawn_audit_test.c
>  create mode 100644 tools/testing/selftests/pidfd/pidfd_spawn_common.c
>  create mode 100644 tools/testing/selftests/pidfd/pidfd_spawn_common.h
>  create mode 100644 tools/testing/selftests/pidfd/pidfd_spawn_compat.c
>  create mode 100644 tools/testing/selftests/pidfd/pidfd_spawn_exec_test.c
>  create mode 100644 tools/testing/selftests/pidfd/pidfd_spawn_policy_test.c
>  create mode 100644 tools/testing/selftests/pidfd/pidfd_spawn_race_test.c
>  create mode 100644 tools/testing/selftests/pidfd/pidfd_spawn_security_test.c
>  create mode 100644 tools/testing/selftests/pidfd/pidfd_spawn_test.c
> 
> -- 
> 2.52.0
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.