Skip to content

Commit b1e1bd2

Browse files
benzeajmberg-intel
authored andcommitted
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 <[email protected]> Signed-off-by: Benjamin Berg <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Johannes Berg <[email protected]>
1 parent dac494b commit b1e1bd2

File tree

3 files changed

+283
-20
lines changed

3 files changed

+283
-20
lines changed

arch/x86/um/os-Linux/mcontext.c

Lines changed: 217 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// SPDX-License-Identifier: GPL-2.0
2-
#include <sys/ucontext.h>
32
#define __FRAME_OFFSETS
3+
#include <linux/errno.h>
4+
#include <linux/string.h>
5+
#include <sys/ucontext.h>
46
#include <asm/ptrace.h>
7+
#include <asm/sigcontext.h>
58
#include <sysdep/ptrace.h>
69
#include <sysdep/mcontext.h>
710
#include <arch.h>
@@ -18,6 +21,10 @@ void get_regs_from_mc(struct uml_pt_regs *regs, mcontext_t *mc)
1821
COPY2(UESP, ESP); /* sic */
1922
COPY(EBX); COPY(EDX); COPY(ECX); COPY(EAX);
2023
COPY(EIP); COPY_SEG_CPL3(CS); COPY(EFL); COPY_SEG_CPL3(SS);
24+
#undef COPY2
25+
#undef COPY
26+
#undef COPY_SEG
27+
#undef COPY_SEG_CPL3
2128
#else
2229
#define COPY2(X,Y) regs->gp[X/sizeof(unsigned long)] = mc->gregs[REG_##Y]
2330
#define COPY(X) regs->gp[X/sizeof(unsigned long)] = mc->gregs[REG_##X]
@@ -29,6 +36,8 @@ void get_regs_from_mc(struct uml_pt_regs *regs, mcontext_t *mc)
2936
COPY2(EFLAGS, EFL);
3037
COPY2(CS, CSGSFS);
3138
regs->gp[SS / sizeof(unsigned long)] = mc->gregs[REG_CSGSFS] >> 48;
39+
#undef COPY2
40+
#undef COPY
3241
#endif
3342
}
3443

@@ -42,3 +51,210 @@ void mc_set_rip(void *_mc, void *target)
4251
mc->gregs[REG_RIP] = (unsigned long)target;
4352
#endif
4453
}
54+
55+
/* Same thing, but the copy macros are turned around. */
56+
void get_mc_from_regs(struct uml_pt_regs *regs, mcontext_t *mc, int single_stepping)
57+
{
58+
#ifdef __i386__
59+
#define COPY2(X,Y) mc->gregs[REG_##Y] = regs->gp[X]
60+
#define COPY(X) mc->gregs[REG_##X] = regs->gp[X]
61+
#define COPY_SEG(X) mc->gregs[REG_##X] = regs->gp[X] & 0xffff;
62+
#define COPY_SEG_CPL3(X) mc->gregs[REG_##X] = (regs->gp[X] & 0xffff) | 3;
63+
COPY_SEG(GS); COPY_SEG(FS); COPY_SEG(ES); COPY_SEG(DS);
64+
COPY(EDI); COPY(ESI); COPY(EBP);
65+
COPY2(UESP, ESP); /* sic */
66+
COPY(EBX); COPY(EDX); COPY(ECX); COPY(EAX);
67+
COPY(EIP); COPY_SEG_CPL3(CS); COPY(EFL); COPY_SEG_CPL3(SS);
68+
#else
69+
#define COPY2(X,Y) mc->gregs[REG_##Y] = regs->gp[X/sizeof(unsigned long)]
70+
#define COPY(X) mc->gregs[REG_##X] = regs->gp[X/sizeof(unsigned long)]
71+
COPY(R8); COPY(R9); COPY(R10); COPY(R11);
72+
COPY(R12); COPY(R13); COPY(R14); COPY(R15);
73+
COPY(RDI); COPY(RSI); COPY(RBP); COPY(RBX);
74+
COPY(RDX); COPY(RAX); COPY(RCX); COPY(RSP);
75+
COPY(RIP);
76+
COPY2(EFLAGS, EFL);
77+
mc->gregs[REG_CSGSFS] = mc->gregs[REG_CSGSFS] & 0xffffffffffffl;
78+
mc->gregs[REG_CSGSFS] |= (regs->gp[SS / sizeof(unsigned long)] & 0xffff) << 48;
79+
#endif
80+
81+
if (single_stepping)
82+
mc->gregs[REG_EFL] |= X86_EFLAGS_TF;
83+
else
84+
mc->gregs[REG_EFL] &= ~X86_EFLAGS_TF;
85+
}
86+
87+
#ifdef CONFIG_X86_32
88+
struct _xstate_64 {
89+
struct _fpstate_64 fpstate;
90+
struct _header xstate_hdr;
91+
struct _ymmh_state ymmh;
92+
/* New processor state extensions go here: */
93+
};
94+
95+
/* Not quite the right structures as these contain more information */
96+
int um_i387_from_fxsr(struct _fpstate_32 *i387,
97+
const struct _fpstate_64 *fxsave);
98+
int um_fxsr_from_i387(struct _fpstate_64 *fxsave,
99+
const struct _fpstate_32 *from);
100+
#else
101+
#define _xstate_64 _xstate
102+
#endif
103+
104+
static struct _fpstate *get_fpstate(struct stub_data *data,
105+
mcontext_t *mcontext,
106+
int *fp_size)
107+
{
108+
struct _fpstate *res;
109+
110+
/* Assume floating point registers are on the same page */
111+
res = (void *)(((unsigned long)mcontext->fpregs &
112+
(UM_KERN_PAGE_SIZE - 1)) +
113+
(unsigned long)&data->sigstack[0]);
114+
115+
if ((void *)res + sizeof(struct _fpstate) >
116+
(void *)data->sigstack + sizeof(data->sigstack))
117+
return NULL;
118+
119+
if (res->sw_reserved.magic1 != FP_XSTATE_MAGIC1) {
120+
*fp_size = sizeof(struct _fpstate);
121+
} else {
122+
char *magic2_addr;
123+
124+
magic2_addr = (void *)res;
125+
magic2_addr += res->sw_reserved.extended_size;
126+
magic2_addr -= FP_XSTATE_MAGIC2_SIZE;
127+
128+
/* We still need to be within our stack */
129+
if ((void *)magic2_addr >
130+
(void *)data->sigstack + sizeof(data->sigstack))
131+
return NULL;
132+
133+
/* If we do not read MAGIC2, then we did something wrong */
134+
if (*(__u32 *)magic2_addr != FP_XSTATE_MAGIC2)
135+
return NULL;
136+
137+
/* Remove MAGIC2 from the size, we do not save/restore it */
138+
*fp_size = res->sw_reserved.extended_size -
139+
FP_XSTATE_MAGIC2_SIZE;
140+
}
141+
142+
return res;
143+
}
144+
145+
int get_stub_state(struct uml_pt_regs *regs, struct stub_data *data,
146+
unsigned long *fp_size_out)
147+
{
148+
mcontext_t *mcontext;
149+
struct _fpstate *fpstate_stub;
150+
struct _xstate_64 *xstate_stub;
151+
int fp_size, xstate_size;
152+
153+
/* mctx_offset is verified by wait_stub_done_seccomp */
154+
mcontext = (void *)&data->sigstack[data->mctx_offset];
155+
156+
get_regs_from_mc(regs, mcontext);
157+
158+
fpstate_stub = get_fpstate(data, mcontext, &fp_size);
159+
if (!fpstate_stub)
160+
return -EINVAL;
161+
162+
#ifdef CONFIG_X86_32
163+
xstate_stub = (void *)&fpstate_stub->_fxsr_env;
164+
xstate_size = fp_size - offsetof(struct _fpstate_32, _fxsr_env);
165+
#else
166+
xstate_stub = (void *)fpstate_stub;
167+
xstate_size = fp_size;
168+
#endif
169+
170+
if (fp_size_out)
171+
*fp_size_out = xstate_size;
172+
173+
if (xstate_size > host_fp_size)
174+
return -ENOSPC;
175+
176+
memcpy(&regs->fp, xstate_stub, xstate_size);
177+
178+
/* We do not need to read the x86_64 FS_BASE/GS_BASE registers as
179+
* we do not permit userspace to set them directly.
180+
*/
181+
182+
#ifdef CONFIG_X86_32
183+
/* Read the i387 legacy FP registers */
184+
if (um_fxsr_from_i387((void *)&regs->fp, fpstate_stub))
185+
return -EINVAL;
186+
#endif
187+
188+
return 0;
189+
}
190+
191+
/* Copied because we cannot include regset.h here. */
192+
struct task_struct;
193+
struct user_regset;
194+
struct membuf {
195+
void *p;
196+
size_t left;
197+
};
198+
199+
int fpregs_legacy_get(struct task_struct *target,
200+
const struct user_regset *regset,
201+
struct membuf to);
202+
203+
int set_stub_state(struct uml_pt_regs *regs, struct stub_data *data,
204+
int single_stepping)
205+
{
206+
mcontext_t *mcontext;
207+
struct _fpstate *fpstate_stub;
208+
struct _xstate_64 *xstate_stub;
209+
int fp_size, xstate_size;
210+
211+
/* mctx_offset is verified by wait_stub_done_seccomp */
212+
mcontext = (void *)&data->sigstack[data->mctx_offset];
213+
214+
if ((unsigned long)mcontext < (unsigned long)data->sigstack ||
215+
(unsigned long)mcontext >
216+
(unsigned long) data->sigstack +
217+
sizeof(data->sigstack) - sizeof(*mcontext))
218+
return -EINVAL;
219+
220+
get_mc_from_regs(regs, mcontext, single_stepping);
221+
222+
fpstate_stub = get_fpstate(data, mcontext, &fp_size);
223+
if (!fpstate_stub)
224+
return -EINVAL;
225+
226+
#ifdef CONFIG_X86_32
227+
xstate_stub = (void *)&fpstate_stub->_fxsr_env;
228+
xstate_size = fp_size - offsetof(struct _fpstate_32, _fxsr_env);
229+
#else
230+
xstate_stub = (void *)fpstate_stub;
231+
xstate_size = fp_size;
232+
#endif
233+
234+
memcpy(fpstate_stub, &regs->fp, fp_size);
235+
236+
#ifdef __i386__
237+
/*
238+
* On x86, the GDT entries are updated by arch_set_tls.
239+
*/
240+
241+
/* Store the i387 legacy FP registers which the host will use */
242+
if (um_i387_from_fxsr(fpstate_stub, (void *)&regs->fp))
243+
return -EINVAL;
244+
#else
245+
/*
246+
* On x86_64, we need to sync the FS_BASE/GS_BASE registers using the
247+
* arch specific data.
248+
*/
249+
if (data->arch_data.fs_base != regs->gp[FS_BASE / sizeof(unsigned long)]) {
250+
data->arch_data.fs_base = regs->gp[FS_BASE / sizeof(unsigned long)];
251+
data->arch_data.sync |= STUB_SYNC_FS_BASE;
252+
}
253+
if (data->arch_data.gs_base != regs->gp[GS_BASE / sizeof(unsigned long)]) {
254+
data->arch_data.gs_base = regs->gp[GS_BASE / sizeof(unsigned long)];
255+
data->arch_data.sync |= STUB_SYNC_GS_BASE;
256+
}
257+
#endif
258+
259+
return 0;
260+
}

arch/x86/um/ptrace.c

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
2525
return tmp;
2626
}
2727

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)
2930
{
3031
struct _fpxreg *st = NULL;
3132
unsigned long twd = (unsigned long) fxsave->twd;
@@ -69,12 +70,16 @@ static inline unsigned long twd_fxsr_to_i387(struct user_fxsr_struct *fxsave)
6970
return ret;
7071
}
7172

72-
/* Get/set the old 32bit i387 registers (pre-FPX) */
73-
static int fpregs_legacy_get(struct task_struct *target,
74-
const struct user_regset *regset,
75-
struct membuf to)
73+
/*
74+
* Get/set the old 32bit i387 registers (pre-FPX)
75+
*
76+
* We provide simple wrappers for mcontext.c, they are only defined locally
77+
* because mcontext.c is userspace facing and needs to a different definition
78+
* of the structures.
79+
*/
80+
static int _um_i387_from_fxsr(struct membuf to,
81+
const struct user_fxsr_struct *fxsave)
7682
{
77-
struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
7883
int i;
7984

8085
membuf_store(&to, (unsigned long)fxsave->cwd | 0xffff0000ul);
@@ -91,23 +96,36 @@ static int fpregs_legacy_get(struct task_struct *target,
9196
return 0;
9297
}
9398

94-
static int fpregs_legacy_set(struct task_struct *target,
99+
int um_i387_from_fxsr(struct user_i387_struct *i387,
100+
const struct user_fxsr_struct *fxsave);
101+
102+
int um_i387_from_fxsr(struct user_i387_struct *i387,
103+
const struct user_fxsr_struct *fxsave)
104+
{
105+
struct membuf to = {
106+
.p = i387,
107+
.left = sizeof(*i387),
108+
};
109+
110+
return _um_i387_from_fxsr(to, fxsave);
111+
}
112+
113+
static int fpregs_legacy_get(struct task_struct *target,
95114
const struct user_regset *regset,
96-
unsigned int pos, unsigned int count,
97-
const void *kbuf, const void __user *ubuf)
115+
struct membuf to)
98116
{
99117
struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
100-
const struct user_i387_struct *from;
101-
struct user_i387_struct buf;
102-
int i;
103118

104-
if (ubuf) {
105-
if (copy_from_user(&buf, ubuf, sizeof(buf)))
106-
return -EFAULT;
107-
from = &buf;
108-
} else {
109-
from = kbuf;
110-
}
119+
return _um_i387_from_fxsr(to, fxsave);
120+
}
121+
122+
int um_fxsr_from_i387(struct user_fxsr_struct *fxsave,
123+
const struct user_i387_struct *from);
124+
125+
int um_fxsr_from_i387(struct user_fxsr_struct *fxsave,
126+
const struct user_i387_struct *from)
127+
{
128+
int i;
111129

112130
fxsave->cwd = (unsigned short)(from->cwd & 0xffff);
113131
fxsave->swd = (unsigned short)(from->swd & 0xffff);
@@ -125,6 +143,26 @@ static int fpregs_legacy_set(struct task_struct *target,
125143

126144
return 0;
127145
}
146+
147+
static int fpregs_legacy_set(struct task_struct *target,
148+
const struct user_regset *regset,
149+
unsigned int pos, unsigned int count,
150+
const void *kbuf, const void __user *ubuf)
151+
{
152+
struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
153+
const struct user_i387_struct *from;
154+
struct user_i387_struct buf;
155+
156+
if (ubuf) {
157+
if (copy_from_user(&buf, ubuf, sizeof(buf)))
158+
return -EFAULT;
159+
from = &buf;
160+
} else {
161+
from = kbuf;
162+
}
163+
164+
return um_fxsr_from_i387(fxsave, &buf);
165+
}
128166
#endif
129167

130168
static int genregs_get(struct task_struct *target,

arch/x86/um/shared/sysdep/mcontext.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,16 @@
66
#ifndef __SYS_SIGCONTEXT_X86_H
77
#define __SYS_SIGCONTEXT_X86_H
88

9+
#include <stub-data.h>
10+
911
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);
1019

1120
#ifdef __i386__
1221

0 commit comments

Comments
 (0)