|
Message-ID: <20180221132443.GA9989@pd.tnic> Date: Wed, 21 Feb 2018 14:24:43 +0100 From: Borislav Petkov <bp@...en8.de> To: Alexander Popov <alex.popov@...ux.com> Cc: kernel-hardening@...ts.openwall.com, Kees Cook <keescook@...omium.org>, PaX Team <pageexec@...email.hu>, Brad Spengler <spender@...ecurity.net>, Ingo Molnar <mingo@...nel.org>, Andy Lutomirski <luto@...nel.org>, Tycho Andersen <tycho@...ho.ws>, Laura Abbott <labbott@...hat.com>, Mark Rutland <mark.rutland@....com>, Ard Biesheuvel <ard.biesheuvel@...aro.org>, Thomas Gleixner <tglx@...utronix.de>, "H . Peter Anvin" <hpa@...or.com>, Peter Zijlstra <a.p.zijlstra@...llo.nl>, "Dmitry V . Levin" <ldv@...linux.org>, x86@...nel.org Subject: Re: [PATCH RFC v8 1/6] x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls On Fri, Feb 16, 2018 at 09:10:52PM +0300, Alexander Popov wrote: > diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig > index 63bf349..62748bd 100644 > --- a/arch/x86/Kconfig > +++ b/arch/x86/Kconfig > @@ -119,6 +119,7 @@ config X86 > select HAVE_ARCH_COMPAT_MMAP_BASES if MMU && COMPAT > select HAVE_ARCH_SECCOMP_FILTER > select HAVE_ARCH_THREAD_STRUCT_WHITELIST > + select HAVE_ARCH_STACKLEAK > select HAVE_ARCH_TRACEHOOK > select HAVE_ARCH_TRANSPARENT_HUGEPAGE > select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64 > diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S > index 16c2c02..4a7365a 100644 > --- a/arch/x86/entry/entry_32.S > +++ b/arch/x86/entry/entry_32.S > @@ -77,6 +77,90 @@ > #endif > .endm > > +.macro erase_kstack > +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK > + call erase_kstack Hmm, why do we need a macro *and* a function of the same name? Why not do call erase_kstack everywhere we need to? And then do ENTRY(erase_kstack) #ifdef CONFIG_GCC_PLUGIN_STACKLEAK ... to tone down the ifdeffery. > +#endif > +.endm > + > +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK > +ENTRY(erase_kstack) > + pushl %edi > + pushl %ecx > + pushl %eax > + pushl %ebp > + > + movl PER_CPU_VAR(current_task), %ebp > + mov TASK_lowest_stack(%ebp), %edi So instead of TASK_lowest_stack and adding all the ifdeffery around, why not do this computation: (unsigned long)task_stack_page(p) + 2 * sizeof(unsigned long); in asm? You only need task->stack as an offset variable. And other code might need it too, anyway so you could just as well use it instead of adding another var to thread_struct. /me reads further. Ah. So this lowest_stack thing changes at the end: "Set the lowest_stack value to the top_of_stack - 256" Why? So that magic needs more explanation for slow people like me. > + mov $STACKLEAK_POISON, %eax > + std > + > + /* > + * Let's search for the poison value in the stack. > + * Start from the lowest_stack and go to the bottom (see std above). Let's write insns capitalized: "see STD above". ... > + /* > + * Check that some further qwords contain poison. If so, the part > + * of the stack below the address in %rdi is likely to be poisoned. > + * Otherwise we need to search deeper. > + */ > + mov $STACKLEAK_POISON_CHECK_DEPTH / 8, %ecx > + repe scasq > + jecxz 2f /* Poison the upper part of the stack */ > + jne 1b /* Search deeper */ > + > +2: You could name that label .Lpoison and make the code more readable: jb .Lpoison and so on. > + /* > + * Two qwords at the bottom of the thread stack are reserved and > + * should not be poisoned (see CONFIG_SCHED_STACK_END_CHECK). > + */ > + or $2 * 8, %rdi > + > + /* > + * Check whether we are on the thread stack to prepare the counter > + * for stack poisoning. > + */ > + mov PER_CPU_VAR(cpu_current_top_of_stack), %rcx > + sub %rsp, %rcx > + cmp $THREAD_SIZE_asm, %rcx > + jb 3f > + > + /* > + * We are not on the thread stack, so we can write poison between > + * the address in %rdi and the stack top. > + */ > + mov PER_CPU_VAR(cpu_current_top_of_stack), %rcx > + sub %rdi, %rcx > + jmp 4f > + > +3: > + /* > + * We are on the thread stack, so we can write poison between the > + * address in %rdi and the address in %rsp (not included, used memory). > + */ > + mov %rsp, %rcx > + sub %rdi, %rcx > + > +4: > + /* Check that the counter value is sane */ > + cmp $THREAD_SIZE_asm, %rcx > + jb 5f > + ud2 > + > +5: > + /* > + * So let's write the poison value to the kernel stack. Start from the > + * address in %rdi and move up (see cld). > + */ > + cld > + shr $3, %ecx > + rep stosq Hohumm, that's >2K loops per syscall here and stack is 0xffffc90000008010: 0xffffffffffff4111 0xffffffffffff4111 0xffffc90000008020: 0xffffffffffff4111 0xffffffffffff4111 0xffffc90000008030: 0xffffffffffff4111 0xffffffffffff4111 0xffffc90000008040: 0xffffffffffff4111 0xffffffffffff4111 0xffffc90000008050: 0xffffffffffff4111 0xffffffffffff4111 0xffffc90000008060: 0xffffffffffff4111 0xffffffffffff4111 ... > diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c > index 9eb448c..6dc55f6 100644 > --- a/arch/x86/kernel/process_64.c > +++ b/arch/x86/kernel/process_64.c > @@ -281,6 +281,11 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp, > p->thread.sp = (unsigned long) fork_frame; > p->thread.io_bitmap_ptr = NULL; > > +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK > + p->thread.lowest_stack = (unsigned long)task_stack_page(p) + > + 2 * sizeof(unsigned long); > +#endif > + > savesegment(gs, p->thread.gsindex); > p->thread.gsbase = p->thread.gsindex ? 0 : me->thread.gsbase; > savesegment(fs, p->thread.fsindex); > diff --git a/include/linux/compiler.h b/include/linux/compiler.h > index e835fc0..fe04f60 100644 > --- a/include/linux/compiler.h > +++ b/include/linux/compiler.h > @@ -337,4 +337,10 @@ unsigned long read_word_at_a_time(const void *addr) > compiletime_assert(__native_word(t), \ > "Need native word sized stores/loads for atomicity.") > > +#ifdef CONFIG_GCC_PLUGIN_STACKLEAK > +/* Poison value points to the unused hole in the virtual memory map */ There are a bunch of those there now. Please document it in Documentation/x86/x86_64/mm.txt > +# define STACKLEAK_POISON -0xBEEF > +# define STACKLEAK_POISON_CHECK_DEPTH 128 > +#endif > + > #endif /* __LINUX_COMPILER_H */ > -- > 2.7.4 > -- Regards/Gruss, Boris. Good mailing practices for 400: avoid top-posting and trim the reply.
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.