Re: [PATCH v7] powerpc: Do not make the entire heap executable
On Wed, Dec 07, 2016 at 02:15:27PM -0800, Kees Cook wrote: > Can you resend this patch with /proc/$pid/maps output showing the > before/after effects of this change also included here? Showing that > reduction in executable mapping should illustrate the benefit of > avoiding having the execute bit set on the brk area (which is a clear > security weakness, having writable/executable code in the process's > memory segment, especially when it was _not_ requested by the ELF > headers). Denys, I'll leave it to you to re-sumbit again.. I suggest this reworded commit message: powerpc: Do not make the entire heap executable gcc has a configure option to generate ELF files that are W^X: --enable-secureplt However the PPC32 kernel is hardwired to add PROT_EXEC to the heap and BSS segments, totally defeating this security protection. This is done because the common ELF loader does not properly respect the load header permissions when mapping a region that is not file backed. For example, non-secure PPC creates these segments: [21] .data PROGBITS10061254 051254 001868 00 WA 0 0 4 [22] .got PROGBITS10062abc 052abc 14 04 WAX 0 0 4 [23] .sdataPROGBITS10062ad0 052ad0 58 00 WA 0 0 4 [24] .sbss NOBITS 10062b28 052b28 40 00 WA 0 0 8 [25] .plt NOBITS 10062b68 052b28 000d38 00 WAX 0 0 4 [26] .bss NOBITS 100638a0 052b28 006424 00 WA 0 0 16 Which results in an ELF load header covering those segments: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x051160 0x10061160 0x10061160 0x019c8 0x08b64 RWE 0x1 If we just remove the PROT_EXEC PPC32 hack then the kernel will do something like: 10061000-10063000 rwxp 00051000 07:00 3208784/app/bin/busybox.dynamic 10063000-1006a000 rw-p 00:00 0 104a6000-104c7000 rw-p 00:00 0 [heap] The 2nd mapping is the anonymous 0 filled pages requested by FileSiz < MemSiz, but it is not executable. This causes instant crashes of normal PPC32 ELFs as their PLT and GOTs must be executable. This patches fixes the above bug in the ELF loader and removes the forced PROT_EXEC from PPC32. The improvement is seen in /proc/PID/maps. After the patch a normal ELF looks like: 10061000-10063000 rwxp 00051000 07:00 3208784/app/bin/busybox.dynamic 10063000-1006a000 rwxp 00:00 0 104a6000-104c7000 rw-p 00:00 0 [heap] while a secure-PLT ELF looks like: 1005-10052000 rw-p 0005 07:00 3208780/app/bin/busybox.dynamic 10052000-10059000 rw-p 00:00 0 1074-10761000 rw-p 00:00 0 [heap] Critically, with this patch applied all of the maps within a secure-plt ELF are W^X. While before this patch we see undeseriable W normal ELF: 10061000-10063000 rwxp 00051000 07:00 3208784/app/bin/busybox.dynamic 10063000-1006a000 rwxp 00:00 0 104a6000-104c7000 rwxp 00:00 0 [heap] secure-plt ELF: 1005-10052000 rw-p 0005 07:00 3208780/app/bin/busybox.dynamic 10052000-10059000 rwxp 00:00 0 104c7000-104e8000 rwxp 00:00 0 [heap] - Andrew/Kees/Denys: Here are full dumps from my PPC405 system. Before patch (secure-plt): 0010-00103000 r-xp 00:00 0 [vdso] 0fe25000-0fe3 r-xp 07:00 6557504/app/lib/libnss_files-2.23.so 0fe3-0fe44000 ---p b000 07:00 6557504/app/lib/libnss_files-2.23.so 0fe44000-0fe45000 r--p f000 07:00 6557504/app/lib/libnss_files-2.23.so 0fe45000-0fe46000 rw-p 0001 07:00 6557504/app/lib/libnss_files-2.23.so 0fe46000-0fe4c000 rw-p 00:00 0 0fe5c000-0ffd2000 r-xp 07:00 4094672/app/lib/libc-2.23.so 0ffd2000-0ffe8000 ---p 00176000 07:00 4094672/app/lib/libc-2.23.so 0ffe8000-0ffec000 r--p 0017c000 07:00 4094672/app/lib/libc-2.23.so 0ffec000-0ffed000 rw-p 0018 07:00 4094672/app/lib/libc-2.23.so 0ffed000-0fff rw-p 00:00 0 1000-1004e000 r-xp 07:00 3208780/app/bin/busybox.dynamic 1005-10052000 rw-p 0005 07:00 3208780/app/bin/busybox.dynamic 10052000-10059000 rwxp 00:00 0 104c7000-104e8000 rwxp 00:00 0 [heap] b7ab1000-b7ad2000 r-xp 07:00 4009940/app/lib/ld-2.23.so b7aee000-b7af rw-p 00:00 0 b7af-b7af1000 r--p 0002f000 07:00 4009940/app/lib/ld-2.23.so b7af1000-b7af2000 rw-p 0003 07:00 4009940/app/lib/ld-2.23.so bfee1000-bff02000 rw-p 00:00 0 [stack] After Patch (secure-plt): 0010-00103000 r-xp 00:00 0 [vdso] 0fe25000-0fe3 r-xp 07:00 6557504/app/lib/libnss_files-2.23.so 0fe3-0fe44000 ---p b000 07:00 6557504/app/lib/libnss_files-2.23.so 0fe44000-0fe45000 r--p f000 07:00 6557504/app/lib/libnss_files-2.23.so 0fe45000-0fe46000
Re: [PATCH v7] powerpc: Do not make the entire heap executable
On Wed, Nov 9, 2016 at 9:06 AM, Denys Vlasenkowrote: > On 32-bit powerpc the ELF PLT sections of binaries (built with --bss-plt, > or with a toolchain which defaults to it) look like this: > > [17] .sbss NOBITS 0002aff8 01aff8 14 00 WA 0 0 > 4 > [18] .plt NOBITS 0002b00c 01aff8 84 00 WAX 0 0 > 4 > [19] .bss NOBITS 0002b090 01aff8 a4 00 WA 0 0 > 4 > > Which results in an ELF load header: > > Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align > LOAD 0x019c70 0x00029c70 0x00029c70 0x01388 0x014c4 RWE 0x1 > > This is all correct, the load region containing the PLT is marked as > executable. Note that the PLT starts at 0002b00c but the file mapping ends at > 0002aff8, so the PLT falls in the 0 fill section described by the load header, > and after a page boundary. > > Unfortunately the generic ELF loader ignores the X bit in the load headers > when it creates the 0 filled non-file backed mappings. It assumes all of these > mappings are RW BSS sections, which is not the case for PPC. > > gcc/ld has an option (--secure-plt) to not do this, this is said to incur > a small performance penalty. > > Currently, to support 32-bit binaries with PLT in BSS kernel maps *entire > brk area* with executable rights for all binaries, even --secure-plt ones. Can you resend this patch with /proc/$pid/maps output showing the before/after effects of this change also included here? Showing that reduction in executable mapping should illustrate the benefit of avoiding having the execute bit set on the brk area (which is a clear security weakness, having writable/executable code in the process's memory segment, especially when it was _not_ requested by the ELF headers). Thanks! -Kees > > Stop doing that. > > Teach the ELF loader to check the X bit in the relevant load header > and create 0 filled anonymous mappings that are executable > if the load header requests that. > > The patch was originally posted in 2012 by Jason Gunthorpe > and apparently ignored: > > https://lkml.org/lkml/2012/9/30/138 > > Lightly run-tested. > > Signed-off-by: Jason Gunthorpe > Signed-off-by: Denys Vlasenko > Acked-by: Kees Cook > Acked-by: Michael Ellerman > Tested-by: Jason Gunthorpe > CC: Andrew Morton > CC: Benjamin Herrenschmidt > CC: Paul Mackerras > CC: "Aneesh Kumar K.V" > CC: Kees Cook > CC: Oleg Nesterov > CC: Michael Ellerman > CC: Florian Weimer > CC: linux...@kvack.org > CC: linuxppc-dev@lists.ozlabs.org > CC: linux-ker...@vger.kernel.org > --- > Changes since v6: > * rebased to current Linus tree > * sending to akpm > > Changes since v5: > * made do_brk_flags() error out if any bits other than VM_EXEC are set. > (Kees Cook: "With this, I'd be happy to Ack.") > See https://patchwork.ozlabs.org/patch/661595/ > > Changes since v4: > * if (current->personality & READ_IMPLIES_EXEC), still use VM_EXEC > for 32-bit executables. > > Changes since v3: > * typo fix in commit message > * rebased to current Linus tree > > Changes since v2: > * moved capability to map with VM_EXEC into vm_brk_flags() > > Changes since v1: > * wrapped lines to not exceed 79 chars > * improved comment > * expanded CC list > > arch/powerpc/include/asm/page.h | 4 +++- > fs/binfmt_elf.c | 30 ++ > include/linux/mm.h | 1 + > mm/mmap.c | 24 +++- > 4 files changed, 45 insertions(+), 14 deletions(-) > > diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h > index 56398e7..17d3d2c 100644 > --- a/arch/powerpc/include/asm/page.h > +++ b/arch/powerpc/include/asm/page.h > @@ -230,7 +230,9 @@ extern long long virt_phys_offset; > * and needs to be executable. This means the whole heap ends > * up being executable. > */ > -#define VM_DATA_DEFAULT_FLAGS32(VM_READ | VM_WRITE | VM_EXEC | \ > +#define VM_DATA_DEFAULT_FLAGS32 \ > + (((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0) | \ > +VM_READ | VM_WRITE | \ > VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) > > #define VM_DATA_DEFAULT_FLAGS64(VM_READ | VM_WRITE | \ > diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c > index 2472af2..065134b 100644 > --- a/fs/binfmt_elf.c > +++ b/fs/binfmt_elf.c > @@ -91,12 +91,18 @@ static struct linux_binfmt elf_format = { > > #define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE) > > -static int set_brk(unsigned long start, unsigned long end) > +static int set_brk(unsigned long start, unsigned
Re: [PATCH v7] powerpc: Do not make the entire heap executable
On Wed, Nov 9, 2016 at 9:06 AM, Denys Vlasenkowrote: > On 32-bit powerpc the ELF PLT sections of binaries (built with --bss-plt, > or with a toolchain which defaults to it) look like this: > > [17] .sbss NOBITS 0002aff8 01aff8 14 00 WA 0 0 > 4 > [18] .plt NOBITS 0002b00c 01aff8 84 00 WAX 0 0 > 4 > [19] .bss NOBITS 0002b090 01aff8 a4 00 WA 0 0 > 4 > > Which results in an ELF load header: > > Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align > LOAD 0x019c70 0x00029c70 0x00029c70 0x01388 0x014c4 RWE 0x1 > > This is all correct, the load region containing the PLT is marked as > executable. Note that the PLT starts at 0002b00c but the file mapping ends at > 0002aff8, so the PLT falls in the 0 fill section described by the load header, > and after a page boundary. > > Unfortunately the generic ELF loader ignores the X bit in the load headers > when it creates the 0 filled non-file backed mappings. It assumes all of these > mappings are RW BSS sections, which is not the case for PPC. > > gcc/ld has an option (--secure-plt) to not do this, this is said to incur > a small performance penalty. > > Currently, to support 32-bit binaries with PLT in BSS kernel maps *entire > brk area* with executable rights for all binaries, even --secure-plt ones. > > Stop doing that. > > Teach the ELF loader to check the X bit in the relevant load header > and create 0 filled anonymous mappings that are executable > if the load header requests that. > > The patch was originally posted in 2012 by Jason Gunthorpe > and apparently ignored: > > https://lkml.org/lkml/2012/9/30/138 > > Lightly run-tested. > > Signed-off-by: Jason Gunthorpe > Signed-off-by: Denys Vlasenko > Acked-by: Kees Cook > Acked-by: Michael Ellerman > Tested-by: Jason Gunthorpe > CC: Andrew Morton > CC: Benjamin Herrenschmidt > CC: Paul Mackerras > CC: "Aneesh Kumar K.V" > CC: Kees Cook > CC: Oleg Nesterov > CC: Michael Ellerman > CC: Florian Weimer > CC: linux...@kvack.org > CC: linuxppc-dev@lists.ozlabs.org > CC: linux-ker...@vger.kernel.org Hi, Friendly ping to Andrew... I'd like to get this landed in -mm... -Kees > --- > Changes since v6: > * rebased to current Linus tree > * sending to akpm > > Changes since v5: > * made do_brk_flags() error out if any bits other than VM_EXEC are set. > (Kees Cook: "With this, I'd be happy to Ack.") > See https://patchwork.ozlabs.org/patch/661595/ > > Changes since v4: > * if (current->personality & READ_IMPLIES_EXEC), still use VM_EXEC > for 32-bit executables. > > Changes since v3: > * typo fix in commit message > * rebased to current Linus tree > > Changes since v2: > * moved capability to map with VM_EXEC into vm_brk_flags() > > Changes since v1: > * wrapped lines to not exceed 79 chars > * improved comment > * expanded CC list > > arch/powerpc/include/asm/page.h | 4 +++- > fs/binfmt_elf.c | 30 ++ > include/linux/mm.h | 1 + > mm/mmap.c | 24 +++- > 4 files changed, 45 insertions(+), 14 deletions(-) > > diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h > index 56398e7..17d3d2c 100644 > --- a/arch/powerpc/include/asm/page.h > +++ b/arch/powerpc/include/asm/page.h > @@ -230,7 +230,9 @@ extern long long virt_phys_offset; > * and needs to be executable. This means the whole heap ends > * up being executable. > */ > -#define VM_DATA_DEFAULT_FLAGS32(VM_READ | VM_WRITE | VM_EXEC | \ > +#define VM_DATA_DEFAULT_FLAGS32 \ > + (((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0) | \ > +VM_READ | VM_WRITE | \ > VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) > > #define VM_DATA_DEFAULT_FLAGS64(VM_READ | VM_WRITE | \ > diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c > index 2472af2..065134b 100644 > --- a/fs/binfmt_elf.c > +++ b/fs/binfmt_elf.c > @@ -91,12 +91,18 @@ static struct linux_binfmt elf_format = { > > #define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE) > > -static int set_brk(unsigned long start, unsigned long end) > +static int set_brk(unsigned long start, unsigned long end, int prot) > { > start = ELF_PAGEALIGN(start); > end = ELF_PAGEALIGN(end); > if (end > start) { > - int error = vm_brk(start, end - start); > + /* > +* Map the last of the bss segment. > +* If the header is requesting these pages to be > +*
[PATCH v7] powerpc: Do not make the entire heap executable
On 32-bit powerpc the ELF PLT sections of binaries (built with --bss-plt, or with a toolchain which defaults to it) look like this: [17] .sbss NOBITS 0002aff8 01aff8 14 00 WA 0 0 4 [18] .plt NOBITS 0002b00c 01aff8 84 00 WAX 0 0 4 [19] .bss NOBITS 0002b090 01aff8 a4 00 WA 0 0 4 Which results in an ELF load header: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x019c70 0x00029c70 0x00029c70 0x01388 0x014c4 RWE 0x1 This is all correct, the load region containing the PLT is marked as executable. Note that the PLT starts at 0002b00c but the file mapping ends at 0002aff8, so the PLT falls in the 0 fill section described by the load header, and after a page boundary. Unfortunately the generic ELF loader ignores the X bit in the load headers when it creates the 0 filled non-file backed mappings. It assumes all of these mappings are RW BSS sections, which is not the case for PPC. gcc/ld has an option (--secure-plt) to not do this, this is said to incur a small performance penalty. Currently, to support 32-bit binaries with PLT in BSS kernel maps *entire brk area* with executable rights for all binaries, even --secure-plt ones. Stop doing that. Teach the ELF loader to check the X bit in the relevant load header and create 0 filled anonymous mappings that are executable if the load header requests that. The patch was originally posted in 2012 by Jason Gunthorpe and apparently ignored: https://lkml.org/lkml/2012/9/30/138 Lightly run-tested. Signed-off-by: Jason GunthorpeSigned-off-by: Denys Vlasenko Acked-by: Kees Cook Acked-by: Michael Ellerman Tested-by: Jason Gunthorpe CC: Andrew Morton CC: Benjamin Herrenschmidt CC: Paul Mackerras CC: "Aneesh Kumar K.V" CC: Kees Cook CC: Oleg Nesterov CC: Michael Ellerman CC: Florian Weimer CC: linux...@kvack.org CC: linuxppc-dev@lists.ozlabs.org CC: linux-ker...@vger.kernel.org --- Changes since v6: * rebased to current Linus tree * sending to akpm Changes since v5: * made do_brk_flags() error out if any bits other than VM_EXEC are set. (Kees Cook: "With this, I'd be happy to Ack.") See https://patchwork.ozlabs.org/patch/661595/ Changes since v4: * if (current->personality & READ_IMPLIES_EXEC), still use VM_EXEC for 32-bit executables. Changes since v3: * typo fix in commit message * rebased to current Linus tree Changes since v2: * moved capability to map with VM_EXEC into vm_brk_flags() Changes since v1: * wrapped lines to not exceed 79 chars * improved comment * expanded CC list arch/powerpc/include/asm/page.h | 4 +++- fs/binfmt_elf.c | 30 ++ include/linux/mm.h | 1 + mm/mmap.c | 24 +++- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h index 56398e7..17d3d2c 100644 --- a/arch/powerpc/include/asm/page.h +++ b/arch/powerpc/include/asm/page.h @@ -230,7 +230,9 @@ extern long long virt_phys_offset; * and needs to be executable. This means the whole heap ends * up being executable. */ -#define VM_DATA_DEFAULT_FLAGS32(VM_READ | VM_WRITE | VM_EXEC | \ +#define VM_DATA_DEFAULT_FLAGS32 \ + (((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0) | \ +VM_READ | VM_WRITE | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) #define VM_DATA_DEFAULT_FLAGS64(VM_READ | VM_WRITE | \ diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 2472af2..065134b 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -91,12 +91,18 @@ static struct linux_binfmt elf_format = { #define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE) -static int set_brk(unsigned long start, unsigned long end) +static int set_brk(unsigned long start, unsigned long end, int prot) { start = ELF_PAGEALIGN(start); end = ELF_PAGEALIGN(end); if (end > start) { - int error = vm_brk(start, end - start); + /* +* Map the last of the bss segment. +* If the header is requesting these pages to be +* executable, honour that (ppc32 needs this). +*/ + int error = vm_brk_flags(start, end - start, + prot & PROT_EXEC ? VM_EXEC : 0); if (error) return error; } @@ -524,6 +530,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, unsigned long