These syscalls implement a new process creation model that avoids the
fork+exec pattern. I first brought this up a few years ago in
https://lists.freebsd.org/archives/freebsd-arch/2022-January/000140.html
but never got a response. Well, now I am taking matters into my own
hands and just implementing it. :)
(A similar direction is being explored on Linux; compare the proposed
spawn syscall series:
https://lore.kernel.org/all/cover.1784204592.git.me@linux.beauty/; see
where my name is mentioned and "embryonic" is used in particular. The
plan proposed there is to start with that patch series, and then end up
with something like this later.)
pdnew creates an unscheduled process with a loaded executable in a
single operation, returning a process descriptor. pdsetfd installs
file descriptors into the unscheduled process, mirroring pddupfd.
pdstart submits it to the scheduler.
Unlike fork+exec, the new process is never a copy of the parent. It
starts with a fresh fd table, default signal handling, and the
executable already loaded --- there is no intermediate state where the
child runs parent code. This is both simpler and more compatible with
capability-based security (all three syscalls are CAPENABLED).
In order to not have a huge amount of little-tested little-used new
code, effort has been taken to deduplicate this new code with the
traditional fork+exec. But because the division of labor is so
different between the old and new models, this ends up taking quite a
lot of refactoring.
The preceding commit factored the shared logic out of fork1/do_fork
and do_execve into a large number of small helper functions; this
commit generalizes those helpers so each has one old-style caller and
one new-style (pdnew) caller. Because the division of labor is so
different between the old and new models, the new caller drives those
helpers with a *different* process and thread than
curproc/curthread:
- The fork helpers (fork_alloc_proc, fork_register_proc, fork_proc_tree) allocate and register an embryonic process on behalf of a parent that is the caller, not the new process itself.
- The exec helpers activate an image into that embryonic process's address space. Because it is not curproc, string and auxv setup can no longer use plain copyout; the new imgp_copyout, imgp_suword, and imgp_suword32 helpers write into the target vmspace via vmspace_iop() (see the preceding privilege-lifting commit) with a real, process-visible VM_PROT_WRITE.
The embryonic process's p_vmspace starts as NULL. exec_new_vmspace
(called by the image activator) has been modified to handle this,
skipping straight to vmspace_exec() to create the correct vmspace
for the executable's ABI. This avoids allocating a throwaway vmspace.
The embryonic process reuses P_INEXEC (cleared by pdstart) to
protect against ptrace, signal delivery, and other interference while
the process is being set up. Closing the process descriptor before
pdstart destroys the embryonic process via
proc_destroy_embryonic.
Interpreter (shebang) scripts are supported.
The syscalls are documented in pdfork.2 alongside the other process
descriptor syscalls.
Note: This patch series overall is very big, more than I feel competent
reviewing, so I feel a bit ill-mannered submitting it yet. Certainly,
everyone should feel free to ignore it until my "unix socket connectat"
series is landed. I am submitting it now anyway simply because I think
the end functionality is very cool, and I don't want it to grow dusty on
my machine.
Assisted-by: Claude Code (Claude Opus 4.8/5 and Fable 5)