Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20170726091520.hqhyx2rxda3ir5su@ishxps>
Date: Wed, 26 Jul 2017 12:15:20 +0300
From: Hans Liljestrand <liljestrandh@...il.com>
To: Kees Cook <keescook@...omium.org>
Cc: "kernel-hardening@...ts.openwall.com" <kernel-hardening@...ts.openwall.com>,
	"Reshetova, Elena" <elena.reshetova@...el.com>,
	Dave Hansen <dave.hansen@...el.com>,
	"H. Peter Anvin" <hpa@...or.com>,
	Daniel Micay <danielmicay@...il.com>
Subject: Re: [RFC PATCH 3/5] x86: add mpxk-wrappers

On Tue, Jul 25, 2017 at 11:22:02AM -0700, Kees Cook wrote:
>On Tue, Jul 25, 2017 at 12:52 AM, Hans Liljestrand
><liljestrandh@...il.com> wrote:
>> Do you mean the FORTIFY_SOURCE thing (could not find anything directly on
>> CONFIG_FORTIFY). Based on a quick look FORTIFY_SOURCE is localized, and
>> essentially just provides the equivalent os our "wrappers", but I could be
>> wildly off?
>
>Yup, I mean this:
>
>https://git.kernel.org/linus/6974f0c4555e285ab217cee58b6e874f776ff409

Okay, thanks!

>
>> MPXK on the other hand also checks other pointer dereferences and propagates
>> pointer bounds into non-wrapper functions. The wrappers themselves also do
>> not load any bounds, instead the calling function passes in the pointer
>> bounds via the bound registers (although the caller might, depending on the
>> situation have loaded the bounds based on the kmalloc allocation).
>
>What are the code patterns that MPXK protects? It sounds like I'm not
>quite seeing what MPXK provides beyond the CONFIG_FORTIFY_SOURCE
>checks (both need to know the bounds already...)

>From what I can tell FORTIFY needs to know the during compile time and is only 
applied on specific function. This is not the case with MPXK, which works more 
like KASAN in that it enforces runtime bounds for any pointer access.

As a concrete example of the runtime instrumentation:

noinline void try_write(char *ptr, int i)
{
	ptr[i] = '\0';
}

becomes (where the bnd0 is set by the caller along with function args):

0000000000000000 <try_write>:
   0:	e8 00 00 00 00       	callq  5 <try_write+0x5>
   5:	4c 8d 54 24 08       	lea    0x8(%rsp),%r10
   a:	48 83 e4 f0          	and    $0xfffffffffffffff0,%rsp
   e:	48 63 f6             	movslq %esi,%rsi
  11:	48 01 f7             	add    %rsi,%rdi
  14:	41 ff 72 f8          	pushq  -0x8(%r10)
  18:	55                   	push   %rbp
  19:	48 89 e5             	mov    %rsp,%rbp
  1c:	41 52                	push   %r10
  1e:	48 83 ec 08          	sub    $0x8,%rsp
  22:	f3 0f 1a 07          	bndcl  (%rdi),%bnd0	# <- bound checks
  26:	f2 0f 1a 07          	bndcu  (%rdi),%bnd0	# <- bound checks
  2a:	c6 07 00             	movb   $0x0,(%rdi)
  2d:	48 83 c4 08          	add    $0x8,%rsp
  31:	41 5a                	pop    %r10
  33:	5d                   	pop    %rbp
  34:	49 8d 62 f8          	lea    -0x8(%r10),%rsp
  38:	f2 c3                	bnd retq 
  3a:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)

This applies to any MPXK compiled function and pointer dereference, not only 
wrappers. The caller of course needs to know (at runtime) the bounds, which 
were set by the instrumentation either compile-time or runtime, potentially in 
the kmalloc wrapper (based on the size argument and returned via the bnd0 
register). The memcpy & friends are wrapped so the checks can be done 
explicitly before letting the functions do their thing. The wrappers serve, 
IIUC, a similar purpose to the KASAN's direct modifications to the same 
functions.

KASAN uses shadow memory to keep track of valid memory regions and then checks 
pointer dereferences against that. MPXK by contrast tracks the bounds along 
in-memory (either on stack or in .data section) or in MPX hardware bound 
registers (using regular MPX GCC instrumentation). This is not possible when 
pointers are stashed away in a shared array for example, so in those cases the 
bounds are stored elsewhere. Vanilla MPX uses a separate data structure for 
this, whereas MPXK attempts to determine bounds based on the kmalloc 
allocation area without explicit bound stores, shadow memory or fat-pointers.

The main difference to KASAN/vanilla-MPX is that MPXK does not use any extra 
memory and we therefore hope MPXK will be more feasible to actually use 
runtime.  In support of that idea MPXK is also designed to be modular, i.e.  
you can just apply it on specific parts of the code (hence also wrappers 
instead of KASAN-like direct changes to memcpy, etc implementations).

>
>> Can you recommend any "standard" way of doing measurements, either coverage
>> or performance, for this kind of protections?
>
>I say, for build coverage (possibly via some compile-time warning),
>how many, e.g., memcpy()s are protected (i.e. bounds are known). And
>then similarly, during runtime (possibly via kprobe) how many
>memcpy()s are protected?

Yes, this sounds good. Although it would not cover non-wrapped pointer 
dereferences.  None the less, this should give us an estimate of how much our 
bound load limitations actually affects MPXK coverage. When considering only 
these functions I think our worst case should be similar to what 
CONFIG_FORTIFY already does.

Thanks for the suggestion!

>
>For performance, it's trickier, and needs a meaningful workload. I've
>used hackbench and kernel build times...

The meaningful workload thing is further complicated because we haven't 
envisioned this for kernel-wide deployment. But I guess we will have to do 
something like this to get comparable numbers. I will have to take a look at 
hackbench.

Thanks,
-hans

>
>-Kees
>
>-- 
>Kees Cook
>Pixel Security

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.