Skip to content

Commit 21f6f50

Browse files
xinli-inteljfvogel
authored andcommitted
x86/ia32: Leave NULL selector values 0~3 unchanged
[ Upstream commit ad546940b5991d3e141238cd80a6d1894b767184 ] The first GDT descriptor is reserved as 'NULL descriptor'. As bits 0 and 1 of a segment selector, i.e., the RPL bits, are NOT used to index GDT, selector values 0~3 all point to the NULL descriptor, thus values 0, 1, 2 and 3 are all valid NULL selector values. When a NULL selector value is to be loaded into a segment register, reload_segments() sets its RPL bits. Later IRET zeros ES, FS, GS, and DS segment registers if any of them is found to have any nonzero NULL selector value. The two operations offset each other to actually effect a nop. Besides, zeroing of RPL in NULL selector values is an information leak in pre-FRED systems as userspace can spot any interrupt/exception by loading a nonzero NULL selector, and waiting for it to become zero. But there is nothing software can do to prevent it before FRED. ERETU, the only legit instruction to return to userspace from kernel under FRED, by design does NOT zero any segment register to avoid this problem behavior. As such, leave NULL selector values 0~3 unchanged and close the leak. Do the same on 32-bit kernel as well. Signed-off-by: Xin Li (Intel) <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Reviewed-by: Andrew Cooper <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Brian Gerst <[email protected]> Cc: Peter Zijlstra <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sasha Levin <[email protected]> (cherry picked from commit 48e705652db9c222be67c50e303ff53a9f1469ee) Signed-off-by: Jack Vogel <[email protected]>
1 parent a989bf1 commit 21f6f50

File tree

1 file changed

+43
-19
lines changed

1 file changed

+43
-19
lines changed

arch/x86/kernel/signal_32.c

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,55 @@
3333
#include <asm/smap.h>
3434
#include <asm/gsseg.h>
3535

36+
/*
37+
* The first GDT descriptor is reserved as 'NULL descriptor'. As bits 0
38+
* and 1 of a segment selector, i.e., the RPL bits, are NOT used to index
39+
* GDT, selector values 0~3 all point to the NULL descriptor, thus values
40+
* 0, 1, 2 and 3 are all valid NULL selector values.
41+
*
42+
* However IRET zeros ES, FS, GS, and DS segment registers if any of them
43+
* is found to have any nonzero NULL selector value, which can be used by
44+
* userspace in pre-FRED systems to spot any interrupt/exception by loading
45+
* a nonzero NULL selector and waiting for it to become zero. Before FRED
46+
* there was nothing software could do to prevent such an information leak.
47+
*
48+
* ERETU, the only legit instruction to return to userspace from kernel
49+
* under FRED, by design does NOT zero any segment register to avoid this
50+
* problem behavior.
51+
*
52+
* As such, leave NULL selector values 0~3 unchanged.
53+
*/
54+
static inline u16 fixup_rpl(u16 sel)
55+
{
56+
return sel <= 3 ? sel : sel | 3;
57+
}
58+
3659
#ifdef CONFIG_IA32_EMULATION
3760
#include <asm/unistd_32_ia32.h>
3861

3962
static inline void reload_segments(struct sigcontext_32 *sc)
4063
{
41-
unsigned int cur;
64+
u16 cur;
4265

66+
/*
67+
* Reload fs and gs if they have changed in the signal
68+
* handler. This does not handle long fs/gs base changes in
69+
* the handler, but does not clobber them at least in the
70+
* normal case.
71+
*/
4372
savesegment(gs, cur);
44-
if ((sc->gs | 0x03) != cur)
45-
load_gs_index(sc->gs | 0x03);
73+
if (fixup_rpl(sc->gs) != cur)
74+
load_gs_index(fixup_rpl(sc->gs));
4675
savesegment(fs, cur);
47-
if ((sc->fs | 0x03) != cur)
48-
loadsegment(fs, sc->fs | 0x03);
76+
if (fixup_rpl(sc->fs) != cur)
77+
loadsegment(fs, fixup_rpl(sc->fs));
78+
4979
savesegment(ds, cur);
50-
if ((sc->ds | 0x03) != cur)
51-
loadsegment(ds, sc->ds | 0x03);
80+
if (fixup_rpl(sc->ds) != cur)
81+
loadsegment(ds, fixup_rpl(sc->ds));
5282
savesegment(es, cur);
53-
if ((sc->es | 0x03) != cur)
54-
loadsegment(es, sc->es | 0x03);
83+
if (fixup_rpl(sc->es) != cur)
84+
loadsegment(es, fixup_rpl(sc->es));
5585
}
5686

5787
#define sigset32_t compat_sigset_t
@@ -105,18 +135,12 @@ static bool ia32_restore_sigcontext(struct pt_regs *regs,
105135
regs->orig_ax = -1;
106136

107137
#ifdef CONFIG_IA32_EMULATION
108-
/*
109-
* Reload fs and gs if they have changed in the signal
110-
* handler. This does not handle long fs/gs base changes in
111-
* the handler, but does not clobber them at least in the
112-
* normal case.
113-
*/
114138
reload_segments(&sc);
115139
#else
116-
loadsegment(gs, sc.gs);
117-
regs->fs = sc.fs;
118-
regs->es = sc.es;
119-
regs->ds = sc.ds;
140+
loadsegment(gs, fixup_rpl(sc.gs));
141+
regs->fs = fixup_rpl(sc.fs);
142+
regs->es = fixup_rpl(sc.es);
143+
regs->ds = fixup_rpl(sc.ds);
120144
#endif
121145

122146
return fpu__restore_sig(compat_ptr(sc.fpstate), 1);

0 commit comments

Comments
 (0)