selftests/x86/fsgsbase: Test selectors 1, 2, and 3
authorAndy Lutomirski <luto@kernel.org>
Tue, 1 Aug 2017 14:11:36 +0000 (07:11 -0700)
committerIngo Molnar <mingo@kernel.org>
Thu, 10 Aug 2017 15:15:13 +0000 (17:15 +0200)
Those are funny cases.  Make sure they work.

(Something is screwy with signal handling if a selector is 1, 2, or 3.
Anyone who wants to dive into that rabbit hole is welcome to do so.)

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Borislav Petkov <bpetkov@suse.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Chang Seok <chang.seok.bae@intel.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: stable@vger.kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
tools/testing/selftests/x86/fsgsbase.c

index b4967d8752365545149274cf9e70b7717a826866..f249e042b3b517970733cd32e051f87a8ba740c4 100644 (file)
@@ -285,9 +285,12 @@ static void *threadproc(void *ctx)
        }
 }
 
-static void set_gs_and_switch_to(unsigned long local, unsigned long remote)
+static void set_gs_and_switch_to(unsigned long local,
+                                unsigned short force_sel,
+                                unsigned long remote)
 {
        unsigned long base;
+       unsigned short sel_pre_sched, sel_post_sched;
 
        bool hard_zero = false;
        if (local == HARD_ZERO) {
@@ -297,6 +300,8 @@ static void set_gs_and_switch_to(unsigned long local, unsigned long remote)
 
        printf("[RUN]\tARCH_SET_GS(0x%lx)%s, then schedule to 0x%lx\n",
               local, hard_zero ? " and clear gs" : "", remote);
+       if (force_sel)
+               printf("\tBefore schedule, set selector to 0x%hx\n", force_sel);
        if (syscall(SYS_arch_prctl, ARCH_SET_GS, local) != 0)
                err(1, "ARCH_SET_GS");
        if (hard_zero)
@@ -307,18 +312,35 @@ static void set_gs_and_switch_to(unsigned long local, unsigned long remote)
                printf("[FAIL]\tGSBASE wasn't set as expected\n");
        }
 
+       if (force_sel) {
+               asm volatile ("mov %0, %%gs" : : "rm" (force_sel));
+               sel_pre_sched = force_sel;
+               local = read_base(GS);
+
+               /*
+                * Signal delivery seems to mess up weird selectors.  Put it
+                * back.
+                */
+               asm volatile ("mov %0, %%gs" : : "rm" (force_sel));
+       } else {
+               asm volatile ("mov %%gs, %0" : "=rm" (sel_pre_sched));
+       }
+
        remote_base = remote;
        ftx = 1;
        syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
        while (ftx != 0)
                syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0);
 
+       asm volatile ("mov %%gs, %0" : "=rm" (sel_post_sched));
        base = read_base(GS);
-       if (base == local) {
-               printf("[OK]\tGSBASE remained 0x%lx\n", local);
+       if (base == local && sel_pre_sched == sel_post_sched) {
+               printf("[OK]\tGS/BASE remained 0x%hx/0x%lx\n",
+                      sel_pre_sched, local);
        } else {
                nerrs++;
-               printf("[FAIL]\tGSBASE changed to 0x%lx\n", base);
+               printf("[FAIL]\tGS/BASE changed from 0x%hx/0x%lx to 0x%hx/0x%lx\n",
+                      sel_pre_sched, local, sel_post_sched, base);
        }
 }
 
@@ -381,8 +403,15 @@ int main()
 
        for (int local = 0; local < 4; local++) {
                for (int remote = 0; remote < 4; remote++) {
-                       set_gs_and_switch_to(bases_with_hard_zero[local],
-                                            bases_with_hard_zero[remote]);
+                       for (unsigned short s = 0; s < 5; s++) {
+                               unsigned short sel = s;
+                               if (s == 4)
+                                       asm ("mov %%ss, %0" : "=rm" (sel));
+                               set_gs_and_switch_to(
+                                       bases_with_hard_zero[local],
+                                       sel,
+                                       bases_with_hard_zero[remote]);
+                       }
                }
        }