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

x86/signal/64: Fix SS if needed when delivering a 64-bit signal

Signals are always delivered to 64-bit tasks with CS set to a long
mode segment. In long mode, SS doesn't matter as long as it's a
present writable segment.

If SS starts out invalid (this can happen if the signal was caused
by an IRET fault or was delivered on the way out of set_thread_area
or modify_ldt), then IRET to the signal handler can fail, eventually
killing the task.

The straightforward fix would be to simply reset SS when delivering
a signal. That breaks DOSEMU, though: 64-bit builds of DOSEMU rely
on SS being set to the faulting SS when signals are delivered.

As a compromise, this patch leaves SS alone so long as it's valid.

The net effect should be that the behavior of successfully delivered
signals is unchanged. Some signals that would previously have
failed to be delivered will now be delivered successfully.

This has no effect for x32 or 32-bit tasks: their signal handlers
were already called with SS == __USER_DS.

(On Xen, there's a slight hole: if a task sets SS to a writable
*kernel* data segment, then we will fail to identify it as invalid
and we'll still kill the task. If anyone cares, this could be fixed
with a new paravirt hook.)

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Acked-by: Borislav Petkov <bp@alien8.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stas Sergeev <stsp@list.ru>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/163c6e1eacde41388f3ff4d2fe6769be651d7b6e.1455664054.git.luto@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Andy Lutomirski and committed by
Ingo Molnar
8ff5bd2e e54fdcca

+72 -2
+23
arch/x86/include/asm/desc_defs.h
··· 98 98 99 99 #endif /* !__ASSEMBLY__ */ 100 100 101 + /* Access rights as returned by LAR */ 102 + #define AR_TYPE_RODATA (0 * (1 << 9)) 103 + #define AR_TYPE_RWDATA (1 * (1 << 9)) 104 + #define AR_TYPE_RODATA_EXPDOWN (2 * (1 << 9)) 105 + #define AR_TYPE_RWDATA_EXPDOWN (3 * (1 << 9)) 106 + #define AR_TYPE_XOCODE (4 * (1 << 9)) 107 + #define AR_TYPE_XRCODE (5 * (1 << 9)) 108 + #define AR_TYPE_XOCODE_CONF (6 * (1 << 9)) 109 + #define AR_TYPE_XRCODE_CONF (7 * (1 << 9)) 110 + #define AR_TYPE_MASK (7 * (1 << 9)) 111 + 112 + #define AR_DPL0 (0 * (1 << 13)) 113 + #define AR_DPL3 (3 * (1 << 13)) 114 + #define AR_DPL_MASK (3 * (1 << 13)) 115 + 116 + #define AR_A (1 << 8) /* "Accessed" */ 117 + #define AR_S (1 << 12) /* If clear, "System" segment */ 118 + #define AR_P (1 << 15) /* "Present" */ 119 + #define AR_AVL (1 << 20) /* "AVaiLable" (no HW effect) */ 120 + #define AR_L (1 << 21) /* "Long mode" for code segments */ 121 + #define AR_DB (1 << 22) /* D/B, effect depends on type */ 122 + #define AR_G (1 << 23) /* "Granularity" (limit in pages) */ 123 + 101 124 #endif /* _ASM_X86_DESC_DEFS_H */
+49 -2
arch/x86/kernel/signal.c
··· 61 61 regs->seg = GET_SEG(seg) | 3; \ 62 62 } while (0) 63 63 64 + #ifdef CONFIG_X86_64 65 + /* 66 + * If regs->ss will cause an IRET fault, change it. Otherwise leave it 67 + * alone. Using this generally makes no sense unless 68 + * user_64bit_mode(regs) would return true. 69 + */ 70 + static void force_valid_ss(struct pt_regs *regs) 71 + { 72 + u32 ar; 73 + asm volatile ("lar %[old_ss], %[ar]\n\t" 74 + "jz 1f\n\t" /* If invalid: */ 75 + "xorl %[ar], %[ar]\n\t" /* set ar = 0 */ 76 + "1:" 77 + : [ar] "=r" (ar) 78 + : [old_ss] "rm" ((u16)regs->ss)); 79 + 80 + /* 81 + * For a valid 64-bit user context, we need DPL 3, type 82 + * read-write data or read-write exp-down data, and S and P 83 + * set. We can't use VERW because VERW doesn't check the 84 + * P bit. 85 + */ 86 + ar &= AR_DPL_MASK | AR_S | AR_P | AR_TYPE_MASK; 87 + if (ar != (AR_DPL3 | AR_S | AR_P | AR_TYPE_RWDATA) && 88 + ar != (AR_DPL3 | AR_S | AR_P | AR_TYPE_RWDATA_EXPDOWN)) 89 + regs->ss = __USER_DS; 90 + } 91 + #endif 92 + 64 93 int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) 65 94 { 66 95 unsigned long buf_val; ··· 488 459 489 460 regs->sp = (unsigned long)frame; 490 461 491 - /* Set up the CS register to run signal handlers in 64-bit mode, 492 - even if the handler happens to be interrupting 32-bit code. */ 462 + /* 463 + * Set up the CS and SS registers to run signal handlers in 464 + * 64-bit mode, even if the handler happens to be interrupting 465 + * 32-bit or 16-bit code. 466 + * 467 + * SS is subtle. In 64-bit mode, we don't need any particular 468 + * SS descriptor, but we do need SS to be valid. It's possible 469 + * that the old SS is entirely bogus -- this can happen if the 470 + * signal we're trying to deliver is #GP or #SS caused by a bad 471 + * SS value. We also have a compatbility issue here: DOSEMU 472 + * relies on the contents of the SS register indicating the 473 + * SS value at the time of the signal, even though that code in 474 + * DOSEMU predates sigreturn's ability to restore SS. (DOSEMU 475 + * avoids relying on sigreturn to restore SS; instead it uses 476 + * a trampoline.) So we do our best: if the old SS was valid, 477 + * we keep it. Otherwise we replace it. 478 + */ 493 479 regs->cs = __USER_CS; 480 + 481 + if (unlikely(regs->ss != __USER_DS)) 482 + force_valid_ss(regs); 494 483 495 484 return 0; 496 485 }