aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@kernel.org>2018-06-27 01:17:17 -0400
committerIngo Molnar <mingo@kernel.org>2018-06-27 03:36:56 -0400
commitec348020566009d3da9b99f07c05814d13969c78 (patch)
tree14c164f98353e3e4fa1aa9f959448e423ef82b14 /tools
parent22cd978e598618e82c3c3348d2069184f6884182 (diff)
selftests/x86/sigreturn/64: Fix spurious failures on AMD CPUs
When I wrote the sigreturn test, I didn't realize that AMD's busted IRET behavior was different from Intel's busted IRET behavior: On AMD CPUs, the CPU leaks the high 32 bits of the kernel stack pointer to certain userspace contexts. Gee, thanks. There's very little the kernel can do about it. Modify the test so it passes. Signed-off-by: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/86e7fd3564497f657de30a36da4505799eebef01.1530076529.git.luto@kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/testing/selftests/x86/sigreturn.c46
1 files changed, 29 insertions, 17 deletions
diff --git a/tools/testing/selftests/x86/sigreturn.c b/tools/testing/selftests/x86/sigreturn.c
index 246145b84a12..2559e2c01793 100644
--- a/tools/testing/selftests/x86/sigreturn.c
+++ b/tools/testing/selftests/x86/sigreturn.c
@@ -612,19 +612,38 @@ static int test_valid_sigreturn(int cs_bits, bool use_16bit_ss, int force_ss)
612 greg_t req = requested_regs[i], res = resulting_regs[i]; 612 greg_t req = requested_regs[i], res = resulting_regs[i];
613 if (i == REG_TRAPNO || i == REG_IP) 613 if (i == REG_TRAPNO || i == REG_IP)
614 continue; /* don't care */ 614 continue; /* don't care */
615 if (i == REG_SP) {
616 printf("\tSP: %llx -> %llx\n", (unsigned long long)req,
617 (unsigned long long)res);
618 615
616 if (i == REG_SP) {
619 /* 617 /*
620 * In many circumstances, the high 32 bits of rsp 618 * If we were using a 16-bit stack segment, then
621 * are zeroed. For example, we could be a real 619 * the kernel is a bit stuck: IRET only restores
622 * 32-bit program, or we could hit any of a number 620 * the low 16 bits of ESP/RSP if SS is 16-bit.
623 * of poorly-documented IRET or segmented ESP 621 * The kernel uses a hack to restore bits 31:16,
624 * oddities. If this happens, it's okay. 622 * but that hack doesn't help with bits 63:32.
623 * On Intel CPUs, bits 63:32 end up zeroed, and, on
624 * AMD CPUs, they leak the high bits of the kernel
625 * espfix64 stack pointer. There's very little that
626 * the kernel can do about it.
627 *
628 * Similarly, if we are returning to a 32-bit context,
629 * the CPU will often lose the high 32 bits of RSP.
625 */ 630 */
626 if (res == (req & 0xFFFFFFFF)) 631
627 continue; /* OK; not expected to work */ 632 if (res == req)
633 continue;
634
635 if (cs_bits != 64 && ((res ^ req) & 0xFFFFFFFF) == 0) {
636 printf("[NOTE]\tSP: %llx -> %llx\n",
637 (unsigned long long)req,
638 (unsigned long long)res);
639 continue;
640 }
641
642 printf("[FAIL]\tSP mismatch: requested 0x%llx; got 0x%llx\n",
643 (unsigned long long)requested_regs[i],
644 (unsigned long long)resulting_regs[i]);
645 nerrs++;
646 continue;
628 } 647 }
629 648
630 bool ignore_reg = false; 649 bool ignore_reg = false;
@@ -663,13 +682,6 @@ static int test_valid_sigreturn(int cs_bits, bool use_16bit_ss, int force_ss)
663 } 682 }
664 683
665 if (requested_regs[i] != resulting_regs[i] && !ignore_reg) { 684 if (requested_regs[i] != resulting_regs[i] && !ignore_reg) {
666 /*
667 * SP is particularly interesting here. The
668 * usual cause of failures is that we hit the
669 * nasty IRET case of returning to a 16-bit SS,
670 * in which case bits 16:31 of the *kernel*
671 * stack pointer persist in ESP.
672 */
673 printf("[FAIL]\tReg %d mismatch: requested 0x%llx; got 0x%llx\n", 685 printf("[FAIL]\tReg %d mismatch: requested 0x%llx; got 0x%llx\n",
674 i, (unsigned long long)requested_regs[i], 686 i, (unsigned long long)requested_regs[i],
675 (unsigned long long)resulting_regs[i]); 687 (unsigned long long)resulting_regs[i]);