[RFC PATCH] arm64: Make arch_randomize_brk avoid stack area

Kees Cook keescook at chromium.org
Mon May 2 12:34:05 PDT 2016


On Thu, Apr 28, 2016 at 7:17 AM, Jon Medhurst (Tixy) <tixy at linaro.org> wrote:
> Sorry, the code patch has errors (I forgot to commit fixes before
> running git format-patch), the correct code, which was in the kernel I
> built and tested, is at the end of this email.

Please resend this as a v2 (with a changelog, etc). Conceptually, it
looks fine to me: it will limit the entropy of the brk base address
when close to the stack on old kernels and should be effectively a
no-op on modern kernels. However, I have some notes inline below.

Also, for v2, can you validate the text vs brk offset entropy on a 4.6
kernel before/after this patch?

This (aslr.c) may be helpful in running a few thousand iterations:

http://bazaar.launchpad.net/~ubuntu-bugcontrol/qa-regression-testing/master/files/head:/scripts/kernel-security/aslr/

$ for i in $(seq 10000); do ./aslr --report brk ; done | ...

>
> On Thu, 2016-04-28 at 14:03 +0100, Jon Medhurst (Tixy) wrote:
> Some incorrect code...
>> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
>> index 8062482..7126a5a 100644
>> --- a/arch/arm64/kernel/process.c
>> +++ b/arch/arm64/kernel/process.c
>> @@ -382,13 +382,24 @@ unsigned long arch_align_stack(unsigned long sp)
>>       return sp & ~0xf;
>>  }
>>
>> -static unsigned long randomize_base(unsigned long base)
>> +unsigned long arch_randomize_brk(struct mm_struct *mm)
>>  {
>>       unsigned long range_end = base + (STACK_RND_MASK << PAGE_SHIFT) + 1;
>> -     return randomize_range(base, range_end, 0) ? : base;
>> -}
>> +     unsigned long max_stack, range_limit;
>>
>> -unsigned long arch_randomize_brk(struct mm_struct *mm)
>> -{
>> -     return randomize_base(mm->brk);
>> +     /*
>> +      * Determine how much room do we need to leave available for the stack.
>> +      * We limit this to a reasonable value, because extremely large or
>> +      * unlimited stacks are always going to bump up against brk at some
>> +      * point and we don't want to fail to randomise brk in those cases.
>> +      */
>> +     max_stack = rlimit(RLIMIT_STACK);
>> +     if (max_stack > SZ_128M)
>> +             max_stack = SZ_128M;
>> +
>> +     range_limit = mm->start_stack - max_stack - 1;
>> +     if (range_end > range_limit)
>> +             range_end > range_limit
>> +
>> +     return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
>>  }
>
> Corrected code...
>
>  arch/arm64/kernel/process.c | 24 ++++++++++++++++++------
>  1 file changed, 18 insertions(+), 6 deletions(-)
>
> diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> index 07c4c53..7e0f404 100644
> --- a/arch/arm64/kernel/process.c
> +++ b/arch/arm64/kernel/process.c
> @@ -434,13 +434,25 @@ unsigned long arch_align_stack(unsigned long sp)
>         return sp & ~0xf;
>  }
>
> -static unsigned long randomize_base(unsigned long base)
> +unsigned long arch_randomize_brk(struct mm_struct *mm)
>  {
> +       unsigned long base = mm->brk;
>         unsigned long range_end = base + (STACK_RND_MASK << PAGE_SHIFT) + 1;

This looks wrong. Shouldn't it be (STACK_RND_MASK + 1) << PAGE_SHIFT ?

STACK_RND_MASK is 0x7ff (32-bit) or 0x3ffff (64-bit):

#define STACK_RND_MASK                  (test_thread_flag(TIF_32BIT) ? \
                                                0x7ff >> (PAGE_SHIFT - 12) : \
                                                0x3ffff >> (PAGE_SHIFT - 12))

(4K paged PAGE_SHIFT is 12)

So the correct offset max would be 0x800000 (32-bit) and 0x40000000
(64-bit), instead of
0x7ff0001 and 0x3ffff0001.

Even with that correction, this looks wrong for 32-bit, which uses
0x2000000 natively:

unsigned long arch_randomize_brk(struct mm_struct *mm)
{
        unsigned long range_end = mm->brk + 0x02000000;
        return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
}

Seems like arm64 compat is using 4 times less entropy than native arm?
(Note that STACK_RND_MASK is correct for arm64 compat: this matches
the default in fs/binfmt_elf.c that arm uses. It just seems like the
brk randomization is accidentally too small on arm64 compat since arm
uses a fixed value unrelated to stack randomization.)

0x02000000 arm native
0x00800000 arm64 compat  <- bug?
0x40000000 arm64


> -       return randomize_range(base, range_end, 0) ? : base;
> -}
> +       unsigned long max_stack, range_limit;
>
> -unsigned long arch_randomize_brk(struct mm_struct *mm)
> -{
> -       return randomize_base(mm->brk);
> +       /*
> +        * Determine how much room do we need to leave available for the stack.
> +        * We limit this to a reasonable value, because extremely large or
> +        * unlimited stacks are always going to bump up against brk at some
> +        * point and we don't want to fail to randomise brk in those cases.
> +        */
> +       max_stack = rlimit(RLIMIT_STACK);
> +       if (max_stack > SZ_128M)
> +               max_stack = SZ_128M;
> +
> +       range_limit = mm->start_stack - max_stack - 1;
> +       if (range_end > range_limit)
> +               range_end = range_limit;
> +
> +       return randomize_range(base, range_end, 0) ? : base;
>  }
> --
> 2.1.4
>

-Kees

-- 
Kees Cook
Chrome OS & Brillo Security



More information about the linux-arm-kernel mailing list