|
@@ -61,6 +61,35 @@
|
|
|
regs->seg = GET_SEG(seg) | 3; \
|
|
|
} while (0)
|
|
|
|
|
|
+#ifdef CONFIG_X86_64
|
|
|
+/*
|
|
|
+ * If regs->ss will cause an IRET fault, change it. Otherwise leave it
|
|
|
+ * alone. Using this generally makes no sense unless
|
|
|
+ * user_64bit_mode(regs) would return true.
|
|
|
+ */
|
|
|
+static void force_valid_ss(struct pt_regs *regs)
|
|
|
+{
|
|
|
+ u32 ar;
|
|
|
+ asm volatile ("lar %[old_ss], %[ar]\n\t"
|
|
|
+ "jz 1f\n\t" /* If invalid: */
|
|
|
+ "xorl %[ar], %[ar]\n\t" /* set ar = 0 */
|
|
|
+ "1:"
|
|
|
+ : [ar] "=r" (ar)
|
|
|
+ : [old_ss] "rm" ((u16)regs->ss));
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For a valid 64-bit user context, we need DPL 3, type
|
|
|
+ * read-write data or read-write exp-down data, and S and P
|
|
|
+ * set. We can't use VERW because VERW doesn't check the
|
|
|
+ * P bit.
|
|
|
+ */
|
|
|
+ ar &= AR_DPL_MASK | AR_S | AR_P | AR_TYPE_MASK;
|
|
|
+ if (ar != (AR_DPL3 | AR_S | AR_P | AR_TYPE_RWDATA) &&
|
|
|
+ ar != (AR_DPL3 | AR_S | AR_P | AR_TYPE_RWDATA_EXPDOWN))
|
|
|
+ regs->ss = __USER_DS;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
|
|
|
{
|
|
|
unsigned long buf_val;
|
|
@@ -459,10 +488,28 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig,
|
|
|
|
|
|
regs->sp = (unsigned long)frame;
|
|
|
|
|
|
- /* Set up the CS register to run signal handlers in 64-bit mode,
|
|
|
- even if the handler happens to be interrupting 32-bit code. */
|
|
|
+ /*
|
|
|
+ * Set up the CS and SS registers to run signal handlers in
|
|
|
+ * 64-bit mode, even if the handler happens to be interrupting
|
|
|
+ * 32-bit or 16-bit code.
|
|
|
+ *
|
|
|
+ * SS is subtle. In 64-bit mode, we don't need any particular
|
|
|
+ * SS descriptor, but we do need SS to be valid. It's possible
|
|
|
+ * that the old SS is entirely bogus -- this can happen if the
|
|
|
+ * signal we're trying to deliver is #GP or #SS caused by a bad
|
|
|
+ * SS value. We also have a compatbility issue here: DOSEMU
|
|
|
+ * relies on the contents of the SS register indicating the
|
|
|
+ * SS value at the time of the signal, even though that code in
|
|
|
+ * DOSEMU predates sigreturn's ability to restore SS. (DOSEMU
|
|
|
+ * avoids relying on sigreturn to restore SS; instead it uses
|
|
|
+ * a trampoline.) So we do our best: if the old SS was valid,
|
|
|
+ * we keep it. Otherwise we replace it.
|
|
|
+ */
|
|
|
regs->cs = __USER_CS;
|
|
|
|
|
|
+ if (unlikely(regs->ss != __USER_DS))
|
|
|
+ force_valid_ss(regs);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
#endif /* CONFIG_X86_32 */
|