diff options
| -rw-r--r-- | tools/testing/selftests/x86/sigreturn.c | 46 |
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]); |
