Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/vdso: Disallow vvar access to vclock IO for never-used vclocks

It makes me uncomfortable that even modern systems grant every
process direct read access to the HPET.

While fixing this for real without regressing anything is a mess
(unmapping the HPET is tricky because we don't adequately track
all the mappings), we can do almost as well by tracking which
vclocks have ever been used and only allowing pages associated
with used vclocks to be faulted in.

This will cause rogue programs that try to peek at the HPET to
get SIGBUS instead on most systems.

We can't restrict faults to vclock pages that are associated
with the currently selected vclock due to a race: a process
could start to access the HPET for the first time and race
against a switch away from the HPET as the current clocksource.
We can't segfault the process trying to peek at the HPET in this
case, even though the process isn't going to do anything useful
with the data.

Signed-off-by: Andy Lutomirski <luto@kernel.org>
Reviewed-by: Kees Cook <keescook@chromium.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Quentin Casasnovas <quentin.casasnovas@oracle.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/e79d06295625c02512277737ab55085a498ac5d8.1451446564.git.luto@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Andy Lutomirski and committed by
Ingo Molnar
bd902c53 a48a7042

+21 -7
+2 -2
arch/x86/entry/vdso/vma.c
··· 130 130 __pa_symbol(&__vvar_page) >> PAGE_SHIFT); 131 131 } else if (sym_offset == image->sym_hpet_page) { 132 132 #ifdef CONFIG_HPET_TIMER 133 - if (hpet_address) { 133 + if (hpet_address && vclock_was_used(VCLOCK_HPET)) { 134 134 ret = vm_insert_pfn_prot( 135 135 vma, 136 136 (unsigned long)vmf->virtual_address, ··· 141 141 } else if (sym_offset == image->sym_pvclock_page) { 142 142 struct pvclock_vsyscall_time_info *pvti = 143 143 pvclock_pvti_cpu0_va(); 144 - if (pvti) { 144 + if (pvti && vclock_was_used(VCLOCK_PVCLOCK)) { 145 145 ret = vm_insert_pfn( 146 146 vma, 147 147 (unsigned long)vmf->virtual_address,
+8 -1
arch/x86/entry/vsyscall/vsyscall_gtod.c
··· 16 16 #include <asm/vgtod.h> 17 17 #include <asm/vvar.h> 18 18 19 + int vclocks_used __read_mostly; 20 + 19 21 DEFINE_VVAR(struct vsyscall_gtod_data, vsyscall_gtod_data); 20 22 21 23 void update_vsyscall_tz(void) ··· 28 26 29 27 void update_vsyscall(struct timekeeper *tk) 30 28 { 29 + int vclock_mode = tk->tkr_mono.clock->archdata.vclock_mode; 31 30 struct vsyscall_gtod_data *vdata = &vsyscall_gtod_data; 31 + 32 + /* Mark the new vclock used. */ 33 + BUILD_BUG_ON(VCLOCK_MAX >= 32); 34 + WRITE_ONCE(vclocks_used, READ_ONCE(vclocks_used) | (1 << vclock_mode)); 32 35 33 36 gtod_write_begin(vdata); 34 37 35 38 /* copy vsyscall data */ 36 - vdata->vclock_mode = tk->tkr_mono.clock->archdata.vclock_mode; 39 + vdata->vclock_mode = vclock_mode; 37 40 vdata->cycle_last = tk->tkr_mono.cycle_last; 38 41 vdata->mask = tk->tkr_mono.mask; 39 42 vdata->mult = tk->tkr_mono.mult;
+5 -4
arch/x86/include/asm/clocksource.h
··· 3 3 #ifndef _ASM_X86_CLOCKSOURCE_H 4 4 #define _ASM_X86_CLOCKSOURCE_H 5 5 6 - #define VCLOCK_NONE 0 /* No vDSO clock available. */ 7 - #define VCLOCK_TSC 1 /* vDSO should use vread_tsc. */ 8 - #define VCLOCK_HPET 2 /* vDSO should use vread_hpet. */ 9 - #define VCLOCK_PVCLOCK 3 /* vDSO should use vread_pvclock. */ 6 + #define VCLOCK_NONE 0 /* No vDSO clock available. */ 7 + #define VCLOCK_TSC 1 /* vDSO should use vread_tsc. */ 8 + #define VCLOCK_HPET 2 /* vDSO should use vread_hpet. */ 9 + #define VCLOCK_PVCLOCK 3 /* vDSO should use vread_pvclock. */ 10 + #define VCLOCK_MAX 3 10 11 11 12 struct arch_clocksource_data { 12 13 int vclock_mode;
+6
arch/x86/include/asm/vgtod.h
··· 37 37 }; 38 38 extern struct vsyscall_gtod_data vsyscall_gtod_data; 39 39 40 + extern int vclocks_used; 41 + static inline bool vclock_was_used(int vclock) 42 + { 43 + return READ_ONCE(vclocks_used) & (1 << vclock); 44 + } 45 + 40 46 static inline unsigned gtod_read_begin(const struct vsyscall_gtod_data *s) 41 47 { 42 48 unsigned ret;