Skip to contentSkip to conversation

um: switch to regset API and depend on XSTATE

PATCH In review Benjamin Berg 3 participants 11 messages Oct 23, 2024 – Dec 16, 2024

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH v5 1/1 um: switch to regset API and depend on XSTATE

From: Benjamin Berg <[email protected]>

The PTRACE_GETREGSET API has now existed since Linux 2.6.33. The XSAVE CPU feature should also be sufficiently common to be able to rely on it.

With this, define our internal FP state to be the hosts XSAVE data. Add discovery for the hosts XSAVE size and place the FP registers at the end of task_struct so that we can adjust the size at runtime.

Next we can implement the regset API on top and update the signal handling as well as ptrace APIs to use them. Also switch coredump creation to use the regset API and finally set HAVE_ARCH_TRACEHOOK.

This considerably improves the signal frames. Previously they might not have contained all the registers (i386) and also did not have the sizes and magic values set to the correct values to permit userspace to decode the frame.

As a side effect, this will permit UML to run on hosts with newer CPU extensions (such as AMX) that need even more register state.

---

v5:
* actually add the v4 fix

v4:
* fix FP register initialization when duplicating tasks

v3:
* Fix FP register initialization, thanks to Johannes Berg

v2:
* Fix unmap size of detection code, thanks to Johannes Berg
* Small formatting changes

v1:
* Use a fixed mmap'ed buffer for size discovery
* Add forgotten enum definition for signal.c
* Fix FPX register size using sizeof()
* Also set magic1 and xstate_size to the expected values

---
 arch/um/Kconfig                         |   2 +
 arch/um/include/asm/processor-generic.h |   4 +-
 arch/um/kernel/process.c                |  22 +-
 arch/um/kernel/um_arch.c                |   2 +
 arch/um/os-Linux/registers.c            |  11 +-
 arch/x86/um/Makefile                    |   2 +-
 arch/x86/um/asm/elf.h                   |   2 +
 arch/x86/um/asm/ptrace.h                |  10 +
 arch/x86/um/os-Linux/registers.c        | 145 +++--------
 arch/x86/um/ptrace.c                    | 267 ++++++++++++++++++++
 arch/x86/um/ptrace_32.c                 |  88 ++-----
 arch/x86/um/ptrace_64.c                 |  37 +--
 arch/x86/um/shared/sysdep/ptrace.h      |   8 +-
 arch/x86/um/shared/sysdep/ptrace_32.h   |   4 -
 arch/x86/um/shared/sysdep/ptrace_64.h   |   4 -
 arch/x86/um/shared/sysdep/ptrace_user.h |   6 -
 arch/x86/um/signal.c                    | 322 ++++++++----------------
 arch/x86/um/user-offsets.c              |   8 -
 18 files changed, 471 insertions(+), 473 deletions(-)
 create mode 100644 arch/x86/um/ptrace.c
arch/um/Kconfig +2 -0
index 5dc702ad9e7a..a9876bdb5bf9 100644
@@ -5,6 +5,7 @@ menu "UML-specific options"
 config UML
 	bool
 	default y
+	select ARCH_WANTS_DYNAMIC_TASK_STRUCT
 	select ARCH_HAS_CPU_FINALIZE_INIT
 	select ARCH_HAS_FORTIFY_SOURCE
 	select ARCH_HAS_GCOV_PROFILE_ALL
@@ -32,6 +33,7 @@ config UML
 	select HAVE_ARCH_VMAP_STACK
 	select HAVE_RUST
 	select ARCH_HAS_UBSAN
+	select HAVE_ARCH_TRACEHOOK
 
 config MMU
 	bool
arch/um/include/asm/processor-generic.h +3 -1
index 02e759a4a435..5d6356eafffe 100644
@@ -20,7 +20,6 @@ struct task_struct;
 struct mm_struct;
 
 struct thread_struct {
-	struct pt_regs regs;
 	struct pt_regs *segv_regs;
 	struct task_struct *prev_sched;
 	struct arch_thread arch;
@@ -31,6 +30,9 @@ struct thread_struct {
 			void *arg;
 		} thread;
 	} request;
+
+	/* Contains variable sized FP registers */
+	struct pt_regs regs;
 };
 
 #define INIT_THREAD \
arch/um/kernel/process.c +7 -15
index d45c79f82d7c..56e7e525fc91 100644
@@ -187,6 +187,13 @@ void initial_thread_cb(void (*proc)(void *), void *arg)
 	kmalloc_ok = save_kmalloc_ok;
 }
 
+int arch_dup_task_struct(struct task_struct *dst,
+			 struct task_struct *src)
+{
+	memcpy(dst, src, arch_task_struct_size);
+	return 0;
+}
+
 void um_idle_sleep(void)
 {
 	if (time_travel_mode != TT_MODE_OFF)
@@ -287,18 +294,3 @@ unsigned long __get_wchan(struct task_struct *p)
 
 	return 0;
 }
-
-int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu)
-{
-#ifdef CONFIG_X86_32
-	extern int have_fpx_regs;
-
-	/* FIXME: A plain copy does not work on i386 with have_fpx_regs */
-	if (have_fpx_regs)
-		return 0;
-#endif
-	memcpy(fpu, &t->thread.regs.regs.fp, sizeof(*fpu));
-
-	return 1;
-}
-
arch/um/kernel/um_arch.c +2 -0
index 6d755a37d5c4..ec17576ce9fc 100644
@@ -402,6 +402,8 @@ int __init linux_main(int argc, char **argv, char **envp)
 		os_info("Kernel virtual memory size shrunk to %lu bytes\n",
 			virtmem_size);
 
+	arch_task_struct_size = sizeof(struct task_struct) + host_fp_size;
+
 	os_flush_stdout();
 
 	return start_uml();
arch/um/os-Linux/registers.c +8 -3
index bd80b921add0..d7ca148807b2 100644
@@ -10,11 +10,12 @@
 #include <sysdep/ptrace.h>
 #include <sysdep/ptrace_user.h>
 #include <registers.h>
+#include <stdlib.h>
 
 /* This is set once at boot time and not changed thereafter */
 
 static unsigned long exec_regs[MAX_REG_NR];
-static unsigned long exec_fp_regs[FP_SIZE];
+static unsigned long *exec_fp_regs;
 
 int init_pid_registers(int pid)
 {
@@ -24,7 +25,11 @@ int init_pid_registers(int pid)
 	if (err < 0)
 		return -errno;
 
-	arch_init_registers(pid);
+	err = arch_init_registers(pid);
+	if (err < 0)
+		return err;
+
+	exec_fp_regs = malloc(host_fp_size);
 	get_fp_registers(pid, exec_fp_regs);
 	return 0;
 }
@@ -34,5 +39,5 @@ void get_safe_registers(unsigned long *regs, unsigned long *fp_regs)
 	memcpy(regs, exec_regs, sizeof(exec_regs));
 
 	if (fp_regs)
-		memcpy(fp_regs, exec_fp_regs, sizeof(exec_fp_regs));
+		memcpy(fp_regs, exec_fp_regs, host_fp_size);
 }
arch/x86/um/Makefile +1 -1
index 36e67fc97c22..b42c31cd2390 100644
@@ -10,7 +10,7 @@ else
 endif
 
 obj-y = bugs_$(BITS).o delay.o fault.o \
-	ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \
+	ptrace.o ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \
 	stub_segv.o \
 	sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \
 	mem_$(BITS).o subarch.o os-Linux/
arch/x86/um/asm/elf.h +2 -0
index 6052200fe925..62ed5d68a978 100644
@@ -8,6 +8,8 @@
 #include <asm/user.h>
 #include <skas.h>
 
+#define CORE_DUMP_USE_REGSET
+
 #ifdef CONFIG_X86_32
 
 #define R_386_NONE	0
arch/x86/um/asm/ptrace.h +10 -0
index 2fef3da55533..2641d28d115c 100644
@@ -2,6 +2,16 @@
 #ifndef __UM_X86_PTRACE_H
 #define __UM_X86_PTRACE_H
 
+/* This is here because signal.c needs the REGSET_FP_LEGACY definition */
+enum {
+	REGSET_GENERAL,
+#ifdef CONFIG_X86_32
+	REGSET_FP_LEGACY,
+#endif
+	REGSET_FP,
+	REGSET_XSTATE,
+};
+
 #include <linux/compiler.h>
 #ifndef CONFIG_X86_32
 #define __FRAME_OFFSETS /* Needed to get the R* macros */
arch/x86/um/os-Linux/registers.c +35 -110

168 lines not on this page · raw message

arch/x86/um/ptrace.c new file +267 -0

267 lines not on this page · raw message

arch/x86/um/ptrace_32.c +17 -71

109 lines not on this page · raw message

arch/x86/um/ptrace_64.c +9 -28
index f8bbad29cd0b..e0d4120a45c8 100644
@@ -8,6 +8,7 @@
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
+#include <linux/regset.h>
 #define __FRAME_OFFSETS
 #include <asm/ptrace.h>
 #include <linux/uaccess.h>
@@ -188,30 +189,6 @@ int peek_user(struct task_struct *child, long addr, long data)
 	return put_user(tmp, (unsigned long *) data);
 }
 
-static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child)
-{
-	int n;
-
-	n = copy_to_user(buf, &child->thread.regs.regs.fp,
-			 sizeof(struct user_i387_struct));
-	if (n > 0)
-		return -EFAULT;
-
-	return n;
-}
-
-static int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child)
-{
-	int n;
-
-	n = copy_from_user(&child->thread.regs.regs.fp, buf,
-			   sizeof(struct user_i387_struct));
-	if (n > 0)
-		return -EFAULT;
-
-	return 0;
-}
-
 long subarch_ptrace(struct task_struct *child, long request,
 		    unsigned long addr, unsigned long data)
 {
@@ -220,11 +197,15 @@ long subarch_ptrace(struct task_struct *child, long request,
 
 	switch (request) {
 	case PTRACE_GETFPREGS: /* Get the child FPU state. */
-		ret = get_fpregs(datap, child);
-		break;
+		return copy_regset_to_user(child, task_user_regset_view(child),
+					   REGSET_FP,
+					   0, sizeof(struct user_i387_struct),
+					   datap);
 	case PTRACE_SETFPREGS: /* Set the child FPU state. */
-		ret = set_fpregs(datap, child);
-		break;
+		return copy_regset_from_user(child, task_user_regset_view(child),
+					     REGSET_FP,
+					     0, sizeof(struct user_i387_struct),
+					     datap);
 	case PTRACE_ARCH_PRCTL:
 		/* XXX Calls ptrace on the host - needs some SMP thinking */
 		ret = arch_prctl(child, data, (void __user *) addr);
arch/x86/um/shared/sysdep/ptrace.h +7 -1
index 6ca4ecabc55b..2dd4ca6713f8 100644
@@ -56,12 +56,16 @@ struct syscall_args {
 				     UPT_SYSCALL_ARG5(r),	 \
 				     UPT_SYSCALL_ARG6(r) } } )
 
+extern unsigned long host_fp_size;
+
 struct uml_pt_regs {
 	unsigned long gp[MAX_REG_NR];
-	unsigned long fp[MAX_FP_NR];
 	struct faultinfo faultinfo;
 	long syscall;
 	int is_user;
+
+	/* Dynamically sized FP registers (holds an XSTATE) */
+	unsigned long fp[];
 };
 
 #define EMPTY_UML_PT_REGS { }
@@ -72,4 +76,6 @@ struct uml_pt_regs {
 
 extern int user_context(unsigned long sp);
 
+extern int arch_init_registers(int pid);
+
 #endif /* __SYSDEP_X86_PTRACE_H */
arch/x86/um/shared/sysdep/ptrace_32.h +0 -4
index 0c4989842fbe..2392470cac4d 100644
@@ -6,8 +6,6 @@
 #ifndef __SYSDEP_I386_PTRACE_H
 #define __SYSDEP_I386_PTRACE_H
 
-#define MAX_FP_NR HOST_FPX_SIZE
-
 #define UPT_SYSCALL_ARG1(r) UPT_BX(r)
 #define UPT_SYSCALL_ARG2(r) UPT_CX(r)
 #define UPT_SYSCALL_ARG3(r) UPT_DX(r)
@@ -15,6 +13,4 @@
 #define UPT_SYSCALL_ARG5(r) UPT_DI(r)
 #define UPT_SYSCALL_ARG6(r) UPT_BP(r)
 
-extern void arch_init_registers(int pid);
-
 #endif
arch/x86/um/shared/sysdep/ptrace_64.h +0 -4
index 0dc223aa1c2d..e73573ac871f 100644
@@ -8,8 +8,6 @@
 #ifndef __SYSDEP_X86_64_PTRACE_H
 #define __SYSDEP_X86_64_PTRACE_H
 
-#define MAX_FP_NR HOST_FP_SIZE
-
 #define REGS_R8(r) ((r)[HOST_R8])
 #define REGS_R9(r) ((r)[HOST_R9])
 #define REGS_R10(r) ((r)[HOST_R10])
@@ -57,6 +55,4 @@
 #define UPT_SYSCALL_ARG5(r) UPT_R8(r)
 #define UPT_SYSCALL_ARG6(r) UPT_R9(r)
 
-extern void arch_init_registers(int pid);
-
 #endif
arch/x86/um/shared/sysdep/ptrace_user.h +0 -6
index 1d1a824fa652..98da23120538 100644
@@ -11,12 +11,6 @@
 #define REGS_IP_INDEX HOST_IP
 #define REGS_SP_INDEX HOST_SP
 
-#ifdef __i386__
-#define FP_SIZE ((HOST_FPX_SIZE > HOST_FP_SIZE) ? HOST_FPX_SIZE : HOST_FP_SIZE)
-#else
-#define FP_SIZE HOST_FP_SIZE
-#endif
-
 /*
  * glibc before 2.27 does not include PTRACE_SYSEMU_SINGLESTEP in its enum,
  * ensure we have a definition by (re-)defining it here.
arch/x86/um/signal.c +101 -221

429 lines not on this page · raw message

arch/x86/um/user-offsets.c +0 -8
index 1c77d9946199..d6e1cd9956bf 100644
@@ -20,9 +20,6 @@ void foo(void);
 void foo(void)
 {
 #ifdef __i386__
-	DEFINE_LONGS(HOST_FP_SIZE, sizeof(struct user_fpregs_struct));
-	DEFINE_LONGS(HOST_FPX_SIZE, sizeof(struct user_fpxregs_struct));
-
 	DEFINE(HOST_IP, EIP);
 	DEFINE(HOST_SP, UESP);
 	DEFINE(HOST_EFLAGS, EFL);
@@ -41,11 +38,6 @@ void foo(void)
 	DEFINE(HOST_GS, GS);
 	DEFINE(HOST_ORIG_AX, ORIG_EAX);
 #else
-#ifdef FP_XSTATE_MAGIC1
-	DEFINE_LONGS(HOST_FP_SIZE, 2696);
-#else
-	DEFINE(HOST_FP_SIZE, sizeof(struct _fpstate) / sizeof(unsigned long));
-#endif
 	DEFINE_LONGS(HOST_BX, RBX);
 	DEFINE_LONGS(HOST_CX, RCX);
 	DEFINE_LONGS(HOST_DI, RDI);
Signature · 1 line
2.47.0
#1 lore raw

Hello,

Quoted 13 lines · On Wed, 23 Oct 2024 11:41:20 +0200 Benjamin Berg <[email protected]>…

From: Benjamin Berg <[email protected]>

The PTRACE_GETREGSET API has now existed since Linux 2.6.33. The XSAVE CPU feature should also be sufficiently common to be able to rely on it.

With this, define our internal FP state to be the hosts XSAVE data. Add discovery for the hosts XSAVE size and place the FP registers at the end of task_struct so that we can adjust the size at runtime.

Next we can implement the regset API on top and update the signal handling as well as ptrace APIs to use them. Also switch coredump creation to use the regset API and finally set HAVE_ARCH_TRACEHOOK.

Quoted 7 lines

This considerably improves the signal frames. Previously they might not have contained all the registers (i386) and also did not have the sizes and magic values set to the correct values to permit userspace to decode the frame.

As a side effect, this will permit UML to run on hosts with newer CPU extensions (such as AMX) that need even more register state.

I just found kunit starts failing from the mainline commit of this patch on my qemu-x86 Debian system, as below.

    $ git checkout 3f17fed2149192c7d3b76a45a6a87b4ff22cd586
    $ ./tools/testing/kunit/kunit.py run --kunitconfig mm/damon/tests/ --build_dir ../kunit.out/
    [...]
    [22:48:27] Configuring KUnit Kernel ...
    Regenerating .config ...
    Populating config with:
    $ make ARCH=um O=../kunit.out/ olddefconfig
    [22:48:30] Building KUnit Kernel ...
    Populating config with:
    $ make ARCH=um O=../kunit.out/ olddefconfig
    Building with:
    $ make all compile_commands.json ARCH=um O=../kunit.out/ --jobs=40
    [...]
    [22:48:46] Starting KUnit Kernel (1/1)...
    [22:48:46] ============================================================
    Running tests with:
    $ ../kunit.out/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
    [22:48:46] [ERROR] Test: <missing>: Could not find any KTAP output. Did any KUnit tests run?
    [22:48:46] ============================================================
    [22:48:46] Testing complete. Ran 0 tests: errors: 1
    [22:48:46] Elapsed time: 19.285s total, 3.805s configuring, 15.475s building, 0.006s running
Quoted 2 lines

[...]

Quoted 32 lines
arch/x86/um/os-Linux/registers.c +12 -19
-void arch_init_registers(int pid)
-{
-	struct user_fpxregs_struct fpx_regs;
-	int err;
-
-	err = ptrace(PTRACE_GETFPXREGS, pid, 0, &fpx_regs);
-	if (!err)
-		return;
-
-	if (errno != EIO)
-		panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d",
-		      errno);
-
-	have_fpx_regs = 0;
-}
-#else
-
-int get_fp_registers(int pid, unsigned long *regs)
+int arch_init_registers(int pid)
 {
-	return save_fp_registers(pid, regs);
+	struct iovec iov = {
+		/* Just use plenty of space, it does not cost us anything */
+		.iov_len = 2 * 1024 * 1024,
+	};
+	int ret;
+
+	iov.iov_base = mmap(NULL, iov.iov_len, PROT_WRITE | PROT_READ,
+			    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+	if (iov.iov_base == MAP_FAILED)
+		return -ENOMEM;
+

arch/x86/um/os-Linux/registers.c line 69

Quoted 10 lines
arch/x86/um/os-Linux/registers.c +9 -0
+	/* GDB has x86_xsave_length, which uses x86_cpuid_count */
+	ret = ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov);
+	if (ret)
+		ret = -errno;
+	munmap(iov.iov_base, 2 * 1024 * 1024);
+
+	host_fp_size = iov.iov_len;
+
+	return ret;
 }

And seems it fails from the registers initialization step:

    $ ../kunit.out/linux
    Core dump limits :
            soft - 0
            hard - NONE
    Checking that ptrace can change system call numbers...OK
    Checking syscall emulation for ptrace...OK
    Checking environment variables for a tempdir...none found
    Checking if /dev/shm is on tmpfs...OK
    Checking PROT_EXEC mmap in /dev/shm...OK
    Failed to initialize default registers

I'm not familiar with uml code, so reporting this issue first. Any thought, please?

Thanks,
SJ

[...]

#2 lore raw

Hi,

that probably means the size detection for the FPU state (i.e. PTRACE_GETREGSET for NT_X86_XSTATE is incorrect on a 32bit host in some way.

Is there anything special about the qemu setup or it is just a default qemu-x86?

Benjamin

Quoted 118 lines · SeongJae Park

Hello,

Quoted 13 lines · On Wed, 23 Oct 2024 11:41:20 +0200 Benjamin Berg <[email protected]>…

From: Benjamin Berg <[email protected]>

The PTRACE_GETREGSET API has now existed since Linux 2.6.33. The XSAVE CPU feature should also be sufficiently common to be able to rely on it.

With this, define our internal FP state to be the hosts XSAVE data. Add discovery for the hosts XSAVE size and place the FP registers at the end of task_struct so that we can adjust the size at runtime.

Next we can implement the regset API on top and update the signal handling as well as ptrace APIs to use them. Also switch coredump creation to use the regset API and finally set HAVE_ARCH_TRACEHOOK.

Quoted 7 lines

This considerably improves the signal frames. Previously they might not have contained all the registers (i386) and also did not have the sizes and magic values set to the correct values to permit userspace to decode the frame.

As a side effect, this will permit UML to run on hosts with newer CPU extensions (such as AMX) that need even more register state.

I just found kunit starts failing from the mainline commit of this patch on my qemu-x86 Debian system, as below.

    $ git checkout 3f17fed2149192c7d3b76a45a6a87b4ff22cd586
    $ ./tools/testing/kunit/kunit.py run --kunitconfig mm/damon/tests/ --build_dir ../kunit.out/
    [...]
    [22:48:27] Configuring KUnit Kernel ...
    Regenerating .config ...
    Populating config with:
    $ make ARCH=um O=../kunit.out/ olddefconfig
    [22:48:30] Building KUnit Kernel ...
    Populating config with:
    $ make ARCH=um O=../kunit.out/ olddefconfig
    Building with:
    $ make all compile_commands.json ARCH=um O=../kunit.out/ --jobs=40
    [...]
    [22:48:46] Starting KUnit Kernel (1/1)...
    [22:48:46] ============================================================
    Running tests with:
    $ ../kunit.out/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
    [22:48:46] [ERROR] Test: <missing>: Could not find any KTAP output. Did any KUnit tests run?
    [22:48:46] ============================================================
    [22:48:46] Testing complete. Ran 0 tests: errors: 1
    [22:48:46] Elapsed time: 19.285s total, 3.805s configuring, 15.475s building, 0.006s running
Quoted 2 lines

[...]

Quoted 41 lines
arch/x86/um/os-Linux/registers.c +1 -18
-void arch_init_registers(int pid)
-{
-	struct user_fpxregs_struct fpx_regs;
-	int err;
-
-	err = ptrace(PTRACE_GETFPXREGS, pid, 0, &fpx_regs);
-	if (!err)
-		return;
-
-	if (errno != EIO)
-		panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d",
-		      errno);
-
-	have_fpx_regs = 0;
-}
-#else
-
-int get_fp_registers(int pid, unsigned long *regs)
+int arch_init_registers(int pid)

 {

arch/x86/um/os-Linux/registers.c +20 -1
-	return save_fp_registers(pid, regs);
+	struct iovec iov = {
+		/* Just use plenty of space, it does not cost us anything */
+		.iov_len = 2 * 1024 * 1024,
+	};
+	int ret;
+
+	iov.iov_base = mmap(NULL, iov.iov_len, PROT_WRITE | PROT_READ,
+			    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+	if (iov.iov_base == MAP_FAILED)
+		return -ENOMEM;
+
+	/* GDB has x86_xsave_length, which uses x86_cpuid_count */
+	ret = ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov);
+	if (ret)
+		ret = -errno;
+	munmap(iov.iov_base, 2 * 1024 * 1024);
+
+	host_fp_size = iov.iov_len;
+
+	return ret;
Quoted 1 line

 }

And seems it fails from the registers initialization step:

    $ ../kunit.out/linux
    Core dump limits :
            soft - 0
            hard - NONE
    Checking that ptrace can change system call numbers...OK
    Checking syscall emulation for ptrace...OK
    Checking environment variables for a tempdir...none found
    Checking if /dev/shm is on tmpfs...OK
    Checking PROT_EXEC mmap in /dev/shm...OK
    Failed to initialize default registers

I'm not familiar with uml code, so reporting this issue first.  Any thought, please?

Thanks,
SJ

[...]

#3 lore raw
Quoted 8 lines · On Tue, 03 Dec 2024 09:40:34 +0100 Benjamin Berg <[email protected]>…

Hi,

that probably means the size detection for the FPU state (i.e. PTRACE_GETREGSET for NT_X86_XSTATE is incorrect on a 32bit host in some way.

Is there anything special about the qemu setup or it is just a default qemu-x86?

I use default qemu-system-x86_64 on my system.

    $ qemu-system-x86_64 --version
    QEMU emulator version 8.2.2 (qemu-8.2.2-1.1.hs+fb.el9)
    Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers

I forgot saying it is not just x86 but x86_64, sorry.

Thanks,
SJ

Quoted 125 lines

Benjamin

Quoted 118 lines · SeongJae Park

Hello,

Quoted 13 lines · On Wed, 23 Oct 2024 11:41:20 +0200 Benjamin Berg <[email protected]>…

From: Benjamin Berg <[email protected]>

The PTRACE_GETREGSET API has now existed since Linux 2.6.33. The XSAVE CPU feature should also be sufficiently common to be able to rely on it.

With this, define our internal FP state to be the hosts XSAVE data. Add discovery for the hosts XSAVE size and place the FP registers at the end of task_struct so that we can adjust the size at runtime.

Next we can implement the regset API on top and update the signal handling as well as ptrace APIs to use them. Also switch coredump creation to use the regset API and finally set HAVE_ARCH_TRACEHOOK.

Quoted 7 lines

This considerably improves the signal frames. Previously they might not have contained all the registers (i386) and also did not have the sizes and magic values set to the correct values to permit userspace to decode the frame.

As a side effect, this will permit UML to run on hosts with newer CPU extensions (such as AMX) that need even more register state.

I just found kunit starts failing from the mainline commit of this patch on my qemu-x86 Debian system, as below.

    $ git checkout 3f17fed2149192c7d3b76a45a6a87b4ff22cd586
    $ ./tools/testing/kunit/kunit.py run --kunitconfig mm/damon/tests/ --build_dir ../kunit.out/
    [...]
    [22:48:27] Configuring KUnit Kernel ...
    Regenerating .config ...
    Populating config with:
    $ make ARCH=um O=../kunit.out/ olddefconfig
    [22:48:30] Building KUnit Kernel ...
    Populating config with:
    $ make ARCH=um O=../kunit.out/ olddefconfig
    Building with:
    $ make all compile_commands.json ARCH=um O=../kunit.out/ --jobs=40
    [...]
    [22:48:46] Starting KUnit Kernel (1/1)...
    [22:48:46] ============================================================
    Running tests with:
    $ ../kunit.out/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
    [22:48:46] [ERROR] Test: <missing>: Could not find any KTAP output. Did any KUnit tests run?
    [22:48:46] ============================================================
    [22:48:46] Testing complete. Ran 0 tests: errors: 1
    [22:48:46] Elapsed time: 19.285s total, 3.805s configuring, 15.475s building, 0.006s running
Quoted 2 lines

[...]

Quoted 41 lines
arch/x86/um/os-Linux/registers.c +1 -18
-void arch_init_registers(int pid)
-{
-	struct user_fpxregs_struct fpx_regs;
-	int err;
-
-	err = ptrace(PTRACE_GETFPXREGS, pid, 0, &fpx_regs);
-	if (!err)
-		return;
-
-	if (errno != EIO)
-		panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d",
-		      errno);
-
-	have_fpx_regs = 0;
-}
-#else
-
-int get_fp_registers(int pid, unsigned long *regs)
+int arch_init_registers(int pid)

 {

arch/x86/um/os-Linux/registers.c +20 -1
-	return save_fp_registers(pid, regs);
+	struct iovec iov = {
+		/* Just use plenty of space, it does not cost us anything */
+		.iov_len = 2 * 1024 * 1024,
+	};
+	int ret;
+
+	iov.iov_base = mmap(NULL, iov.iov_len, PROT_WRITE | PROT_READ,
+			    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+	if (iov.iov_base == MAP_FAILED)
+		return -ENOMEM;
+
+	/* GDB has x86_xsave_length, which uses x86_cpuid_count */
+	ret = ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov);
+	if (ret)
+		ret = -errno;
+	munmap(iov.iov_base, 2 * 1024 * 1024);
+
+	host_fp_size = iov.iov_len;
+
+	return ret;
Quoted 1 line

 }

And seems it fails from the registers initialization step:

    $ ../kunit.out/linux
    Core dump limits :
            soft - 0
            hard - NONE
    Checking that ptrace can change system call numbers...OK
    Checking syscall emulation for ptrace...OK
    Checking environment variables for a tempdir...none found
    Checking if /dev/shm is on tmpfs...OK
    Checking PROT_EXEC mmap in /dev/shm...OK
    Failed to initialize default registers

I'm not familiar with uml code, so reporting this issue first.  Any thought, please?

Thanks,
SJ

[...]

#4 lore raw
Quoted 11 lines · On Tue, 03 Dec 2024 07:01:09 SeongJae Park <[email protected]> wrote:
Quoted 8 lines · On Tue, 03 Dec 2024 09:40:34 +0100 Benjamin Berg <[email protected]>…

Hi,

that probably means the size detection for the FPU state (i.e. PTRACE_GETREGSET for NT_X86_XSTATE is incorrect on a 32bit host in some way.

Is there anything special about the qemu setup or it is just a default qemu-x86?

Quoted 7 lines

I use default qemu-system-x86_64 on my system.

    $ qemu-system-x86_64 --version
    QEMU emulator version 8.2.2 (qemu-8.2.2-1.1.hs+fb.el9)
    Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers

I forgot saying it is not just x86 but x86_64, sorry.

Oh, and seems my qemu has some downstream changes. I will try to reproduce the issue with upstream versions and report the result again.

Thanks,
SJ

[...]

#5 lore raw
Quoted 23 lines · SeongJae Park
Quoted 21 lines · On Tue, 03 Dec 2024 07:01:09 SeongJae Park <[email protected]> wrote:
Quoted 10 lines · On Tue, 03 Dec 2024 09:40:34 +0100 Benjamin Berg <[email protected]>…

Hi,

that probably means the size detection for the FPU state (i.e. PTRACE_GETREGSET for NT_X86_XSTATE is incorrect on a 32bit host in some
way.

Is there anything special about the qemu setup or it is just a default
qemu-x86?

I use default qemu-system-x86_64 on my system.

    $ qemu-system-x86_64 --version
    QEMU emulator version 8.2.2 (qemu-8.2.2-1.1.hs+fb.el9)
    Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project
developers
Quoted 4 lines
Quoted 1 line

I forgot saying it is not just x86 but x86_64, sorry.

Oh, and seems my qemu has some downstream changes.  I will try to reproduce the issue with upstream versions and report the result again.

I doubt that is the reason. The code tries to detect the size of the NT_X86_XSTATE register set and something breaks.

Thinking about it a bit, the only good explanation is that the qemu CPU does not have XSTATE support. This would cause the ptrace syscall to fetch the NT_X86_XSTATE register set to always fail (with -ENODEV).

Honestly, I just had not expected such an issue. Could you try the below patch to add a fallback?

Regards,
Benjamin

arch/x86/um/os-Linux/registers.c +18 -3
index 76eaeb93928c..eb1cdadc8a61 100644
@@ -18,6 +18,7 @@
 #include <registers.h>
 #include <sys/mman.h>
 
+static unsigned long ptrace_regset;
 unsigned long host_fp_size;
 
 int get_fp_registers(int pid, unsigned long *regs)
@@ -27,7 +28,7 @@ int get_fp_registers(int pid, unsigned long *regs)
 		.iov_len = host_fp_size,
 	};
 
-	if (ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov) < 0)
+	if (ptrace(PTRACE_GETREGSET, pid, ptrace_regset, &iov) < 0)
 		return -errno;
 	return 0;
 }
@@ -39,7 +40,7 @@ int put_fp_registers(int pid, unsigned long *regs)
 		.iov_len = host_fp_size,
 	};
 
-	if (ptrace(PTRACE_SETREGSET, pid, NT_X86_XSTATE, &iov) < 0)
+	if (ptrace(PTRACE_SETREGSET, pid, ptrace_regset, &iov) < 0)
 		return -errno;
 	return 0;
 }
@@ -58,9 +59,23 @@ int arch_init_registers(int pid)
 		return -ENOMEM;
 
 	/* GDB has x86_xsave_length, which uses x86_cpuid_count */
-	ret = ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov);
+	ptrace_regset = NT_X86_XSTATE;
+	ret = ptrace(PTRACE_GETREGSET, pid, ptrace_regset, &iov);
 	if (ret)
 		ret = -errno;
+
+	if (ret == -ENODEV) {
+#ifdef CONFIG_X86_32
+		ptrace_regset = NT_PRXFPREG;
+#else
+		ptrace_regset = NT_PRFPREG;
+#endif
+		iov.iov_len = 2 * 1024 * 1024;
+		ret = ptrace(PTRACE_GETREGSET, pid, ptrace_regset, &iov);
+		if (ret)
+			ret = -errno;
+	}
+
 	munmap(iov.iov_base, 2 * 1024 * 1024);
 
 	host_fp_size = iov.iov_len;
Tested-by
#6 lore raw
Quoted 32 lines · On Tue, 03 Dec 2024 18:07:36 +0100 Benjamin Berg <[email protected]>…
Quoted 23 lines · SeongJae Park
Quoted 21 lines · On Tue, 03 Dec 2024 07:01:09 SeongJae Park <[email protected]> wrote:
Quoted 10 lines · On Tue, 03 Dec 2024 09:40:34 +0100 Benjamin Berg <[email protected]>…

Hi,

that probably means the size detection for the FPU state (i.e. PTRACE_GETREGSET for NT_X86_XSTATE is incorrect on a 32bit host in some
way.

Is there anything special about the qemu setup or it is just a default
qemu-x86?

I use default qemu-system-x86_64 on my system.

    $ qemu-system-x86_64 --version
    QEMU emulator version 8.2.2 (qemu-8.2.2-1.1.hs+fb.el9)
    Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project
developers
Quoted 4 lines
Quoted 1 line

I forgot saying it is not just x86 but x86_64, sorry.

Oh, and seems my qemu has some downstream changes.  I will try to reproduce the issue with upstream versions and report the result again.

I doubt that is the reason. The code tries to detect the size of the NT_X86_XSTATE register set and something breaks.

Quoted 6 lines

Thinking about it a bit, the only good explanation is that the qemu CPU does not have XSTATE support. This would cause the ptrace syscall to fetch the NT_X86_XSTATE register set to always fail (with -ENODEV).

Honestly, I just had not expected such an issue. Could you try the below patch to add a fallback?

Thank you, I just confiremd it fixes my issue.

Thanks,
SJ

[...]

#7 lore raw
In reply to Benjamin Berg

Hi Benjamin,

Quoted 18 lines · Benjamin Berg

From: Benjamin Berg <[email protected]>

The PTRACE_GETREGSET API has now existed since Linux 2.6.33. The XSAVE CPU feature should also be sufficiently common to be able to rely on it.

With this, define our internal FP state to be the hosts XSAVE data. Add discovery for the hosts XSAVE size and place the FP registers at the end of task_struct so that we can adjust the size at runtime.

Next we can implement the regset API on top and update the signal handling as well as ptrace APIs to use them. Also switch coredump creation to use the regset API and finally set HAVE_ARCH_TRACEHOOK.

This considerably improves the signal frames. Previously they might not have contained all the registers (i386) and also did not have the sizes and magic values set to the correct values to permit userspace to decode the frame.

Quoted 4 lines

As a side effect, this will permit UML to run on hosts with newer CPU extensions (such as AMX) that need even more register state.

This patch seems to trip up KASAN. Or at least, KUnit tests fail when I enable CONFIG_KASAN, and 'git bisect' points me here:

$ git bisect run ./tools/testing/kunit/kunit.py run stackinit.test_user --kconfig_add CONFIG_KASAN=y
[...]
3f17fed2149192c7d3b76a45a6a87b4ff22cd586 is the first bad commit
commit 3f17fed2149192c7d3b76a45a6a87b4ff22cd586
Author: Benjamin Berg <[email protected]>
Date:   Wed Oct 23 11:41:20 2024 +0200

um: switch to regset API and depend on XSTATE
[...]

If I run at Linus's latest:

243f750a2df0 Merge tag 'gpio-fixes-for-v6.13-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

I get a KASAN warning and panic [1]. I tried this fix for fun, but it doesn't help:
Subject: [PATCH] um: add back support for FXSAVE registers
https://lore.kernel.org/linux-um/[email protected]/

I'm not very familiar with this area, but let me know if there's more I can help with on tracking the issue down. Hopefully, it's as easy as running these same commands for you to reproduce.

Brian

[1]

$ ./tools/testing/kunit/kunit.py run stackinit.test_user --kconfig_add CONFIG_KASAN=y --raw_output=all
[...]
<3>==================================================================
<3>BUG: KASAN: global-out-of-bounds in arch_dup_task_struct+0x4b/0x70
<3>Read of size 4616 at addr 0000000060b1aec0 by task swapper/0
<3>
<3>CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.13.0-rc2-00194-g6787126c27ef #61
<3>Stack:
<4> 00000000 00000000 ffffff00 60acc428
<4> 60ad2ffc 9f225db0 00000001 6008b7fb
<4> 60b17aa0 6003fbf5 60b1aec0 6004c654
<3>Call Trace:
<3> [<60038c0e>] ? show_stack.cold+0x64/0xf3
<3> [<6008b7fb>] ? dump_stack_lvl+0x8b/0xa7
<3> [<6003fbf5>] ? _printk+0x0/0x103
<3> [<6004c654>] ? print_report+0x145/0x519
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<6031f854>] ? kasan_report+0x114/0x160
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<60320830>] ? kasan_check_range+0x0/0x1e0
<3> [<603209a0>] ? kasan_check_range+0x170/0x1e0
<3> [<6032135d>] ? __asan_memcpy+0x2d/0x80
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<600b9381>] ? copy_process+0x3e1/0x7390
<3> [<600af1a0>] ? block_signals+0x0/0x20
<3> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<3> [<601b48d6>] ? stack_trace_save+0x86/0xa0
<3> [<6063ef2c>] ? stack_depot_save_flags+0x2c/0xa80
<3> [<601b4850>] ? stack_trace_save+0x0/0xa0
<3> [<6031e919>] ? kasan_save_stack+0x49/0x60
<3> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<3> [<6031e919>] ? kasan_save_stack+0x49/0x60
<3> [<600b8fa0>] ? copy_process+0x0/0x7390
<3> [<600c04b3>] ? kernel_clone+0xd3/0x8c0
<3> [<600c03e0>] ? kernel_clone+0x0/0x8c0
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<600c107d>] ? user_mode_thread+0x9d/0xc0
<3> [<600c0fe0>] ? user_mode_thread+0x0/0xc0
<3> [<60926934>] ? kernel_init+0x0/0x18c
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<603bb69d>] ? kern_mount+0x3d/0xb0
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60926831>] ? rest_init+0x2d/0x130
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<60002679>] ? do_one_initcall+0x0/0x450
<3> [<60005c97>] ? start_kernel_proc+0x0/0x1d
<3> [<60005cb0>] ? start_kernel_proc+0x19/0x1d
<3> [<600904fa>] ? new_thread_handler+0xca/0x130
<3>
<3>The buggy address belongs to the variable:
<3> 0x60b1aec0
<3>
<3>The buggy address belongs to the physical page:
<4>page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0xb1a
<4>flags: 0x2000(reserved|zone=0)
<4>raw: 0000000000002000 000000009f225db8 000000009f225db8 0000000000000000
<4>raw: 0000000000000000 0000000000000000 00000001ffffffff
<4>page dumped because: kasan: bad access detected
<3>
<3>Memory state around the buggy address:
<3> 0000000060b1b600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
<3> 0000000060b1b680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
<3>>0000000060b1b700: 00 00 00 00 00 00 00 00 f9 f9 f9 f9 00 00 00 00
<3>                                           ^
<3> 0000000060b1b780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
<3> 0000000060b1b800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
<3>==================================================================
<4>Disabling lock debugging due to kernel taint
<4>
<6>Pid: 0, comm: swapper Tainted: G    B              6.13.0-rc2-00194-g6787126c27ef
<6>RIP: 0033:copy_namespaces+0x104/0x2b0
<6>RSP: 0000000060b17b70  EFLAGS: 00010246
<6>RAX: 0000000000000001 RBX: 00000000610a8000 RCX: 0000000060133d7f
<6>RDX: 0000000000000001 RSI: 0000000000000004 RDI: 0000000000000000
<6>RBP: 0000000000000000 R08: 0000000000000001 R09: 0000100000000000
<6>R10: 0000000000000003 R11: ffffffffffffffff R12: 0000000000800300
<6>R13: 000000006102a000 R14: 00000000610a84d8 R15: 0000000060b31ba0
<0>Kernel panic - not syncing: Segfault with no mm
<4>CPU: 0 UID: 0 PID: 0 Comm: swapper Tainted: G    B              6.13.0-rc2-00194-g6787126c27ef #61
<4>Tainted: [B]=BAD_PAGE
<4>Stack:
<4> 00000000 60321286 61070380 0c162f92
<4> 00000000 60b1aec0 61070110 610a8000
<4> 610a8498 600bae85 61001400 60b17ed0
<4>Call Trace:
<4> [<60321286>] ? __asan_memset+0x26/0x50
<4> [<600bae85>] ? copy_process+0x1ee5/0x7390
<4> [<600af1a0>] ? block_signals+0x0/0x20
<4> [<6063ef2c>] ? stack_depot_save_flags+0x2c/0xa80
<4> [<601b4850>] ? stack_trace_save+0x0/0xa0
<4> [<6031e919>] ? kasan_save_stack+0x49/0x60
<4> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<4> [<6031e919>] ? kasan_save_stack+0x49/0x60
<4> [<600b8fa0>] ? copy_process+0x0/0x7390
<4> [<600c04b3>] ? kernel_clone+0xd3/0x8c0
<4> [<600c03e0>] ? kernel_clone+0x0/0x8c0
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<600c107d>] ? user_mode_thread+0x9d/0xc0
<4> [<600c0fe0>] ? user_mode_thread+0x0/0xc0
<4> [<60926934>] ? kernel_init+0x0/0x18c
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<603bb69d>] ? kern_mount+0x3d/0xb0
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60926831>] ? rest_init+0x2d/0x130
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<60002679>] ? do_one_initcall+0x0/0x450
<4> [<60005c97>] ? start_kernel_proc+0x0/0x1d
<4> [<60005cb0>] ? start_kernel_proc+0x19/0x1d
<4> [<600904fa>] ? new_thread_handler+0xca/0x130
[11:56:56] Elapsed time: 6.794s total, 0.001s configuring, 5.513s building, 1.280s running
#8 lore raw

Hi Brian,

outch. It is doing a memcpy of init_task. Now, struct task_struct is variably sized, but init_struct is statically allocated, which could explain why the memcpy is not permitted to read the larger memory (for the FP register space).
I can reproduce it with the kunit.py script, but didn't run into it with my own configuration.

Now, this patch works around the problem:

arch/um/kernel/process.c +0 -0
index 30bdc0a87dc8..7748df822d30 100644
@@ -191,7 +191,10 @@ void initial_thread_cb(void (*proc)(void *), void

*arg)
int arch_dup_task_struct(struct task_struct *dst,
struct task_struct *src)
{
- memcpy(dst, src, arch_task_struct_size);
+ if (src == &init_task)
+ memcpy(dst, src, sizeof(init_task));
+ else

+               memcpy(dst, src, arch_task_struct_size);
        return 0;

}

However, that cannot really be correct. I believe what should be happening is that init_task is loaded into init_stack (see INIT_TASK_DATA in vmlinux.lds.h). I am assuming that if this was the case, then KASAN would be happy with it. However, I see the following addresses
__start_init_stack: 0x606dc000
__end_init_stack: 0x606e0000
init_task: 0x606e2ec0
and I am not sure why the linker script is not placing init_task into the stack here.

Also note that commit 2f681ba4b352 ("um: move thread info into task") may be part of a correct fix here.

Benjamin

Quoted 195 lines · Brian Norris

Hi Benjamin,

Quoted 24 lines · Benjamin Berg

From: Benjamin Berg <[email protected]>

The PTRACE_GETREGSET API has now existed since Linux 2.6.33. The XSAVE
CPU feature should also be sufficiently common to be able to rely on it.

With this, define our internal FP state to be the hosts XSAVE data. Add
discovery for the hosts XSAVE size and place the FP registers at the end
of task_struct so that we can adjust the size at runtime.

Next we can implement the regset API on top and update the signal handling as well as ptrace APIs to use them. Also switch coredump creation to use the regset API and finally set HAVE_ARCH_TRACEHOOK.

This considerably improves the signal frames. Previously they might not
have contained all the registers (i386) and also did not have the sizes and magic values set to the correct values to permit userspace to
decode the frame.

Quoted 5 lines

As a side effect, this will permit UML to run on hosts with newer CPU
extensions (such as AMX) that need even more register state.

This patch seems to trip up KASAN. Or at least, KUnit tests fail when I
enable CONFIG_KASAN, and 'git bisect' points me here:

$ git bisect run ./tools/testing/kunit/kunit.py run
stackinit.test_user --kconfig_add CONFIG_KASAN=y
[...]
3f17fed2149192c7d3b76a45a6a87b4ff22cd586 is the first bad commit
commit 3f17fed2149192c7d3b76a45a6a87b4ff22cd586
Author: Benjamin Berg <[email protected]>
Date:   Wed Oct 23 11:41:20 2024 +0200

    um: switch to regset API and depend on XSTATE
[...]

If I run at Linus's latest:

  243f750a2df0 Merge tag 'gpio-fixes-for-v6.13-rc3' of
git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

I get a KASAN warning and panic [1]. I tried this fix for fun, but it doesn't help:
Subject: [PATCH] um: add back support for FXSAVE registers
https://lore.kernel.org/linux-um/[email protected]/

I'm not very familiar with this area, but let me know if there's more I
can help with on tracking the issue down. Hopefully, it's as easy as running these same commands for you to reproduce.

Brian

[1]

$ ./tools/testing/kunit/kunit.py run stackinit.test_user --
kconfig_add CONFIG_KASAN=y --raw_output=all
[...]
<3>==================================================================
<3>BUG: KASAN: global-out-of-bounds in arch_dup_task_struct+0x4b/0x70
<3>Read of size 4616 at addr 0000000060b1aec0 by task swapper/0
<3>
<3>CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.13.0-rc2-00194-
g6787126c27ef #61
<3>Stack:
<4> 00000000 00000000 ffffff00 60acc428
<4> 60ad2ffc 9f225db0 00000001 6008b7fb
<4> 60b17aa0 6003fbf5 60b1aec0 6004c654
<3>Call Trace:
<3> [<60038c0e>] ? show_stack.cold+0x64/0xf3
<3> [<6008b7fb>] ? dump_stack_lvl+0x8b/0xa7
<3> [<6003fbf5>] ? _printk+0x0/0x103
<3> [<6004c654>] ? print_report+0x145/0x519
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<6031f854>] ? kasan_report+0x114/0x160
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<60320830>] ? kasan_check_range+0x0/0x1e0
<3> [<603209a0>] ? kasan_check_range+0x170/0x1e0
<3> [<6032135d>] ? __asan_memcpy+0x2d/0x80
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<600b9381>] ? copy_process+0x3e1/0x7390
<3> [<600af1a0>] ? block_signals+0x0/0x20
<3> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<3> [<601b48d6>] ? stack_trace_save+0x86/0xa0
<3> [<6063ef2c>] ? stack_depot_save_flags+0x2c/0xa80
<3> [<601b4850>] ? stack_trace_save+0x0/0xa0
<3> [<6031e919>] ? kasan_save_stack+0x49/0x60
<3> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<3> [<6031e919>] ? kasan_save_stack+0x49/0x60
<3> [<600b8fa0>] ? copy_process+0x0/0x7390
<3> [<600c04b3>] ? kernel_clone+0xd3/0x8c0
<3> [<600c03e0>] ? kernel_clone+0x0/0x8c0
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<600c107d>] ? user_mode_thread+0x9d/0xc0
<3> [<600c0fe0>] ? user_mode_thread+0x0/0xc0
<3> [<60926934>] ? kernel_init+0x0/0x18c
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<603bb69d>] ? kern_mount+0x3d/0xb0
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60926831>] ? rest_init+0x2d/0x130
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<60002679>] ? do_one_initcall+0x0/0x450
<3> [<60005c97>] ? start_kernel_proc+0x0/0x1d
<3> [<60005cb0>] ? start_kernel_proc+0x19/0x1d
<3> [<600904fa>] ? new_thread_handler+0xca/0x130
<3>
<3>The buggy address belongs to the variable:
<3> 0x60b1aec0
<3>
<3>The buggy address belongs to the physical page:
<4>page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x0
pfn:0xb1a
<4>flags: 0x2000(reserved|zone=0)
<4>raw: 0000000000002000 000000009f225db8 000000009f225db8
0000000000000000
<4>raw: 0000000000000000 0000000000000000 00000001ffffffff
<4>page dumped because: kasan: bad access detected
<3>
<3>Memory state around the buggy address:
<3> 0000000060b1b600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
<3> 0000000060b1b680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
<3>>0000000060b1b700: 00 00 00 00 00 00 00 00 f9 f9 f9 f9 00 00 00 00
<3>                                           ^
<3> 0000000060b1b780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
<3> 0000000060b1b800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
<3>==================================================================
<4>Disabling lock debugging due to kernel taint
<4>
<6>Pid: 0, comm: swapper Tainted: G    B              6.13.0-rc2-
00194-g6787126c27ef
<6>RIP: 0033:copy_namespaces+0x104/0x2b0
<6>RSP: 0000000060b17b70  EFLAGS: 00010246
<6>RAX: 0000000000000001 RBX: 00000000610a8000 RCX: 0000000060133d7f
<6>RDX: 0000000000000001 RSI: 0000000000000004 RDI: 0000000000000000
<6>RBP: 0000000000000000 R08: 0000000000000001 R09: 0000100000000000
<6>R10: 0000000000000003 R11: ffffffffffffffff R12: 0000000000800300
<6>R13: 000000006102a000 R14: 00000000610a84d8 R15: 0000000060b31ba0
<0>Kernel panic - not syncing: Segfault with no mm
<4>CPU: 0 UID: 0 PID: 0 Comm: swapper Tainted: G    B             
6.13.0-rc2-00194-g6787126c27ef #61
<4>Tainted: [B]=BAD_PAGE
<4>Stack:
<4> 00000000 60321286 61070380 0c162f92
<4> 00000000 60b1aec0 61070110 610a8000
<4> 610a8498 600bae85 61001400 60b17ed0
<4>Call Trace:
<4> [<60321286>] ? __asan_memset+0x26/0x50
<4> [<600bae85>] ? copy_process+0x1ee5/0x7390
<4> [<600af1a0>] ? block_signals+0x0/0x20
<4> [<6063ef2c>] ? stack_depot_save_flags+0x2c/0xa80
<4> [<601b4850>] ? stack_trace_save+0x0/0xa0
<4> [<6031e919>] ? kasan_save_stack+0x49/0x60
<4> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<4> [<6031e919>] ? kasan_save_stack+0x49/0x60
<4> [<600b8fa0>] ? copy_process+0x0/0x7390
<4> [<600c04b3>] ? kernel_clone+0xd3/0x8c0
<4> [<600c03e0>] ? kernel_clone+0x0/0x8c0
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<600c107d>] ? user_mode_thread+0x9d/0xc0
<4> [<600c0fe0>] ? user_mode_thread+0x0/0xc0
<4> [<60926934>] ? kernel_init+0x0/0x18c
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<603bb69d>] ? kern_mount+0x3d/0xb0
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60926831>] ? rest_init+0x2d/0x130
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<60002679>] ? do_one_initcall+0x0/0x450
<4> [<60005c97>] ? start_kernel_proc+0x0/0x1d
<4> [<60005cb0>] ? start_kernel_proc+0x19/0x1d
<4> [<600904fa>] ? new_thread_handler+0xca/0x130
[11:56:56] Elapsed time: 6.794s total, 0.001s configuring, 5.513s
building, 1.280s running
#9 lore raw

Hi,

Quoted 38 lines · Benjamin Berg

outch. It is doing a memcpy of init_task. Now, struct task_struct is variably sized, but init_struct is statically allocated, which could explain why the memcpy is not permitted to read the larger memory (for the FP register space).
I can reproduce it with the kunit.py script, but didn't run into it with my own configuration.

Now, this patch works around the problem:

arch/um/kernel/process.c +4 -1
index 30bdc0a87dc8..7748df822d30 100644
@@ -191,7 +191,10 @@ void initial_thread_cb(void (*proc)(void *), void
 *arg)
  int arch_dup_task_struct(struct task_struct *dst,
                          struct task_struct *src)
  {
-       memcpy(dst, src, arch_task_struct_size);
+       if (src == &init_task)
+               memcpy(dst, src, sizeof(init_task));
+       else
+               memcpy(dst, src, arch_task_struct_size);
         return 0;
  }

However, that cannot really be correct. I believe what should be happening is that init_task is loaded into init_stack (see INIT_TASK_DATA in vmlinux.lds.h). I am assuming that if this was the case, then KASAN would be happy with it. However, I see the following addresses
  __start_init_stack: 0x606dc000
  __end_init_stack: 0x606e0000
  init_task: 0x606e2ec0
and I am not sure why the linker script is not placing init_task into the stack here.

Quoted 2 lines

Also note that commit 2f681ba4b352 ("um: move thread info into task") may be part of a correct fix here.

So, I dug a bit more, and found

commit 0eb5085c38749f2a91e5bd8cbebb1ebf3398343c

Author: Heiko Carstens <[email protected]>
Date:   Thu Nov 16 14:36:38 2023 +0100

arch: remove ARCH_TASK_STRUCT_ON_STACK

This explains why init_task is not on init_stack. It also means that the related linker script entries that I saw can be removed.

So, maybe the above patch is actually acceptable. We never need the FPU register state for init_task, so we do not really need it to be allocated either. The only place where it causes issues is in arch_dup_task_struct.
In that case, x86 would require the same fix.

My best guess right now is that whether the error occurs depends on the on the size/alignment of init_task. If we happen to have enough padding afterwards then we do not run into the red zone of the next (unexported) global variable (init_sighand for me). But, if the padding is too small, then KASAN detects the error and aborts.

Does someone maybe know a KASAN/x86 expert that we could talk to?

Benjamin

Quoted 219 lines · Brian Norris
Quoted 215 lines

Hi Benjamin,

Quoted 27 lines · Benjamin Berg

From: Benjamin Berg <[email protected]>

The PTRACE_GETREGSET API has now existed since Linux 2.6.33. The XSAVE
CPU feature should also be sufficiently common to be able to rely on it.

With this, define our internal FP state to be the hosts XSAVE data.
Add
discovery for the hosts XSAVE size and place the FP registers at the end
of task_struct so that we can adjust the size at runtime.

Next we can implement the regset API on top and update the signal handling as well as ptrace APIs to use them. Also switch coredump creation to use the regset API and finally set HAVE_ARCH_TRACEHOOK.

This considerably improves the signal frames. Previously they might
not
have contained all the registers (i386) and also did not have the sizes and magic values set to the correct values to permit userspace to
decode the frame.

Quoted 5 lines

As a side effect, this will permit UML to run on hosts with newer CPU
extensions (such as AMX) that need even more register state.

This patch seems to trip up KASAN. Or at least, KUnit tests fail when
I
enable CONFIG_KASAN, and 'git bisect' points me here:

$ git bisect run ./tools/testing/kunit/kunit.py run
stackinit.test_user --kconfig_add CONFIG_KASAN=y
[...]
3f17fed2149192c7d3b76a45a6a87b4ff22cd586 is the first bad commit
commit 3f17fed2149192c7d3b76a45a6a87b4ff22cd586
Author: Benjamin Berg <[email protected]>
Date:   Wed Oct 23 11:41:20 2024 +0200

    um: switch to regset API and depend on XSTATE
[...]

If I run at Linus's latest:

  243f750a2df0 Merge tag 'gpio-fixes-for-v6.13-rc3' of
git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

I get a KASAN warning and panic [1]. I tried this fix for fun, but it
doesn't help:
Subject: [PATCH] um: add back support for FXSAVE registers
https://lore.kernel.org/linux-um/[email protected]/

I'm not very familiar with this area, but let me know if there's more
I
can help with on tracking the issue down. Hopefully, it's as easy as
running these same commands for you to reproduce.

Brian

[1]

$ ./tools/testing/kunit/kunit.py run stackinit.test_user --
kconfig_add CONFIG_KASAN=y --raw_output=all
[...]
<3>================================================================
==
<3>BUG: KASAN: global-out-of-bounds in
arch_dup_task_struct+0x4b/0x70
<3>Read of size 4616 at addr 0000000060b1aec0 by task swapper/0
<3>
<3>CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.13.0-rc2-00194-
g6787126c27ef #61
<3>Stack:
<4> 00000000 00000000 ffffff00 60acc428
<4> 60ad2ffc 9f225db0 00000001 6008b7fb
<4> 60b17aa0 6003fbf5 60b1aec0 6004c654
<3>Call Trace:
<3> [<60038c0e>] ? show_stack.cold+0x64/0xf3
<3> [<6008b7fb>] ? dump_stack_lvl+0x8b/0xa7
<3> [<6003fbf5>] ? _printk+0x0/0x103
<3> [<6004c654>] ? print_report+0x145/0x519
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<6031f854>] ? kasan_report+0x114/0x160
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<60320830>] ? kasan_check_range+0x0/0x1e0
<3> [<603209a0>] ? kasan_check_range+0x170/0x1e0
<3> [<6032135d>] ? __asan_memcpy+0x2d/0x80
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<600b9381>] ? copy_process+0x3e1/0x7390
<3> [<600af1a0>] ? block_signals+0x0/0x20
<3> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<3> [<601b48d6>] ? stack_trace_save+0x86/0xa0
<3> [<6063ef2c>] ? stack_depot_save_flags+0x2c/0xa80
<3> [<601b4850>] ? stack_trace_save+0x0/0xa0
<3> [<6031e919>] ? kasan_save_stack+0x49/0x60
<3> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<3> [<6031e919>] ? kasan_save_stack+0x49/0x60
<3> [<600b8fa0>] ? copy_process+0x0/0x7390
<3> [<600c04b3>] ? kernel_clone+0xd3/0x8c0
<3> [<600c03e0>] ? kernel_clone+0x0/0x8c0
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<600c107d>] ? user_mode_thread+0x9d/0xc0
<3> [<600c0fe0>] ? user_mode_thread+0x0/0xc0
<3> [<60926934>] ? kernel_init+0x0/0x18c
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<603bb69d>] ? kern_mount+0x3d/0xb0
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60926831>] ? rest_init+0x2d/0x130
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<60002679>] ? do_one_initcall+0x0/0x450
<3> [<60005c97>] ? start_kernel_proc+0x0/0x1d
<3> [<60005cb0>] ? start_kernel_proc+0x19/0x1d
<3> [<600904fa>] ? new_thread_handler+0xca/0x130
<3>
<3>The buggy address belongs to the variable:
<3> 0x60b1aec0
<3>
<3>The buggy address belongs to the physical page:
<4>page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x0
pfn:0xb1a
<4>flags: 0x2000(reserved|zone=0)
<4>raw: 0000000000002000 000000009f225db8 000000009f225db8
0000000000000000
<4>raw: 0000000000000000 0000000000000000 00000001ffffffff
<4>page dumped because: kasan: bad access detected
<3>
<3>Memory state around the buggy address:
<3> 0000000060b1b600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00
<3> 0000000060b1b680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00
<3>>0000000060b1b700: 00 00 00 00 00 00 00 00 f9 f9 f9 f9 00 00 00
00
<3>                                           ^
<3> 0000000060b1b780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00
<3> 0000000060b1b800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00
<3>================================================================
==
<4>Disabling lock debugging due to kernel taint
<4>
<6>Pid: 0, comm: swapper Tainted: G    B              6.13.0-rc2-
00194-g6787126c27ef
<6>RIP: 0033:copy_namespaces+0x104/0x2b0
<6>RSP: 0000000060b17b70  EFLAGS: 00010246
<6>RAX: 0000000000000001 RBX: 00000000610a8000 RCX:
0000000060133d7f
<6>RDX: 0000000000000001 RSI: 0000000000000004 RDI:
0000000000000000
<6>RBP: 0000000000000000 R08: 0000000000000001 R09:
0000100000000000
<6>R10: 0000000000000003 R11: ffffffffffffffff R12:
0000000000800300
<6>R13: 000000006102a000 R14: 00000000610a84d8 R15:
0000000060b31ba0
<0>Kernel panic - not syncing: Segfault with no mm
<4>CPU: 0 UID: 0 PID: 0 Comm: swapper Tainted: G    B             
6.13.0-rc2-00194-g6787126c27ef #61
<4>Tainted: [B]=BAD_PAGE
<4>Stack:
<4> 00000000 60321286 61070380 0c162f92
<4> 00000000 60b1aec0 61070110 610a8000
<4> 610a8498 600bae85 61001400 60b17ed0
<4>Call Trace:
<4> [<60321286>] ? __asan_memset+0x26/0x50
<4> [<600bae85>] ? copy_process+0x1ee5/0x7390
<4> [<600af1a0>] ? block_signals+0x0/0x20
<4> [<6063ef2c>] ? stack_depot_save_flags+0x2c/0xa80
<4> [<601b4850>] ? stack_trace_save+0x0/0xa0
<4> [<6031e919>] ? kasan_save_stack+0x49/0x60
<4> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<4> [<6031e919>] ? kasan_save_stack+0x49/0x60
<4> [<600b8fa0>] ? copy_process+0x0/0x7390
<4> [<600c04b3>] ? kernel_clone+0xd3/0x8c0
<4> [<600c03e0>] ? kernel_clone+0x0/0x8c0
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<600c107d>] ? user_mode_thread+0x9d/0xc0
<4> [<600c0fe0>] ? user_mode_thread+0x0/0xc0
<4> [<60926934>] ? kernel_init+0x0/0x18c
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<603bb69d>] ? kern_mount+0x3d/0xb0
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60926831>] ? rest_init+0x2d/0x130
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<60002679>] ? do_one_initcall+0x0/0x450
<4> [<60005c97>] ? start_kernel_proc+0x0/0x1d
<4> [<60005cb0>] ? start_kernel_proc+0x19/0x1d
<4> [<600904fa>] ? new_thread_handler+0xca/0x130
[11:56:56] Elapsed time: 6.794s total, 0.001s configuring, 5.513s
building, 1.280s running
Tested-by
#10 lore raw

(+ kasan-dev; leaving most of this thread intact)

Quoted 21 lines · Benjamin Berg

Hi,

Quoted 18 lines · Benjamin Berg

outch. It is doing a memcpy of init_task. Now, struct task_struct is variably sized, but init_struct is statically allocated, which could explain why the memcpy is not permitted to read the larger memory (for the FP register space).
I can reproduce it with the kunit.py script, but didn't run into it with my own configuration.

Now, this patch works around the problem:

arch/um/kernel/process.c +0 -0
index 30bdc0a87dc8..7748df822d30 100644
@@ -191,7 +191,10 @@ void initial_thread_cb(void (*proc)(void *), void
 *arg)
  int arch_dup_task_struct(struct task_struct *dst,
                          struct task_struct *src)
  {
Quoted 8 lines
Quoted 8 lines
arch/um/kernel/process.c +4 -1
-       memcpy(dst, src, arch_task_struct_size);
+       if (src == &init_task)
+               memcpy(dst, src, sizeof(init_task));
+       else
+               memcpy(dst, src, arch_task_struct_size);
         return 0;
  }

FWIW, after fixing up the mangled whitespace, this works for me:

Quoted 33 lines
Quoted 12 lines

However, that cannot really be correct. I believe what should be happening is that init_task is loaded into init_stack (see INIT_TASK_DATA in vmlinux.lds.h). I am assuming that if this was the case, then KASAN would be happy with it. However, I see the following addresses
  __start_init_stack: 0x606dc000
  __end_init_stack: 0x606e0000
  init_task: 0x606e2ec0
and I am not sure why the linker script is not placing init_task into the stack here.

Quoted 2 lines

Also note that commit 2f681ba4b352 ("um: move thread info into task") may be part of a correct fix here.

So, I dug a bit more, and found

commit 0eb5085c38749f2a91e5bd8cbebb1ebf3398343c

Author: Heiko Carstens <[email protected]>
Date:   Thu Nov 16 14:36:38 2023 +0100

arch: remove ARCH_TASK_STRUCT_ON_STACK

This explains why init_task is not on init_stack. It also means that the related linker script entries that I saw can be removed.

So, maybe the above patch is actually acceptable. We never need the FPU register state for init_task, so we do not really need it to be allocated either. The only place where it causes issues is in arch_dup_task_struct.
In that case, x86 would require the same fix.

Quoted 8 lines

My best guess right now is that whether the error occurs depends on the on the size/alignment of init_task. If we happen to have enough padding afterwards then we do not run into the red zone of the next (unexported) global variable (init_sighand for me). But, if the padding is too small, then KASAN detects the error and aborts.

Does someone maybe know a KASAN/x86 expert that we could talk to?

Not exactly, but I've CC'd their development list.

Brian

Quoted 222 lines

Benjamin

Quoted 219 lines · Brian Norris
Quoted 215 lines

Hi Benjamin,

Quoted 27 lines · Benjamin Berg

From: Benjamin Berg <[email protected]>

The PTRACE_GETREGSET API has now existed since Linux 2.6.33. The XSAVE
CPU feature should also be sufficiently common to be able to rely on it.

With this, define our internal FP state to be the hosts XSAVE data.
Add
discovery for the hosts XSAVE size and place the FP registers at the end
of task_struct so that we can adjust the size at runtime.

Next we can implement the regset API on top and update the signal handling as well as ptrace APIs to use them. Also switch coredump creation to use the regset API and finally set HAVE_ARCH_TRACEHOOK.

This considerably improves the signal frames. Previously they might
not
have contained all the registers (i386) and also did not have the sizes and magic values set to the correct values to permit userspace to
decode the frame.

Quoted 5 lines

As a side effect, this will permit UML to run on hosts with newer CPU
extensions (such as AMX) that need even more register state.

This patch seems to trip up KASAN. Or at least, KUnit tests fail when
I
enable CONFIG_KASAN, and 'git bisect' points me here:

$ git bisect run ./tools/testing/kunit/kunit.py run
stackinit.test_user --kconfig_add CONFIG_KASAN=y
[...]
3f17fed2149192c7d3b76a45a6a87b4ff22cd586 is the first bad commit
commit 3f17fed2149192c7d3b76a45a6a87b4ff22cd586
Author: Benjamin Berg <[email protected]>
Date:   Wed Oct 23 11:41:20 2024 +0200

    um: switch to regset API and depend on XSTATE
[...]

If I run at Linus's latest:

  243f750a2df0 Merge tag 'gpio-fixes-for-v6.13-rc3' of
git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

I get a KASAN warning and panic [1]. I tried this fix for fun, but it
doesn't help:
Subject: [PATCH] um: add back support for FXSAVE registers
https://lore.kernel.org/linux-um/[email protected]/

I'm not very familiar with this area, but let me know if there's more
I
can help with on tracking the issue down. Hopefully, it's as easy as
running these same commands for you to reproduce.

Brian

[1]

$ ./tools/testing/kunit/kunit.py run stackinit.test_user --
kconfig_add CONFIG_KASAN=y --raw_output=all
[...]
<3>================================================================
==
<3>BUG: KASAN: global-out-of-bounds in
arch_dup_task_struct+0x4b/0x70
<3>Read of size 4616 at addr 0000000060b1aec0 by task swapper/0
<3>
<3>CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.13.0-rc2-00194-
g6787126c27ef #61
<3>Stack:
<4> 00000000 00000000 ffffff00 60acc428
<4> 60ad2ffc 9f225db0 00000001 6008b7fb
<4> 60b17aa0 6003fbf5 60b1aec0 6004c654
<3>Call Trace:
<3> [<60038c0e>] ? show_stack.cold+0x64/0xf3
<3> [<6008b7fb>] ? dump_stack_lvl+0x8b/0xa7
<3> [<6003fbf5>] ? _printk+0x0/0x103
<3> [<6004c654>] ? print_report+0x145/0x519
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<6031f854>] ? kasan_report+0x114/0x160
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<60320830>] ? kasan_check_range+0x0/0x1e0
<3> [<603209a0>] ? kasan_check_range+0x170/0x1e0
<3> [<6032135d>] ? __asan_memcpy+0x2d/0x80
<3> [<60090f2b>] ? arch_dup_task_struct+0x4b/0x70
<3> [<600b9381>] ? copy_process+0x3e1/0x7390
<3> [<600af1a0>] ? block_signals+0x0/0x20
<3> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<3> [<601b48d6>] ? stack_trace_save+0x86/0xa0
<3> [<6063ef2c>] ? stack_depot_save_flags+0x2c/0xa80
<3> [<601b4850>] ? stack_trace_save+0x0/0xa0
<3> [<6031e919>] ? kasan_save_stack+0x49/0x60
<3> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<3> [<6031e919>] ? kasan_save_stack+0x49/0x60
<3> [<600b8fa0>] ? copy_process+0x0/0x7390
<3> [<600c04b3>] ? kernel_clone+0xd3/0x8c0
<3> [<600c03e0>] ? kernel_clone+0x0/0x8c0
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<600c107d>] ? user_mode_thread+0x9d/0xc0
<3> [<600c0fe0>] ? user_mode_thread+0x0/0xc0
<3> [<60926934>] ? kernel_init+0x0/0x18c
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<603bb69d>] ? kern_mount+0x3d/0xb0
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60926831>] ? rest_init+0x2d/0x130
<3> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<3> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<3> [<60038700>] ? arch_local_save_flags+0x0/0x43
<3> [<60002679>] ? do_one_initcall+0x0/0x450
<3> [<60005c97>] ? start_kernel_proc+0x0/0x1d
<3> [<60005cb0>] ? start_kernel_proc+0x19/0x1d
<3> [<600904fa>] ? new_thread_handler+0xca/0x130
<3>
<3>The buggy address belongs to the variable:
<3> 0x60b1aec0
<3>
<3>The buggy address belongs to the physical page:
<4>page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x0
pfn:0xb1a
<4>flags: 0x2000(reserved|zone=0)
<4>raw: 0000000000002000 000000009f225db8 000000009f225db8
0000000000000000
<4>raw: 0000000000000000 0000000000000000 00000001ffffffff
<4>page dumped because: kasan: bad access detected
<3>
<3>Memory state around the buggy address:
<3> 0000000060b1b600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00
<3> 0000000060b1b680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00
<3>>0000000060b1b700: 00 00 00 00 00 00 00 00 f9 f9 f9 f9 00 00 00
00
<3>                                           ^
<3> 0000000060b1b780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00
<3> 0000000060b1b800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00
<3>================================================================
==
<4>Disabling lock debugging due to kernel taint
<4>
<6>Pid: 0, comm: swapper Tainted: G    B              6.13.0-rc2-
00194-g6787126c27ef
<6>RIP: 0033:copy_namespaces+0x104/0x2b0
<6>RSP: 0000000060b17b70  EFLAGS: 00010246
<6>RAX: 0000000000000001 RBX: 00000000610a8000 RCX:
0000000060133d7f
<6>RDX: 0000000000000001 RSI: 0000000000000004 RDI:
0000000000000000
<6>RBP: 0000000000000000 R08: 0000000000000001 R09:
0000100000000000
<6>R10: 0000000000000003 R11: ffffffffffffffff R12:
0000000000800300
<6>R13: 000000006102a000 R14: 00000000610a84d8 R15:
0000000060b31ba0
<0>Kernel panic - not syncing: Segfault with no mm
<4>CPU: 0 UID: 0 PID: 0 Comm: swapper Tainted: G    B             
6.13.0-rc2-00194-g6787126c27ef #61
<4>Tainted: [B]=BAD_PAGE
<4>Stack:
<4> 00000000 60321286 61070380 0c162f92
<4> 00000000 60b1aec0 61070110 610a8000
<4> 610a8498 600bae85 61001400 60b17ed0
<4>Call Trace:
<4> [<60321286>] ? __asan_memset+0x26/0x50
<4> [<600bae85>] ? copy_process+0x1ee5/0x7390
<4> [<600af1a0>] ? block_signals+0x0/0x20
<4> [<6063ef2c>] ? stack_depot_save_flags+0x2c/0xa80
<4> [<601b4850>] ? stack_trace_save+0x0/0xa0
<4> [<6031e919>] ? kasan_save_stack+0x49/0x60
<4> [<603bb46e>] ? vfs_kern_mount.part.0+0x6e/0x140
<4> [<6031e919>] ? kasan_save_stack+0x49/0x60
<4> [<600b8fa0>] ? copy_process+0x0/0x7390
<4> [<600c04b3>] ? kernel_clone+0xd3/0x8c0
<4> [<600c03e0>] ? kernel_clone+0x0/0x8c0
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<600c107d>] ? user_mode_thread+0x9d/0xc0
<4> [<600c0fe0>] ? user_mode_thread+0x0/0xc0
<4> [<60926934>] ? kernel_init+0x0/0x18c
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<603bb69d>] ? kern_mount+0x3d/0xb0
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60926831>] ? rest_init+0x2d/0x130
<4> [<6003875e>] ? arch_local_irq_disable+0x0/0xc
<4> [<60038743>] ? arch_irqs_disabled_flags+0x0/0x9
<4> [<60038700>] ? arch_local_save_flags+0x0/0x43
<4> [<60002679>] ? do_one_initcall+0x0/0x450
<4> [<60005c97>] ? start_kernel_proc+0x0/0x1d
<4> [<60005cb0>] ? start_kernel_proc+0x19/0x1d
<4> [<600904fa>] ? new_thread_handler+0xca/0x130
[11:56:56] Elapsed time: 6.794s total, 0.001s configuring, 5.513s
building, 1.280s running