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

um: Add helper functions to get/set state for SECCOMP

When not using ptrace, we need to both save and restore registers
through the mcontext as provided by the host kernel to our signal
handlers.

Add corresponding functions to store the state to an mcontext and
helpers to access the mcontext of the subprocess through the stub data.

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

authored by

Benjamin Berg and committed by
Johannes Berg
b1e1bd2e dac494bf

+283 -20
+217 -1
arch/x86/um/os-Linux/mcontext.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 - #include <sys/ucontext.h> 3 2 #define __FRAME_OFFSETS 3 + #include <linux/errno.h> 4 + #include <linux/string.h> 5 + #include <sys/ucontext.h> 4 6 #include <asm/ptrace.h> 7 + #include <asm/sigcontext.h> 5 8 #include <sysdep/ptrace.h> 6 9 #include <sysdep/mcontext.h> 7 10 #include <arch.h> ··· 21 18 COPY2(UESP, ESP); /* sic */ 22 19 COPY(EBX); COPY(EDX); COPY(ECX); COPY(EAX); 23 20 COPY(EIP); COPY_SEG_CPL3(CS); COPY(EFL); COPY_SEG_CPL3(SS); 21 + #undef COPY2 22 + #undef COPY 23 + #undef COPY_SEG 24 + #undef COPY_SEG_CPL3 24 25 #else 25 26 #define COPY2(X,Y) regs->gp[X/sizeof(unsigned long)] = mc->gregs[REG_##Y] 26 27 #define COPY(X) regs->gp[X/sizeof(unsigned long)] = mc->gregs[REG_##X] ··· 36 29 COPY2(EFLAGS, EFL); 37 30 COPY2(CS, CSGSFS); 38 31 regs->gp[SS / sizeof(unsigned long)] = mc->gregs[REG_CSGSFS] >> 48; 32 + #undef COPY2 33 + #undef COPY 39 34 #endif 40 35 } 41 36 ··· 50 41 #else 51 42 mc->gregs[REG_RIP] = (unsigned long)target; 52 43 #endif 44 + } 45 + 46 + /* Same thing, but the copy macros are turned around. */ 47 + void get_mc_from_regs(struct uml_pt_regs *regs, mcontext_t *mc, int single_stepping) 48 + { 49 + #ifdef __i386__ 50 + #define COPY2(X,Y) mc->gregs[REG_##Y] = regs->gp[X] 51 + #define COPY(X) mc->gregs[REG_##X] = regs->gp[X] 52 + #define COPY_SEG(X) mc->gregs[REG_##X] = regs->gp[X] & 0xffff; 53 + #define COPY_SEG_CPL3(X) mc->gregs[REG_##X] = (regs->gp[X] & 0xffff) | 3; 54 + COPY_SEG(GS); COPY_SEG(FS); COPY_SEG(ES); COPY_SEG(DS); 55 + COPY(EDI); COPY(ESI); COPY(EBP); 56 + COPY2(UESP, ESP); /* sic */ 57 + COPY(EBX); COPY(EDX); COPY(ECX); COPY(EAX); 58 + COPY(EIP); COPY_SEG_CPL3(CS); COPY(EFL); COPY_SEG_CPL3(SS); 59 + #else 60 + #define COPY2(X,Y) mc->gregs[REG_##Y] = regs->gp[X/sizeof(unsigned long)] 61 + #define COPY(X) mc->gregs[REG_##X] = regs->gp[X/sizeof(unsigned long)] 62 + COPY(R8); COPY(R9); COPY(R10); COPY(R11); 63 + COPY(R12); COPY(R13); COPY(R14); COPY(R15); 64 + COPY(RDI); COPY(RSI); COPY(RBP); COPY(RBX); 65 + COPY(RDX); COPY(RAX); COPY(RCX); COPY(RSP); 66 + COPY(RIP); 67 + COPY2(EFLAGS, EFL); 68 + mc->gregs[REG_CSGSFS] = mc->gregs[REG_CSGSFS] & 0xffffffffffffl; 69 + mc->gregs[REG_CSGSFS] |= (regs->gp[SS / sizeof(unsigned long)] & 0xffff) << 48; 70 + #endif 71 + 72 + if (single_stepping) 73 + mc->gregs[REG_EFL] |= X86_EFLAGS_TF; 74 + else 75 + mc->gregs[REG_EFL] &= ~X86_EFLAGS_TF; 76 + } 77 + 78 + #ifdef CONFIG_X86_32 79 + struct _xstate_64 { 80 + struct _fpstate_64 fpstate; 81 + struct _header xstate_hdr; 82 + struct _ymmh_state ymmh; 83 + /* New processor state extensions go here: */ 84 + }; 85 + 86 + /* Not quite the right structures as these contain more information */ 87 + int um_i387_from_fxsr(struct _fpstate_32 *i387, 88 + const struct _fpstate_64 *fxsave); 89 + int um_fxsr_from_i387(struct _fpstate_64 *fxsave, 90 + const struct _fpstate_32 *from); 91 + #else 92 + #define _xstate_64 _xstate 93 + #endif 94 + 95 + static struct _fpstate *get_fpstate(struct stub_data *data, 96 + mcontext_t *mcontext, 97 + int *fp_size) 98 + { 99 + struct _fpstate *res; 100 + 101 + /* Assume floating point registers are on the same page */ 102 + res = (void *)(((unsigned long)mcontext->fpregs & 103 + (UM_KERN_PAGE_SIZE - 1)) + 104 + (unsigned long)&data->sigstack[0]); 105 + 106 + if ((void *)res + sizeof(struct _fpstate) > 107 + (void *)data->sigstack + sizeof(data->sigstack)) 108 + return NULL; 109 + 110 + if (res->sw_reserved.magic1 != FP_XSTATE_MAGIC1) { 111 + *fp_size = sizeof(struct _fpstate); 112 + } else { 113 + char *magic2_addr; 114 + 115 + magic2_addr = (void *)res; 116 + magic2_addr += res->sw_reserved.extended_size; 117 + magic2_addr -= FP_XSTATE_MAGIC2_SIZE; 118 + 119 + /* We still need to be within our stack */ 120 + if ((void *)magic2_addr > 121 + (void *)data->sigstack + sizeof(data->sigstack)) 122 + return NULL; 123 + 124 + /* If we do not read MAGIC2, then we did something wrong */ 125 + if (*(__u32 *)magic2_addr != FP_XSTATE_MAGIC2) 126 + return NULL; 127 + 128 + /* Remove MAGIC2 from the size, we do not save/restore it */ 129 + *fp_size = res->sw_reserved.extended_size - 130 + FP_XSTATE_MAGIC2_SIZE; 131 + } 132 + 133 + return res; 134 + } 135 + 136 + int get_stub_state(struct uml_pt_regs *regs, struct stub_data *data, 137 + unsigned long *fp_size_out) 138 + { 139 + mcontext_t *mcontext; 140 + struct _fpstate *fpstate_stub; 141 + struct _xstate_64 *xstate_stub; 142 + int fp_size, xstate_size; 143 + 144 + /* mctx_offset is verified by wait_stub_done_seccomp */ 145 + mcontext = (void *)&data->sigstack[data->mctx_offset]; 146 + 147 + get_regs_from_mc(regs, mcontext); 148 + 149 + fpstate_stub = get_fpstate(data, mcontext, &fp_size); 150 + if (!fpstate_stub) 151 + return -EINVAL; 152 + 153 + #ifdef CONFIG_X86_32 154 + xstate_stub = (void *)&fpstate_stub->_fxsr_env; 155 + xstate_size = fp_size - offsetof(struct _fpstate_32, _fxsr_env); 156 + #else 157 + xstate_stub = (void *)fpstate_stub; 158 + xstate_size = fp_size; 159 + #endif 160 + 161 + if (fp_size_out) 162 + *fp_size_out = xstate_size; 163 + 164 + if (xstate_size > host_fp_size) 165 + return -ENOSPC; 166 + 167 + memcpy(&regs->fp, xstate_stub, xstate_size); 168 + 169 + /* We do not need to read the x86_64 FS_BASE/GS_BASE registers as 170 + * we do not permit userspace to set them directly. 171 + */ 172 + 173 + #ifdef CONFIG_X86_32 174 + /* Read the i387 legacy FP registers */ 175 + if (um_fxsr_from_i387((void *)&regs->fp, fpstate_stub)) 176 + return -EINVAL; 177 + #endif 178 + 179 + return 0; 180 + } 181 + 182 + /* Copied because we cannot include regset.h here. */ 183 + struct task_struct; 184 + struct user_regset; 185 + struct membuf { 186 + void *p; 187 + size_t left; 188 + }; 189 + 190 + int fpregs_legacy_get(struct task_struct *target, 191 + const struct user_regset *regset, 192 + struct membuf to); 193 + 194 + int set_stub_state(struct uml_pt_regs *regs, struct stub_data *data, 195 + int single_stepping) 196 + { 197 + mcontext_t *mcontext; 198 + struct _fpstate *fpstate_stub; 199 + struct _xstate_64 *xstate_stub; 200 + int fp_size, xstate_size; 201 + 202 + /* mctx_offset is verified by wait_stub_done_seccomp */ 203 + mcontext = (void *)&data->sigstack[data->mctx_offset]; 204 + 205 + if ((unsigned long)mcontext < (unsigned long)data->sigstack || 206 + (unsigned long)mcontext > 207 + (unsigned long) data->sigstack + 208 + sizeof(data->sigstack) - sizeof(*mcontext)) 209 + return -EINVAL; 210 + 211 + get_mc_from_regs(regs, mcontext, single_stepping); 212 + 213 + fpstate_stub = get_fpstate(data, mcontext, &fp_size); 214 + if (!fpstate_stub) 215 + return -EINVAL; 216 + 217 + #ifdef CONFIG_X86_32 218 + xstate_stub = (void *)&fpstate_stub->_fxsr_env; 219 + xstate_size = fp_size - offsetof(struct _fpstate_32, _fxsr_env); 220 + #else 221 + xstate_stub = (void *)fpstate_stub; 222 + xstate_size = fp_size; 223 + #endif 224 + 225 + memcpy(fpstate_stub, &regs->fp, fp_size); 226 + 227 + #ifdef __i386__ 228 + /* 229 + * On x86, the GDT entries are updated by arch_set_tls. 230 + */ 231 + 232 + /* Store the i387 legacy FP registers which the host will use */ 233 + if (um_i387_from_fxsr(fpstate_stub, (void *)&regs->fp)) 234 + return -EINVAL; 235 + #else 236 + /* 237 + * On x86_64, we need to sync the FS_BASE/GS_BASE registers using the 238 + * arch specific data. 239 + */ 240 + if (data->arch_data.fs_base != regs->gp[FS_BASE / sizeof(unsigned long)]) { 241 + data->arch_data.fs_base = regs->gp[FS_BASE / sizeof(unsigned long)]; 242 + data->arch_data.sync |= STUB_SYNC_FS_BASE; 243 + } 244 + if (data->arch_data.gs_base != regs->gp[GS_BASE / sizeof(unsigned long)]) { 245 + data->arch_data.gs_base = regs->gp[GS_BASE / sizeof(unsigned long)]; 246 + data->arch_data.sync |= STUB_SYNC_GS_BASE; 247 + } 248 + #endif 249 + 250 + return 0; 53 251 }
+57 -19
arch/x86/um/ptrace.c
··· 25 25 return tmp; 26 26 } 27 27 28 - static inline unsigned long twd_fxsr_to_i387(struct user_fxsr_struct *fxsave) 28 + static inline unsigned long 29 + twd_fxsr_to_i387(const struct user_fxsr_struct *fxsave) 29 30 { 30 31 struct _fpxreg *st = NULL; 31 32 unsigned long twd = (unsigned long) fxsave->twd; ··· 70 69 return ret; 71 70 } 72 71 73 - /* Get/set the old 32bit i387 registers (pre-FPX) */ 74 - static int fpregs_legacy_get(struct task_struct *target, 75 - const struct user_regset *regset, 76 - struct membuf to) 72 + /* 73 + * Get/set the old 32bit i387 registers (pre-FPX) 74 + * 75 + * We provide simple wrappers for mcontext.c, they are only defined locally 76 + * because mcontext.c is userspace facing and needs to a different definition 77 + * of the structures. 78 + */ 79 + static int _um_i387_from_fxsr(struct membuf to, 80 + const struct user_fxsr_struct *fxsave) 77 81 { 78 - struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp; 79 82 int i; 80 83 81 84 membuf_store(&to, (unsigned long)fxsave->cwd | 0xffff0000ul); ··· 96 91 return 0; 97 92 } 98 93 99 - static int fpregs_legacy_set(struct task_struct *target, 94 + int um_i387_from_fxsr(struct user_i387_struct *i387, 95 + const struct user_fxsr_struct *fxsave); 96 + 97 + int um_i387_from_fxsr(struct user_i387_struct *i387, 98 + const struct user_fxsr_struct *fxsave) 99 + { 100 + struct membuf to = { 101 + .p = i387, 102 + .left = sizeof(*i387), 103 + }; 104 + 105 + return _um_i387_from_fxsr(to, fxsave); 106 + } 107 + 108 + static int fpregs_legacy_get(struct task_struct *target, 100 109 const struct user_regset *regset, 101 - unsigned int pos, unsigned int count, 102 - const void *kbuf, const void __user *ubuf) 110 + struct membuf to) 103 111 { 104 112 struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp; 105 - const struct user_i387_struct *from; 106 - struct user_i387_struct buf; 107 - int i; 108 113 109 - if (ubuf) { 110 - if (copy_from_user(&buf, ubuf, sizeof(buf))) 111 - return -EFAULT; 112 - from = &buf; 113 - } else { 114 - from = kbuf; 115 - } 114 + return _um_i387_from_fxsr(to, fxsave); 115 + } 116 + 117 + int um_fxsr_from_i387(struct user_fxsr_struct *fxsave, 118 + const struct user_i387_struct *from); 119 + 120 + int um_fxsr_from_i387(struct user_fxsr_struct *fxsave, 121 + const struct user_i387_struct *from) 122 + { 123 + int i; 116 124 117 125 fxsave->cwd = (unsigned short)(from->cwd & 0xffff); 118 126 fxsave->swd = (unsigned short)(from->swd & 0xffff); ··· 142 124 } 143 125 144 126 return 0; 127 + } 128 + 129 + static int fpregs_legacy_set(struct task_struct *target, 130 + const struct user_regset *regset, 131 + unsigned int pos, unsigned int count, 132 + const void *kbuf, const void __user *ubuf) 133 + { 134 + struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp; 135 + const struct user_i387_struct *from; 136 + struct user_i387_struct buf; 137 + 138 + if (ubuf) { 139 + if (copy_from_user(&buf, ubuf, sizeof(buf))) 140 + return -EFAULT; 141 + from = &buf; 142 + } else { 143 + from = kbuf; 144 + } 145 + 146 + return um_fxsr_from_i387(fxsave, &buf); 145 147 } 146 148 #endif 147 149
+9
arch/x86/um/shared/sysdep/mcontext.h
··· 6 6 #ifndef __SYS_SIGCONTEXT_X86_H 7 7 #define __SYS_SIGCONTEXT_X86_H 8 8 9 + #include <stub-data.h> 10 + 9 11 extern void get_regs_from_mc(struct uml_pt_regs *, mcontext_t *); 12 + extern void get_mc_from_regs(struct uml_pt_regs *regs, mcontext_t *mc, 13 + int single_stepping); 14 + 15 + extern int get_stub_state(struct uml_pt_regs *regs, struct stub_data *data, 16 + unsigned long *fp_size_out); 17 + extern int set_stub_state(struct uml_pt_regs *regs, struct stub_data *data, 18 + int single_stepping); 10 19 11 20 #ifdef __i386__ 12 21