|
33 | 33 | #include <asm/smap.h>
|
34 | 34 | #include <asm/gsseg.h>
|
35 | 35 |
|
| 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 | + |
36 | 59 | #ifdef CONFIG_IA32_EMULATION
|
37 | 60 | #include <asm/unistd_32_ia32.h>
|
38 | 61 |
|
39 | 62 | static inline void reload_segments(struct sigcontext_32 *sc)
|
40 | 63 | {
|
41 |
| - unsigned int cur; |
| 64 | + u16 cur; |
42 | 65 |
|
| 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 | + */ |
43 | 72 | 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)); |
46 | 75 | 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 | + |
49 | 79 | 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)); |
52 | 82 | 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)); |
55 | 85 | }
|
56 | 86 |
|
57 | 87 | #define sigset32_t compat_sigset_t
|
@@ -105,18 +135,12 @@ static bool ia32_restore_sigcontext(struct pt_regs *regs,
|
105 | 135 | regs->orig_ax = -1;
|
106 | 136 |
|
107 | 137 | #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 |
| - */ |
114 | 138 | reload_segments(&sc);
|
115 | 139 | #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); |
120 | 144 | #endif
|
121 | 145 |
|
122 | 146 | return fpu__restore_sig(compat_ptr(sc.fpstate), 1);
|
|
0 commit comments