CVSROOT: /cvs
Module name: src
Changes by: [email protected] 2026/08/30 13:10:17
Modified files:
sys/kern : exec_script.c kern_exec.c
sys/sys : exec.h
Log message:
sys: avoid script pathname TOCTOU in exec
A readable script pathname is read twice during exec: namei() copies the
user string for vnode lookup, while exec_script_makecmds() later copies
the same user address into the synthetic interpreter argument list. The
preceding single_thread_set() excludes sibling threads from this window;
a separate process sharing writable MAP_SHARED memory remains able to
modify the pathname between reads.
The failing sequence is:
1. Setup: a process stores pathname A in writable MAP_SHARED memory and
calls fork(); the parent and child retain mappings of the same VM
object.
2. Check: the parent calls execve(2); namei() copies pathname A and
resolves vnode A, after which check_exec() verifies execution access
and reads the script header.
3. Mutation: after namei() copies A but before the script handler
rereads the user address, the child stores pathname B through its
shared mapping.
4. Use: exec_script_makecmds() calls copyinstr() on the original user
address, obtains pathname B, and places it in the synthetic argument
list. This second copy creates the mismatch: the kernel retains vnode
A while the interpreter argument names B.
5. Effect: the interpreter opens pathname B; the kernel validated vnode
A, but the interpreter reads and executes contents selected by B.
After single_thread_set() stops sibling threads, copy the pathname once
into a bounded kernel buffer; use that snapshot for namei() and the
synthetic script argument, then release it when check_exec() returns.
This retains EFAULT and ENAMETOOLONG results while removing the second
userspace access.
OK: deraadt@, kettenis@