diff options
author | Andy Lutomirski <luto@kernel.org> | 2017-11-30 10:57:57 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2017-12-06 06:29:12 -0500 |
commit | 5b06bbcfc2c621da3009da8decb7511500c293ed (patch) | |
tree | b9f1c36dac098579ad53feb971b6225fcb48aaee /arch/x86/power | |
parent | ddec3bdee05b06f1dda20ded003c3e10e4184cab (diff) |
x86/power: Fix some ordering bugs in __restore_processor_context()
__restore_processor_context() had a couple of ordering bugs. It
restored GSBASE after calling load_gs_index(), and the latter can
call into tracing code. It also tried to restore segment registers
before restoring the LDT, which is straight-up wrong.
Reorder the code so that we restore GSBASE, then the descriptor
tables, then the segments.
This fixes two bugs. First, it fixes a regression that broke resume
under certain configurations due to irqflag tracing in
native_load_gs_index(). Second, it fixes resume when the userspace
process that initiated suspect had funny segments. The latter can be
reproduced by compiling this:
// SPDX-License-Identifier: GPL-2.0
/*
* ldt_echo.c - Echo argv[1] while using an LDT segment
*/
int main(int argc, char **argv)
{
int ret;
size_t len;
char *buf;
const struct user_desc desc = {
.entry_number = 0,
.base_addr = 0,
.limit = 0xfffff,
.seg_32bit = 1,
.contents = 0, /* Data, grow-up */
.read_exec_only = 0,
.limit_in_pages = 1,
.seg_not_present = 0,
.useable = 0
};
if (argc != 2)
errx(1, "Usage: %s STRING", argv[0]);
len = asprintf(&buf, "%s\n", argv[1]);
if (len < 0)
errx(1, "Out of memory");
ret = syscall(SYS_modify_ldt, 1, &desc, sizeof(desc));
if (ret < -1)
errno = -ret;
if (ret)
err(1, "modify_ldt");
asm volatile ("movw %0, %%es" :: "rm" ((unsigned short)7));
write(1, buf, len);
return 0;
}
and running ldt_echo >/sys/power/mem
Without the fix, the latter causes a triple fault on resume.
Fixes: ca37e57bbe0c ("x86/entry/64: Add missing irqflags tracing to native_load_gs_index()")
Reported-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://lkml.kernel.org/r/6b31721ea92f51ea839e79bd97ade4a75b1eeea2.1512057304.git.luto@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/power')
-rw-r--r-- | arch/x86/power/cpu.c | 21 |
1 files changed, 17 insertions, 4 deletions
diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c index 84fcfde53f8f..5191de14f4df 100644 --- a/arch/x86/power/cpu.c +++ b/arch/x86/power/cpu.c | |||
@@ -226,8 +226,20 @@ static void notrace __restore_processor_state(struct saved_context *ctxt) | |||
226 | load_idt((const struct desc_ptr *)&ctxt->idt_limit); | 226 | load_idt((const struct desc_ptr *)&ctxt->idt_limit); |
227 | #endif | 227 | #endif |
228 | 228 | ||
229 | #ifdef CONFIG_X86_64 | ||
229 | /* | 230 | /* |
230 | * segment registers | 231 | * We need GSBASE restored before percpu access can work. |
232 | * percpu access can happen in exception handlers or in complicated | ||
233 | * helpers like load_gs_index(). | ||
234 | */ | ||
235 | wrmsrl(MSR_GS_BASE, ctxt->gs_base); | ||
236 | #endif | ||
237 | |||
238 | fix_processor_context(); | ||
239 | |||
240 | /* | ||
241 | * Restore segment registers. This happens after restoring the GDT | ||
242 | * and LDT, which happen in fix_processor_context(). | ||
231 | */ | 243 | */ |
232 | #ifdef CONFIG_X86_32 | 244 | #ifdef CONFIG_X86_32 |
233 | loadsegment(es, ctxt->es); | 245 | loadsegment(es, ctxt->es); |
@@ -248,13 +260,14 @@ static void notrace __restore_processor_state(struct saved_context *ctxt) | |||
248 | load_gs_index(ctxt->gs); | 260 | load_gs_index(ctxt->gs); |
249 | asm volatile ("movw %0, %%ss" :: "r" (ctxt->ss)); | 261 | asm volatile ("movw %0, %%ss" :: "r" (ctxt->ss)); |
250 | 262 | ||
263 | /* | ||
264 | * Restore FSBASE and user GSBASE after reloading the respective | ||
265 | * segment selectors. | ||
266 | */ | ||
251 | wrmsrl(MSR_FS_BASE, ctxt->fs_base); | 267 | wrmsrl(MSR_FS_BASE, ctxt->fs_base); |
252 | wrmsrl(MSR_GS_BASE, ctxt->gs_base); | ||
253 | wrmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base); | 268 | wrmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base); |
254 | #endif | 269 | #endif |
255 | 270 | ||
256 | fix_processor_context(); | ||
257 | |||
258 | do_fpu_end(); | 271 | do_fpu_end(); |
259 | tsc_verify_tsc_adjust(true); | 272 | tsc_verify_tsc_adjust(true); |
260 | x86_platform.restore_sched_clock_state(); | 273 | x86_platform.restore_sched_clock_state(); |