x86: prevent unconditional writes to DebugCtl MSR
PATCH 1/1 x86: prevent unconditional writes to DebugCtl MSR
Otherwise, enabling (or better, subsequent disabling) of single stepping would cause a kernel oops on CPUs not having this MSR.
The patch could have been added a conditional to the MSR write in user_disable_single_step(), but centralizing the updates seems safer and (looking forward) better manageable.
--- arch/x86/kernel/kprobes.c | 4 ++-- arch/x86/kernel/process_32.c | 4 ++-- arch/x86/kernel/process_64.c | 4 ++-- arch/x86/kernel/step.c | 2 +- include/asm-x86/processor.h | 9 +++++++++ 5 files changed, 16 insertions(+), 7 deletions(-)
arch/x86/kernel/kprobes.c +2 -2
@@ -410,13 +410,13 @@ static void __kprobes set_current_kprobestatic void __kprobes clear_btf (void ) {if (test_thread_flag (TIF_DEBUGCTLMSR )) -wrmsrl (MSR_IA32_DEBUGCTLMSR ,0 ); +update_debugctlmsr (0 ); }static void __kprobes restore_btf (void ) {if (test_thread_flag (TIF_DEBUGCTLMSR )) -wrmsrl (MSR_IA32_DEBUGCTLMSR , current->thread.debugctlmsr); +update_debugctlmsr (current->thread.debugctlmsr); }static void __kprobes prepare_singlestep (struct kprobe *p,struct pt_regs *regs)
arch/x86/kernel/process_32.c +2 -2
@@ -575,12 +575,12 @@ __switch_to_xtra(struct task_struct *pre/* we clear debugctl to make sure DS * is not in use when we change it */ debugctl =0 ; -wrmsrl (MSR_IA32_DEBUGCTLMSR ,0 ); +update_debugctlmsr (0 );wrmsr (MSR_IA32_DS_AREA , next->ds_area_msr,0 ); }if (next->debugctlmsr != debugctl) -wrmsr (MSR_IA32_DEBUGCTLMSR , next->debugctlmsr,0 ); +update_debugctlmsr (next->debugctlmsr);if (test_tsk_thread_flag (next_p,TIF_DEBUG )) {set_debugreg (next->debugreg0,0 );
arch/x86/kernel/process_64.c +2 -2
@@ -573,12 +573,12 @@ static inline void __switch_to_xtra(stru/* we clear debugctl to make sure DS * is not in use when we change it */ debugctl =0 ; -wrmsrl (MSR_IA32_DEBUGCTLMSR ,0 ); +update_debugctlmsr (0 );wrmsrl (MSR_IA32_DS_AREA , next->ds_area_msr); }if (next->debugctlmsr != debugctl) -wrmsrl (MSR_IA32_DEBUGCTLMSR , next->debugctlmsr); +update_debugctlmsr (next->debugctlmsr);if (test_tsk_thread_flag (next_p,TIF_DEBUG )) {loaddebug (next,0 );
arch/x86/kernel/step.c +1 -1
@@ -145,7 +145,7 @@ static void write_debugctlmsr(struct tasif (child != current)return ; -wrmsrl (MSR_IA32_DEBUGCTLMSR , val); +update_debugctlmsr (val); }/*
include/asm-x86/processor.h +9 -0
@@ -662,6 +662,15 @@ extern void switch_to_new_gdt(void);extern void cpu_init (void );extern void init_gdt (int cpu); +static inline void update_debugctlmsr (unsigned long debugctlmsr) +{ +#ifndef CONFIG_X86_DEBUGCTLMSR +if (boot_cpu_data.x86 <6 ) +return ; +#endif +wrmsrl (MSR_IA32_DEBUGCTLMSR , debugctlmsr); +} +/* from system description table in BIOS. Mostly for MCA use, but * others may find it useful. */ extern unsigned int machine_id;
include/asm-x86/processor.h extern void switch_to_new_gdt(void); · line 671
Quoted 8 lines · Jan Beulich
include/asm-x86/processor.h +7
-0+static inline void update_debugctlmsr (unsigned long debugctlmsr) +{ +#ifndef CONFIG_X86_DEBUGCTLMSR +if (boot_cpu_data.x86 <6 ) +return ; +#endif +wrmsrl (MSR_IA32_DEBUGCTLMSR , debugctlmsr);
It would be better to use checking_wrmsrl() instead of the model check because often VMs etc. are missing such MSRs too, but report the higher family number.
Also silently failing is not very nice, but that's a different issue.
-Andi
Quoted 6 lines · Jan Beulich
Otherwise, enabling (or better, subsequent disabling) of single stepping would cause a kernel oops on CPUs not having this MSR.
The patch could have been added a conditional to the MSR write in
user_disable_single_step(), but centralizing the updates seems safer and (looking forward) better manageable.
thanks Jan, applied.
Ingo