]>
Commit | Line | Data |
---|---|---|
fcb9b3db SL |
1 | From 15b5e7c4808e86d9ffd15b759af8686fd1dfaa5f Mon Sep 17 00:00:00 2001 |
2 | From: Andy Lutomirski <luto@kernel.org> | |
3 | Date: Thu, 30 Nov 2017 07:57:57 -0800 | |
4 | Subject: x86/power: Fix some ordering bugs in __restore_processor_context() | |
5 | ||
6 | [ Upstream commit 5b06bbcfc2c621da3009da8decb7511500c293ed ] | |
7 | ||
8 | __restore_processor_context() had a couple of ordering bugs. It | |
9 | restored GSBASE after calling load_gs_index(), and the latter can | |
10 | call into tracing code. It also tried to restore segment registers | |
11 | before restoring the LDT, which is straight-up wrong. | |
12 | ||
13 | Reorder the code so that we restore GSBASE, then the descriptor | |
14 | tables, then the segments. | |
15 | ||
16 | This fixes two bugs. First, it fixes a regression that broke resume | |
17 | under certain configurations due to irqflag tracing in | |
18 | native_load_gs_index(). Second, it fixes resume when the userspace | |
19 | process that initiated suspect had funny segments. The latter can be | |
20 | reproduced by compiling this: | |
21 | ||
22 | // SPDX-License-Identifier: GPL-2.0 | |
23 | /* | |
24 | * ldt_echo.c - Echo argv[1] while using an LDT segment | |
25 | */ | |
26 | ||
27 | int main(int argc, char **argv) | |
28 | { | |
29 | int ret; | |
30 | size_t len; | |
31 | char *buf; | |
32 | ||
33 | const struct user_desc desc = { | |
34 | .entry_number = 0, | |
35 | .base_addr = 0, | |
36 | .limit = 0xfffff, | |
37 | .seg_32bit = 1, | |
38 | .contents = 0, /* Data, grow-up */ | |
39 | .read_exec_only = 0, | |
40 | .limit_in_pages = 1, | |
41 | .seg_not_present = 0, | |
42 | .useable = 0 | |
43 | }; | |
44 | ||
45 | if (argc != 2) | |
46 | errx(1, "Usage: %s STRING", argv[0]); | |
47 | ||
48 | len = asprintf(&buf, "%s\n", argv[1]); | |
49 | if (len < 0) | |
50 | errx(1, "Out of memory"); | |
51 | ||
52 | ret = syscall(SYS_modify_ldt, 1, &desc, sizeof(desc)); | |
53 | if (ret < -1) | |
54 | errno = -ret; | |
55 | if (ret) | |
56 | err(1, "modify_ldt"); | |
57 | ||
58 | asm volatile ("movw %0, %%es" :: "rm" ((unsigned short)7)); | |
59 | write(1, buf, len); | |
60 | return 0; | |
61 | } | |
62 | ||
63 | and running ldt_echo >/sys/power/mem | |
64 | ||
65 | Without the fix, the latter causes a triple fault on resume. | |
66 | ||
67 | Fixes: ca37e57bbe0c ("x86/entry/64: Add missing irqflags tracing to native_load_gs_index()") | |
68 | Reported-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> | |
69 | Signed-off-by: Andy Lutomirski <luto@kernel.org> | |
70 | Signed-off-by: Thomas Gleixner <tglx@linutronix.de> | |
71 | Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> | |
72 | Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> | |
73 | Cc: Borislav Petkov <bp@alien8.de> | |
74 | Cc: Linus Torvalds <torvalds@linux-foundation.org> | |
75 | Link: https://lkml.kernel.org/r/6b31721ea92f51ea839e79bd97ade4a75b1eeea2.1512057304.git.luto@kernel.org | |
76 | Signed-off-by: Ingo Molnar <mingo@kernel.org> | |
77 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
78 | --- | |
79 | arch/x86/power/cpu.c | 21 +++++++++++++++++---- | |
80 | 1 file changed, 17 insertions(+), 4 deletions(-) | |
81 | ||
82 | diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c | |
83 | index 04d5157fe7f8..a51d2dfb57d1 100644 | |
84 | --- a/arch/x86/power/cpu.c | |
85 | +++ b/arch/x86/power/cpu.c | |
86 | @@ -228,8 +228,20 @@ static void notrace __restore_processor_state(struct saved_context *ctxt) | |
87 | load_idt((const struct desc_ptr *)&ctxt->idt_limit); | |
88 | #endif | |
89 | ||
90 | +#ifdef CONFIG_X86_64 | |
91 | /* | |
92 | - * segment registers | |
93 | + * We need GSBASE restored before percpu access can work. | |
94 | + * percpu access can happen in exception handlers or in complicated | |
95 | + * helpers like load_gs_index(). | |
96 | + */ | |
97 | + wrmsrl(MSR_GS_BASE, ctxt->gs_base); | |
98 | +#endif | |
99 | + | |
100 | + fix_processor_context(); | |
101 | + | |
102 | + /* | |
103 | + * Restore segment registers. This happens after restoring the GDT | |
104 | + * and LDT, which happen in fix_processor_context(). | |
105 | */ | |
106 | #ifdef CONFIG_X86_32 | |
107 | loadsegment(es, ctxt->es); | |
108 | @@ -250,13 +262,14 @@ static void notrace __restore_processor_state(struct saved_context *ctxt) | |
109 | load_gs_index(ctxt->gs); | |
110 | asm volatile ("movw %0, %%ss" :: "r" (ctxt->ss)); | |
111 | ||
112 | + /* | |
113 | + * Restore FSBASE and user GSBASE after reloading the respective | |
114 | + * segment selectors. | |
115 | + */ | |
116 | wrmsrl(MSR_FS_BASE, ctxt->fs_base); | |
117 | - wrmsrl(MSR_GS_BASE, ctxt->gs_base); | |
118 | wrmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base); | |
119 | #endif | |
120 | ||
121 | - fix_processor_context(); | |
122 | - | |
123 | do_fpu_end(); | |
124 | tsc_verify_tsc_adjust(true); | |
125 | x86_platform.restore_sched_clock_state(); | |
126 | -- | |
127 | 2.19.1 | |
128 |