x86/ptrace: Fix 32-bit PTRACE_SETREGS vs fsbase and gsbase
authorAndy Lutomirski <luto@kernel.org>
Fri, 26 Jun 2020 17:24:29 +0000 (10:24 -0700)
committerThomas Gleixner <tglx@linutronix.de>
Wed, 1 Jul 2020 13:27:20 +0000 (15:27 +0200)
commit40c45904f818c1f6555294ca27afc5fda4f09e68
treead8cc885117f022d4b9a6b4ed8f76f294ce7c266
parent8e259031c67a5ea0666428edb64c89e8c6ebd18e
x86/ptrace: Fix 32-bit PTRACE_SETREGS vs fsbase and gsbase

Debuggers expect that doing PTRACE_GETREGS, then poking at a tracee
and maybe letting it run for a while, then doing PTRACE_SETREGS will
put the tracee back where it was.  In the specific case of a 32-bit
tracer and tracee, the PTRACE_GETREGS/SETREGS data structure doesn't
have fs_base or gs_base fields, so FSBASE and GSBASE fields are
never stored anywhere.  Everything used to still work because
nonzero FS or GS would result full reloads of the segment registers
when the tracee resumes, and the bases associated with FS==0 or
GS==0 are irrelevant to 32-bit code.

Adding FSGSBASE support broke this: when FSGSBASE is enabled, FSBASE
and GSBASE are now restored independently of FS and GS for all tasks
when context-switched in.  This means that, if a 32-bit tracer
restores a previous state using PTRACE_SETREGS but the tracee's
pre-restore and post-restore bases don't match, then the tracee is
resumed with the wrong base.

Fix it by explicitly loading the base when a 32-bit tracer pokes FS
or GS on a 64-bit kernel.

Also add a test case.

Fixes: 673903495c85 ("x86/process/64: Use FSBSBASE in switch_to() if available")
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/229cc6a50ecbb701abd50fe4ddaf0eda888898cd.1593192140.git.luto@kernel.org
arch/x86/include/asm/fsgsbase.h
arch/x86/kernel/process_64.c
arch/x86/kernel/ptrace.c
tools/testing/selftests/x86/Makefile
tools/testing/selftests/x86/fsgsbase_restore.c [new file with mode: 0644]