This is not a port of the x86 TLS fix i sent yesterday. That leak was inside
copy_thread() when set_new_tls() failed, so copy_process() skipped
exit_thread(). Here copy_thread() returns 0 and a later
copy_process() step fails. The concrete hole is clone3() with
CLONE_VM|CLONE_PIDFD and an unwritable pidfd pointer: put_user()
returns -EFAULT after the architecture has mapped a shadow stack.
exit_thread() does run, but arm64 and riscv did not implement it, so
that mapping stays in the parent's mm.

Normal exit already frees via deactivate_mm(). The failed-fork path
never reaches that. The free helpers already refuse a foreign mm
and a cleared base, so wiring them to exit_thread() does not unmap
the parent on !CLONE_VM or double-free on a normal exit. A
successful clone3() child with the same flags exits through
deactivate_mm() and then exit_thread(); that is the usual teardown
order this series also enables.

riscv's shstk_release() comment already described this
exit_thread() call. The architecture never selected
HAVE_EXIT_THREAD, so the documented path was not connected.

Do not free on a copy_thread() allocation error: the child still
aliases the parent's base, and copy_process() skips exit_thread()
when copy_thread() fails. kernel_thread() uses CLONE_VM, so a
failed copy_process() still shares the caller's mm and the mm check
in the free helpers does not save us; the kthread path clears
inherited tracking for that reason.

The selftests assume /proc is mounted. A clone3() with a valid
pidfd must succeed and write a pidfd before the rejected calls
count. ENOSYS, EINVAL, and EPERM from that clone skip; any other
error fails the test.

Sixteen clone3() calls with CLONE_VM|CLONE_PIDFD and an unwritable
pidfd pointer leaked 16 shadow-stack VMAs without the fix. With it,
the count stayed unchanged and call/return on the parent still used
the original mapping.

Base: 2f2f7eeef8b0 ("Merge tag 'soc-fixes-7.3-3' of
git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc").

arm64: QEMU 11.1.2 -machine virt -cpu max, CONFIG_ARM64_GCS, one
vCPU. Unpatched ss_vmas=1->17 (+512 kB). Patched ss_vmas=1->1.

riscv: QEMU 11.1.2 -machine virt
-cpu rv64,zimop=on,zcmop=on,zicfiss=on, CONFIG_RISCV_USER_CFI, one
vCPU. Unpatched ss_vmas=1->17 (+1024 kB). Patched ss_vmas=1->1.

Assisted-by: LLM

Iván Ezequiel Rodriguez (4):
  arm64/gcs: release GCS on failed fork
  riscv/shstk: release shadow stack on failed fork
  selftests/arm64: GCS must not leak on a failed clone3
  selftests/riscv: shadow stack must not leak on a failed clone3

 arch/arm64/Kconfig                            |   1 +
 arch/arm64/kernel/process.c                   |  21 ++
 arch/riscv/Kconfig                            |   1 +
 arch/riscv/kernel/process.c                   |  18 +
 tools/testing/selftests/arm64/gcs/Makefile    |   3 +-
 .../selftests/arm64/gcs/gcs-clone-pidfd.c     | 355 ++++++++++++++++++
 tools/testing/selftests/riscv/cfi/Makefile    |   5 +-
 .../selftests/riscv/cfi/shstk-clone-pidfd.c   | 317 ++++++++++++++++
 8 files changed, 719 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/arm64/gcs/gcs-clone-pidfd.c
 create mode 100644 tools/testing/selftests/riscv/cfi/shstk-clone-pidfd.c

-- 
2.43.0

Reply via email to