Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

um: always use the internal copy of the FP registers

When switching from userspace to the kernel, all registers including the
FP registers are copied into the kernel and restored later on. As such,
the true source for the FP register state is actually already in the
kernel and they should never be grabbed from the userspace process.

Change the various places to simply copy the data from the internal FP
register storage area. Note that on i386 the format of PTRACE_GETFPREGS
and PTRACE_GETFPXREGS is different enough that conversion would be
needed. With this patch, -EINVAL is returned if the non-native format is
requested.

The upside is, that this patchset fixes setting registers via ptrace
(which simply did not work before) as well as fixing setting floating
point registers using the mcontext on signal return on i386.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Link: https://patch.msgid.link/20240913133845.964292-1-benjamin@sipsolutions.net
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Benjamin Berg and committed by
Johannes Berg
5a695127 865e3845

+80 -84
-6
arch/um/include/shared/registers.h
··· 8 8 9 9 #include <sysdep/ptrace.h> 10 10 11 - extern int save_i387_registers(int pid, unsigned long *fp_regs); 12 - extern int restore_i387_registers(int pid, unsigned long *fp_regs); 13 - extern int save_fp_registers(int pid, unsigned long *fp_regs); 14 - extern int restore_fp_registers(int pid, unsigned long *fp_regs); 15 - extern int save_fpx_registers(int pid, unsigned long *fp_regs); 16 - extern int restore_fpx_registers(int pid, unsigned long *fp_regs); 17 11 extern int init_pid_registers(int pid); 18 12 extern void get_safe_registers(unsigned long *regs, unsigned long *fp_regs); 19 13 extern int get_fp_registers(int pid, unsigned long *regs);
+9 -2
arch/um/kernel/process.c
··· 290 290 291 291 int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu) 292 292 { 293 - int cpu = current_thread_info()->cpu; 293 + #ifdef CONFIG_X86_32 294 + extern int have_fpx_regs; 294 295 295 - return save_i387_registers(userspace_pid[cpu], (unsigned long *) fpu) == 0; 296 + /* FIXME: A plain copy does not work on i386 with have_fpx_regs */ 297 + if (have_fpx_regs) 298 + return 0; 299 + #endif 300 + memcpy(fpu, &t->thread.regs.regs.fp, sizeof(*fpu)); 301 + 302 + return 1; 296 303 } 297 304
+6 -6
arch/x86/um/os-Linux/registers.c
··· 19 19 20 20 static int have_xstate_support; 21 21 22 - int save_i387_registers(int pid, unsigned long *fp_regs) 22 + static int save_i387_registers(int pid, unsigned long *fp_regs) 23 23 { 24 24 if (ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0) 25 25 return -errno; 26 26 return 0; 27 27 } 28 28 29 - int save_fp_registers(int pid, unsigned long *fp_regs) 29 + static int save_fp_registers(int pid, unsigned long *fp_regs) 30 30 { 31 31 #ifdef PTRACE_GETREGSET 32 32 struct iovec iov; ··· 42 42 return save_i387_registers(pid, fp_regs); 43 43 } 44 44 45 - int restore_i387_registers(int pid, unsigned long *fp_regs) 45 + static int restore_i387_registers(int pid, unsigned long *fp_regs) 46 46 { 47 47 if (ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0) 48 48 return -errno; 49 49 return 0; 50 50 } 51 51 52 - int restore_fp_registers(int pid, unsigned long *fp_regs) 52 + static int restore_fp_registers(int pid, unsigned long *fp_regs) 53 53 { 54 54 #ifdef PTRACE_SETREGSET 55 55 struct iovec iov; ··· 66 66 67 67 #ifdef __i386__ 68 68 int have_fpx_regs = 1; 69 - int save_fpx_registers(int pid, unsigned long *fp_regs) 69 + static int save_fpx_registers(int pid, unsigned long *fp_regs) 70 70 { 71 71 if (ptrace(PTRACE_GETFPXREGS, pid, 0, fp_regs) < 0) 72 72 return -errno; 73 73 return 0; 74 74 } 75 75 76 - int restore_fpx_registers(int pid, unsigned long *fp_regs) 76 + static int restore_fpx_registers(int pid, unsigned long *fp_regs) 77 77 { 78 78 if (ptrace(PTRACE_SETFPXREGS, pid, 0, fp_regs) < 0) 79 79 return -errno;
+27 -23
arch/x86/um/ptrace_32.c
··· 168 168 return put_user(tmp, (unsigned long __user *) data); 169 169 } 170 170 171 + /* FIXME: Do the required conversions instead of erroring out */ 172 + extern int have_fpx_regs; 173 + 171 174 static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) 172 175 { 173 - int err, n, cpu = task_cpu(child); 174 - struct user_i387_struct fpregs; 176 + int n; 175 177 176 - err = save_i387_registers(userspace_pid[cpu], 177 - (unsigned long *) &fpregs); 178 - if (err) 179 - return err; 178 + if (have_fpx_regs) 179 + return -EINVAL; 180 180 181 - n = copy_to_user(buf, &fpregs, sizeof(fpregs)); 181 + n = copy_to_user(buf, &child->thread.regs.regs.fp, 182 + sizeof(struct user_i387_struct)); 182 183 if(n > 0) 183 184 return -EFAULT; 184 185 ··· 188 187 189 188 static int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) 190 189 { 191 - int n, cpu = task_cpu(child); 192 - struct user_i387_struct fpregs; 190 + int n; 193 191 194 - n = copy_from_user(&fpregs, buf, sizeof(fpregs)); 192 + if (have_fpx_regs) 193 + return -EINVAL; 194 + 195 + n = copy_from_user(&child->thread.regs.regs.fp, buf, 196 + sizeof(struct user_i387_struct)); 195 197 if (n > 0) 196 198 return -EFAULT; 197 199 198 - return restore_i387_registers(userspace_pid[cpu], 199 - (unsigned long *) &fpregs); 200 + return 0; 200 201 } 201 202 202 203 static int get_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child) 203 204 { 204 - int err, n, cpu = task_cpu(child); 205 - struct user_fxsr_struct fpregs; 205 + int n; 206 206 207 - err = save_fpx_registers(userspace_pid[cpu], (unsigned long *) &fpregs); 208 - if (err) 209 - return err; 207 + if (!have_fpx_regs) 208 + return -EINVAL; 210 209 211 - n = copy_to_user(buf, &fpregs, sizeof(fpregs)); 210 + n = copy_to_user(buf, &child->thread.regs.regs.fp, 211 + sizeof(struct user_fxsr_struct)); 212 212 if(n > 0) 213 213 return -EFAULT; 214 214 ··· 218 216 219 217 static int set_fpxregs(struct user_fxsr_struct __user *buf, struct task_struct *child) 220 218 { 221 - int n, cpu = task_cpu(child); 222 - struct user_fxsr_struct fpregs; 219 + int n; 223 220 224 - n = copy_from_user(&fpregs, buf, sizeof(fpregs)); 221 + if (!have_fpx_regs) 222 + return -EINVAL; 223 + 224 + n = copy_from_user(&child->thread.regs.regs.fp, buf, 225 + sizeof(struct user_fxsr_struct)); 225 226 if (n > 0) 226 227 return -EFAULT; 227 228 228 - return restore_fpx_registers(userspace_pid[cpu], 229 - (unsigned long *) &fpregs); 229 + return 0; 230 230 } 231 231 232 232 long subarch_ptrace(struct task_struct *child, long request,
+7 -13
arch/x86/um/ptrace_64.c
··· 190 190 191 191 static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) 192 192 { 193 - int err, n, cpu = ((struct thread_info *) child->stack)->cpu; 194 - struct user_i387_struct fpregs; 193 + int n; 195 194 196 - err = save_i387_registers(userspace_pid[cpu], 197 - (unsigned long *) &fpregs); 198 - if (err) 199 - return err; 200 - 201 - n = copy_to_user(buf, &fpregs, sizeof(fpregs)); 195 + n = copy_to_user(buf, &child->thread.regs.regs.fp, 196 + sizeof(struct user_i387_struct)); 202 197 if (n > 0) 203 198 return -EFAULT; 204 199 ··· 202 207 203 208 static int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) 204 209 { 205 - int n, cpu = ((struct thread_info *) child->stack)->cpu; 206 - struct user_i387_struct fpregs; 210 + int n; 207 211 208 - n = copy_from_user(&fpregs, buf, sizeof(fpregs)); 212 + n = copy_from_user(&child->thread.regs.regs.fp, buf, 213 + sizeof(struct user_i387_struct)); 209 214 if (n > 0) 210 215 return -EFAULT; 211 216 212 - return restore_i387_registers(userspace_pid[cpu], 213 - (unsigned long *) &fpregs); 217 + return 0; 214 218 } 215 219 216 220 long subarch_ptrace(struct task_struct *child, long request,
+31 -34
arch/x86/um/signal.c
··· 204 204 205 205 #ifdef CONFIG_X86_32 206 206 if (have_fpx_regs) { 207 - struct user_fxsr_struct fpx; 208 - int pid = userspace_pid[current_thread_info()->cpu]; 207 + struct user_fxsr_struct *fpx; 208 + fpx = (void *)&regs->regs.fp; 209 209 210 - err = copy_from_user(&fpx, 211 - &((struct _fpstate __user *)sc.fpstate)->_fxsr_env[0], 212 - sizeof(struct user_fxsr_struct)); 210 + err = convert_fxsr_from_user(fpx, (void *)sc.fpstate); 213 211 if (err) 214 212 return 1; 215 - 216 - err = convert_fxsr_from_user(&fpx, (void *)sc.fpstate); 213 + } else { 214 + BUILD_BUG_ON(sizeof(regs->regs.fp) > sizeof(struct _fpstate)); 215 + err = copy_from_user(regs->regs.fp, (void *)sc.fpstate, 216 + sizeof(regs->regs.fp)); 217 217 if (err) 218 218 return 1; 219 - 220 - err = restore_fpx_registers(pid, (unsigned long *) &fpx); 221 - if (err < 0) { 222 - printk(KERN_ERR "copy_sc_from_user - " 223 - "restore_fpx_registers failed, errno = %d\n", 224 - -err); 225 - return 1; 226 - } 227 - } else 228 - #endif 219 + } 220 + #else 229 221 { 222 + /* FIXME: Save/restore extended state (past the 16 YMM regs) */ 223 + BUILD_BUG_ON(sizeof(regs->regs.fp) < sizeof(struct _xstate)); 224 + 230 225 err = copy_from_user(regs->regs.fp, (void *)sc.fpstate, 231 226 sizeof(struct _xstate)); 232 227 if (err) 233 228 return 1; 234 229 } 230 + #endif 235 231 return 0; 236 232 } 237 233 ··· 287 291 288 292 #ifdef CONFIG_X86_32 289 293 if (have_fpx_regs) { 290 - int pid = userspace_pid[current_thread_info()->cpu]; 291 - struct user_fxsr_struct fpx; 294 + struct user_fxsr_struct *fpx; 292 295 293 - err = save_fpx_registers(pid, (unsigned long *) &fpx); 294 - if (err < 0){ 295 - printk(KERN_ERR "copy_sc_to_user - save_fpx_registers " 296 - "failed, errno = %d\n", err); 297 - return 1; 298 - } 296 + fpx = (void *)&regs->regs.fp; 299 297 300 - err = convert_fxsr_to_user(&to_fp->fpstate, &fpx); 298 + err = convert_fxsr_to_user(&to_fp->fpstate, fpx); 301 299 if (err) 302 300 return 1; 303 301 304 - err |= __put_user(fpx.swd, &to_fp->fpstate.status); 302 + err |= __put_user(fpx->swd, &to_fp->fpstate.status); 305 303 err |= __put_user(X86_FXSR_MAGIC, &to_fp->fpstate.magic); 306 304 if (err) 307 305 return 1; 308 306 309 - if (copy_to_user(&to_fp->fpstate._fxsr_env[0], &fpx, 310 - sizeof(struct user_fxsr_struct))) 307 + } else { 308 + if (copy_to_user(to_fp, regs->regs.fp, sizeof(regs->regs.fp))) 311 309 return 1; 312 - } else 313 - #endif 314 - { 315 - if (copy_to_user(to_fp, regs->regs.fp, sizeof(struct _xstate))) 316 - return 1; 310 + 311 + /* Need to fill in the sw_reserved bits ... */ 312 + BUILD_BUG_ON(offsetof(typeof(*to_fp), 313 + fpstate.sw_reserved.magic1) >= 314 + sizeof(struct _fpstate)); 315 + __put_user(0, &to_fp->fpstate.sw_reserved.magic1); 316 + __put_user(sizeof(struct _fpstate), 317 + &to_fp->fpstate.sw_reserved.extended_size); 317 318 } 319 + #else 320 + if (copy_to_user(to_fp, regs->regs.fp, sizeof(struct _xstate))) 321 + return 1; 322 + #endif 318 323 319 324 return 0; 320 325 }