Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Wed, 24 Feb 2016 13:48:16 -0800
From: Kees Cook <keescook@...omium.org>
To: Laura Abbott <labbott@...hat.com>
Cc: Laura Abbott <labbott@...oraproject.org>, 
	Greg Kroah-Hartman <gregkh@...uxfoundation.org>, Arnd Bergmann <arnd@...db.de>, 
	"kernel-hardening@...ts.openwall.com" <kernel-hardening@...ts.openwall.com>, LKML <linux-kernel@...r.kernel.org>
Subject: Re: [PATCHv2] lkdtm: Add READ_AFTER_FREE test

On Wed, Feb 24, 2016 at 11:40 AM, Laura Abbott <labbott@...hat.com> wrote:
> On 02/24/2016 09:22 AM, Kees Cook wrote:
>>
>> I did 3 defconfig builds as a benchmark, just to get ballpark numbers...
>>
>> On Tue, Feb 23, 2016 at 1:25 PM, Kees Cook <keescook@...omium.org> wrote:
>>>
>>> Okay, it looks like the combinations to test are:
>>>
>>> default:
>>> DEBUG_PAGEALLOC=n
>>> PAGE_POISONING=n
>>
>>
>> Run times: 412.57 414.19 417.27
>> Mean: 414.68
>> Std Dev: 1.95
>>
>> READ_AFTER_FREE fails:
>> [   83.521712] lkdtm: Performing direct entry READ_AFTER_FREE
>> [   83.521851] lkdtm: Value in memory before free: 12345678
>> [   83.521861] lkdtm: Attempting to read from freed memory
>> [   83.521864] lkdtm: Successfully read value: 12345678
>>
>> WRITE_AFTER_FREE accidentally(?) gets detected (due to
>> CONFIG_SLUB_DEBUG=y default) but does not kill process:
>> [  120.544198] lkdtm: Performing direct entry WRITE_AFTER_FREE
>> [  120.544874]
>> =============================================================================
>> [  120.545028] BUG kmalloc-1024 (Not tainted): Freepointer corrupt

Okay, I've fixed this test to write into the middle, similar to the
READ_AFTER_FREE test, now it no longer detects the write.

>> READ_BUDDY_AFTER_FREE fails:
>> [  120.711466] lkdtm: Performing direct entry READ_BUDDY_AFTER_FREE
>> [  120.711472] lkdtm: Value in memory before free: 12345678
>> [  120.711473] lkdtm: Attempting to read from freed memory
>> [  120.711475] lkdtm: Successfully read value: 12345678
>>
>> WRITE_BUDDY_AFTER_FREE fails:
>> [  120.714371] lkdtm: Performing direct entry WRITE_BUDDY_AFTER_FREE
>> [  120.714374] lkdtm: Writing to the buddy page before free
>> [  120.714377] lkdtm: Writing to the buddy page after free
>> [  120.714378] lkdtm: Wrote to free page successfully
>>
>> So, other than what SLUB_DEBUG caught (which I think is probably an
>> accident, as we can change the test to avoid SLUB_DEBUG detection),
>> this is as-expected: use-after-free was not detected.
>>
>>> heavy-duty:
>>> DEBUG_PAGEALLOC=y (ARCH_SUPPORTS_DEBUG_PAGEALLOC=y)
>>> debug_pagealloc=on
>>
>>
>> Run times: 486.82 464.90 469.34
>> Mean: 473.69
>> Std Dev: 9.46
>>
>> Kind of a giant std-dev, but regardless, DEBUG_PAGEALLOC appears to
>> introduce a 14% perf overhead.
>>
>> READ_AFTER_FREE fails, as before.
>> WRITE_AFTER_FREE accidentally gets detected, as before.
>>
>> READ_BUDDY_AFTER_FREE detected:
>> [ 1760.522979] lkdtm: Performing direct entry READ_BUDDY_AFTER_FREE
>> [ 1760.522985] lkdtm: Value in memory before free: 12345678
>> [ 1760.523013] BUG: unable to handle kernel paging request at
>> ffff88007994b000
>>
>> WRITE_BUDDY_AFTER_FREE detected:
>> [ 1938.050091] lkdtm: Performing direct entry WRITE_BUDDY_AFTER_FREE
>> [ 1938.051475] lkdtm: Writing to the buddy page before free
>> [ 1938.056455] lkdtm: Writing to the buddy page after free
>> [ 1938.058543] BUG: unable to handle kernel paging request at
>> ffff880079b38000
>>
>> I was expecting DEBUG_PAGEALLOC to detect the read/write after free cases
>> too.
>>
>
> Do you mean trigger a paging failure for the slab read/write free cases as
> well?
> That generally won't occur for slab allocations. The slab allocator works by
> allowing multiple allocations to exist on the same page. DEBUG_PAGEALLOC
> works on
> a PAGE_SIZE granularity so as long as there are other allocation on the same
> slab
> page it needs to be mapped. It may also be possible for a slab page to have
> no
> allocated objects but still not be freed back into the buddy allocator.
>
> Basically, DEBUG_PAGEALLOC will only kick in if a page is really and truly
> free
> in the buddy allocator.

Okay, I think I understand. Seems like the buddy test is finer
granularity, essentially.

>>> random poison only:
>>> DEBUG_PAGEALLOC=n
>>> PAGE_POISONING=y
>>> PAGE_POISONING_NO_SANITY=y
>>> PAGE_POISONING_ZERO=n
>>> page_poison=on
>>
>>
>> Run times: 428.96 424.76 426.12
>> Mean: 426.61
>> Std Dev: 1.75
>>
>> This is under 3% perf overhead, with similar std-dev.
>>
>> READ_AFTER_FREE fails, as before.
>> WRITE_AFTER_FREE accidentally gets detected, as before.
>>
>> READ_BUDDY_AFTER_FREE detected:
>> [ 1448.167650] lkdtm: Performing direct entry READ_BUDDY_AFTER_FREE
>> [ 1448.167654] lkdtm: Value in memory before free: 12345678
>> [ 1448.167656] lkdtm: Attempting to read from freed memory
>> [ 1448.167677] general protection fault: 0000 [#1] SMP
>>
>> WRITE_BUDDY_AFTER_FREE fails:
>> [ 1448.206587] lkdtm: Performing direct entry WRITE_BUDDY_AFTER_FREE
>> [ 1448.207550] lkdtm: Writing to the buddy page before free
>> [ 1448.208327] lkdtm: Writing to the buddy page after free
>> [ 1448.209088] lkdtm: Wrote to free page successfully
>>
>> I wasn't expecting the GP with NO_SANITY=y. I was expecting to read
>> back random data?
>>
>
> NO_SANITY just means that page poisoning isn't going to run the sanity
> checks to check for bit flips or ovewritten poison. It doesn't have
> any impact on how the poisoning works.

Okay, I think I understand. Another thing, though: I wasn't expecting
READ_AFTER_FREE to return unpoisoned data.

>
>>>
>>> zero poison only:
>>> DEBUG_PAGEALLOC=n
>>> PAGE_POISONING=y
>>> PAGE_POISONING_NO_SANITY=y
>>> PAGE_POISONING_ZERO=y
>>> page_poison=on
>>
>>
>> This breaks my test system (as mentioned in other thread).
>>
>
> Yep, looks like the v1 patches and not the v2 patches which fix
> a known issue with the zeroing.

Ah-ha, I'll go find those and retest.

-Kees

>>> random poison with sanity:
>>> DEBUG_PAGEALLOC=n
>>> PAGE_POISONING=y
>>> PAGE_POISONING_NO_SANITY=n
>>> PAGE_POISONING_ZERO=n
>>> page_poison=on
>>
>>
>> Run times: 432.72 434.41 429.19
>> Mean: 432.11
>> Std Dev: 2.18
>>
>> Just over 4% perf overhead to no checks, and only 1.5% more overhead
>> compared to no sanity.
>>
>> READ_AFTER_FREE fails, as before, but I thought it should be detected
>> with the sanity checking?
>> WRITE_AFTER_FREE accidentally gets detected, as before.
>>
>> READ_BUDDY_AFTER_FREE detects:
>> [   46.720158] lkdtm: Performing direct entry READ_BUDDY_AFTER_FREE
>> [   46.720163] lkdtm: Value in memory before free: 12345678
>> [   46.720164] lkdtm: Attempting to read from freed memory
>> [   46.720169] general protection fault: 0000 [#1] SMP
>>
>> WRITE_BUDDY_AFTER_FREE detects, but does not kill process:
>> [   46.756109] lkdtm: Performing direct entry WRITE_BUDDY_AFTER_FREE
>> [   46.756975] lkdtm: Writing to the buddy page before free
>> [   46.757764] lkdtm: Writing to the buddy page after free
>> [   46.758514] lkdtm: Wrote to free page successfully
>> [   46.759244] pagealloc: memory corruption
>>
>> While WRITE_BUDDY_AFTER_FREE is detected, it doesn't kill the process
>> (similar to the WRITE_AFTER_FREE detection). We should change this
>> (maybe optionally) to drop processes that are triggering issues like
>> this.
>>
>
> So an optional call to panic when SL*B or page corruption is detected?
>
>>>
>>> zero poison with sanity:
>>> DEBUG_PAGEALLOC=n
>>> PAGE_POISONING=y
>>> PAGE_POISONING_NO_SANITY=n
>>> PAGE_POISONING_ZERO=y
>>> page_poison=on
>>
>>
>> Again, couldn't test.
>>
>> Looks like good progress to me. :)
>>
>
> Thanks!
>
> Laura
>
>
>>
>> -Kees
>>
>



-- 
Kees Cook
Chrome OS & Brillo 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.