Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <2C0135CD-ACCF-462F-B7C6-E8554C6C99BF@gmail.com>
Date: Tue, 14 Feb 2017 10:44:22 +0900
From: Hoeun Ryu <hoeun.ryu@...il.com>
To: Kees Cook <keescook@...omium.org>
Cc: LKML <linux-kernel@...r.kernel.org>,
 "kernel-hardening@...ts.openwall.com" <kernel-hardening@...ts.openwall.com>
Subject: Re: [PATCH] usercopy: add testcases to check zeroing on failure of usercopy



> On Feb 14, 2017, at 4:24 AM, Kees Cook <keescook@...omium.org> wrote:
> 
>> On Mon, Feb 13, 2017 at 10:33 AM, Kees Cook <keescook@...omium.org> wrote:
>>> On Sat, Feb 11, 2017 at 10:13 PM, Hoeun Ryu <hoeun.ryu@...il.com> wrote:
>>> In the hardend usercopy, the destination buffer will be zeroed if
>>> copy_from_user/get_user fails. This patch adds testcases for it.
>>> The destination buffer is set with non-zero value before illegal
>>> copy_from_user/get_user is executed and the buffer is compared to
>>> zero after usercopy is done.
>>> 
>>> Signed-off-by: Hoeun Ryu <hoeun.ryu@...il.com>
>>> ---
>>> lib/test_user_copy.c | 17 +++++++++++++++++
>>> 1 file changed, 17 insertions(+)
>>> 
>>> diff --git a/lib/test_user_copy.c b/lib/test_user_copy.c
>>> index 0ecef3e..54bd898 100644
>>> --- a/lib/test_user_copy.c
>>> +++ b/lib/test_user_copy.c
>>> @@ -41,11 +41,18 @@ static int __init test_user_copy_init(void)
>>>        char *bad_usermem;
>>>        unsigned long user_addr;
>>>        unsigned long value = 0x5A;
>>> +       char *zerokmem;
>>> 
>>>        kmem = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
>>>        if (!kmem)
>>>                return -ENOMEM;
>>> 
>>> +       zerokmem = kzalloc(PAGE_SIZE * 2, GFP_KERNEL);
>>> +       if (!zerokmem) {
>>> +               kfree(kmem);
>>> +               return -ENOMEM;
>>> +       }
>>> +
>>>        user_addr = vm_mmap(NULL, 0, PAGE_SIZE * 2,
>>>                            PROT_READ | PROT_WRITE | PROT_EXEC,
>>>                            MAP_ANONYMOUS | MAP_PRIVATE, 0);
>>> @@ -69,25 +76,35 @@ static int __init test_user_copy_init(void)
>>>                    "legitimate put_user failed");
>>> 
>>>        /* Invalid usage: none of these should succeed. */
>>> +       memset(kmem, 0x5A, PAGE_SIZE);
>>>        ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE),
>>>                                    PAGE_SIZE),
>>>                    "illegal all-kernel copy_from_user passed");
>>> +       ret |= test(memcmp(zerokmem, kmem, PAGE_SIZE),
>>> +                   "zeroing failure for illegal all-kernel copy_from_user");
>>> +       memset(bad_usermem, 0x5A, PAGE_SIZE);
>> 
>> Oh, actually, ha-ha: this isn't legal: it's a direct copy from kernel
>> to userspace. :) This needs a copy_to_user()... (and same for the
>> memcmp...)

I just came up with that usercopy doesn't check the buffer is valid when zeroing happens. I mean if the buffer is wrong address pointing other kernel objects or user space address, is it possible for zeroing to overwrite the address ?

>> 
>>>        ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem,
>>>                                    PAGE_SIZE),
>>>                    "illegal reversed copy_from_user passed");
>>> +       ret |= test(memcmp(zerokmem, bad_usermem, PAGE_SIZE),
>>> +                   "zeroing failure for illegal reversed copy_from_user");
>>>        ret |= test(!copy_to_user((char __user *)kmem, kmem + PAGE_SIZE,
>>>                                  PAGE_SIZE),
>>>                    "illegal all-kernel copy_to_user passed");
>>>        ret |= test(!copy_to_user((char __user *)kmem, bad_usermem,
>>>                                  PAGE_SIZE),
>>>                    "illegal reversed copy_to_user passed");
>>> +       memset(kmem, 0x5A, PAGE_SIZE);
>>>        ret |= test(!get_user(value, (unsigned long __user *)kmem),
>>>                    "illegal get_user passed");
>>> +       ret |= test(memcmp(zerokmem, kmem, sizeof(value)),
>>> +                   "zeroing failure for illegal get_user");

Actually on my x86_64 (qemu), this testcase fails.
The generic get_user has zeroing but the one of arch x86 does not.
Do we need to propagate zeroing to the other arch specific get_user code ?

>>>        ret |= test(!put_user(value, (unsigned long __user *)kmem),
>>>                    "illegal put_user passed");
>>> 
>>>        vm_munmap(user_addr, PAGE_SIZE * 2);
>>>        kfree(kmem);
>>> +       kfree(zerokmem);
>>> 
>>>        if (ret == 0) {
>>>                pr_info("tests passed.\n");
>>> --
>>> 2.7.4
>>> 
>> 
>> Can you respin this?
> 
> On second thought, don't worry about a respin, I had to adjust the bad
> copy_from_user() that was already in there (which fails under SMAP/PAN
> anyway). I'll clean this up and add it to the tree.
> 
> Thanks!
> 
> -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.