|
|
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.