Linux Security Modules development
 help / color / mirror / Atom feed
* [RFC PATCH 00/24] pidfd: add a minimal process spawn builder
@ 2026-07-16 14:31 Li Chen
  2026-07-16 14:31 ` [RFC PATCH 01/24] pidfd: add spawn builder uapi Li Chen
                   ` (23 more replies)
  0 siblings, 24 replies; 26+ messages in thread
From: Li Chen @ 2026-07-16 14:31 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Kees Cook, Gabriel Krisman Bertazi, Josh Triplett, Mateusz Guzik,
	Andy Lutomirski, John Ericson, Jonathan Corbet, Shuah Khan,
	Arnd Bergmann, Oleg Nesterov, Andrew Morton, Paul Moore,
	Eric Paris, Mickaël Salaün, Günther Noack,
	Alexander Viro, Jan Kara, linux-api, linux-fsdevel, linux-kernel,
	linux-kselftest, linux-doc, audit, linux-security-module,
	linux-arch, linux-mm

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/20260716052726.1032092-1-me@linux.beauty/
  * pidfs: handle FS_IOC32_GETVERSION in compat ioctl
    https://lore.kernel.org/all/20260716052822.1034228-1-me@linux.beauty/

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",
        .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);
    pidfd_spawn_run(fd, &run, sizeof(run));

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/ce71d6df-6851-4e4b-9603-1d55d8d522b8@app.fastmail.com/
[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


^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2026-07-16 15:58 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-16 14:31 [RFC PATCH 00/24] pidfd: add a minimal process spawn builder Li Chen
2026-07-16 14:31 ` [RFC PATCH 01/24] pidfd: add spawn builder uapi Li Chen
2026-07-16 14:31 ` [RFC PATCH 02/24] libfs: allow custom validation of stashed inode data Li Chen
2026-07-16 14:31 ` [RFC PATCH 03/24] pidfs: add taskless future pidfd inodes Li Chen
2026-07-16 14:31 ` [RFC PATCH 04/24] pidfd: create taskless spawn builders Li Chen
2026-07-16 14:31 ` [RFC PATCH 05/24] pidfd: add spawn builder path configuration Li Chen
2026-07-16 14:31 ` [RFC PATCH 06/24] exec: expose execveat internals to process builders Li Chen
2026-07-16 14:31 ` [RFC PATCH 07/24] fork: expose vfork completion helper Li Chen
2026-07-16 14:31 ` [RFC PATCH 08/24] pidfs: attach pids to future pidfd files Li Chen
2026-07-16 14:31 ` [RFC PATCH 09/24] fork: let process builders supply preallocated pids Li Chen
2026-07-16 14:31 ` [RFC PATCH 10/24] pidfs: publish future pidfd files Li Chen
2026-07-16 14:31 ` [RFC PATCH 11/24] pidfd: add spawn builder state tracking Li Chen
2026-07-16 14:31 ` [RFC PATCH 12/24] fork: let kernel callers create embryonic tasks Li Chen
2026-07-16 15:57   ` Andy Lutomirski
2026-07-16 14:31 ` [RFC PATCH 13/24] fork: let new tasks start with task work Li Chen
2026-07-16 14:31 ` [RFC PATCH 14/24] pidfd: create and execute spawn builder tasks Li Chen
2026-07-16 14:31 ` [RFC PATCH 15/24] fork: keep embryonic tasks hidden until exec completes Li Chen
2026-07-16 14:31 ` [RFC PATCH 16/24] audit: add pidfd spawn child contexts Li Chen
2026-07-16 14:31 ` [RFC PATCH 17/24] pidfd: audit child spawn execution Li Chen
2026-07-16 14:31 ` [RFC PATCH 18/24] pidfd: make spawn builder execution signal-safe Li Chen
2026-07-16 14:31 ` [RFC PATCH 19/24] file: expose spawn file-action helpers Li Chen
2026-07-16 14:31 ` [RFC PATCH 20/24] pidfd: add initial spawn file actions Li Chen
2026-07-16 14:31 ` [RFC PATCH 21/24] pidfd: consume spawn builders on the first run attempt Li Chen
2026-07-16 14:31 ` [RFC PATCH 22/24] pidfd: expose spawn builder system calls Li Chen
2026-07-16 14:31 ` [RFC PATCH 23/24] selftests/pidfd: cover pidfd spawn builders Li Chen
2026-07-16 14:31 ` [RFC PATCH 24/24] Documentation: describe " Li Chen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox