binfmt_elf: Use ELF_ET_DYN_BASE only for PIE
Subsystems (3)
RFC PATCH 1/1 binfmt_elf: Use ELF_ET_DYN_BASE only for PIE
The ELF_ET_DYN_BASE position was originally intended to keep loaders away from ET_EXEC binaries. (For example, running "/lib/ld-linux.so.2 /bin/cat" might cause the subsequent load of /bin/cat into where the loader had been loaded.) With the advent of PIE (ET_DYN binaries with an INTERP Program Header), ELF_ET_DYN_BASE continued to be used since the kernel was only looking at ET_DYN. However, since ELF_ET_DYN_BASE is traditionally set at the top 1/3rd of the TASK_SIZE, a substantial portion of the address space is unused.
For 32-bit tasks when RLIMIT_STACK is set to RLIM_INFINITY, programs are loaded below the mmap region. This means they can be made to collide (CVE-2017-1000370) or nearly collide (CVE-2017-1000371) with pathological stack regions. Lowering ELF_ET_DYN_BASE solves both by moving programs above the mmap region in all cases, and will now additionally avoid programs falling back to the mmap region by enforcing MAP_FIXED for program loads (i.e. if it would have collided with the stack, now it will fail to load instead of falling back to the mmap region).
To allow for a lower ELF_ET_DYN_BASE, loaders (ET_DYN without INTERP) are loaded into the mmap region, leaving space available for either an ET_EXEC binary with a fixed location or PIE being loaded into mmap by the loader. Only PIE programs are loaded offset from ELF_ET_DYN_BASE, which means architectures can now safely lower their values without risk of loaders colliding with their subsequently loaded programs.
Thanks go to PaX for inspiration on how to approach this solution.
--- arch/x86/include/asm/elf.h | 8 ++------ fs/binfmt_elf.c | 38 +++++++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 13 deletions(-)
arch/x86/include/asm/elf.h +2 -6
@@ -245,12 +245,8 @@ extern int force_personality32;#define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE 4096 -/* This is the location that an ET_DYN program is loaded if exec'ed. Typical -use of this is to invoke "./ld.so someprog" to test out a new version of -the loader. We need to make sure that it is out of the way of the program -that it will "exec", and that there is sufficient room for the brk. */ - -#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) +/* This is the base location for PIE (ET_DYN with INTERP) loads. */ +#define ELF_ET_DYN_BASE 0x400000UL/* This yields a mask that user programs can use to figure out what instruction set this CPU supports. This could be done in user space,
fs/binfmt_elf.c +31 -7
@@ -930,13 +930,37 @@ static int load_elf_binary(struct linux_binprm *bprm)if (loc->elf_ex.e_type ==ET_EXEC || load_addr_set) { elf_flags |=MAP_FIXED ; }else if (loc->elf_ex.e_type ==ET_DYN ) { -/* Try and get dynamic programs out of the way of the -* default mmap base, as well as whatever program they -* might try to exec. This is because the brk will -* follow the loader, and is not movable. */ - load_bias =ELF_ET_DYN_BASE - vaddr; -if (current->flags &PF_RANDOMIZE ) - load_bias +=arch_mmap_rnd (); +/* +* There are effectively two types of ET_DYN +* binaries: programs (i.e. PIE: ET_DYN with INTERP) +* and loaders (ET_DYN without INTERP, since they +* _are_ the ELF interpreter). The loaders must +* be loaded away from programs since the program +* may otherwise collide with the loader (especially +* for ET_EXEC which does not have a randomized +* position). For example to handle invocations of +* "./ld.so someprog" to test out a new version of +* the loader, the subsequent program that the +* loader loads must avoid the loader itself, so +* they cannot share the same load range. Sufficient +* room for the brk must be allocated with the +* loader as well, since brk must be available with +* the loader. +* +* Therefore, programs are loaded offset from +* ELF_ET_DYN_BASE and loaders are loaded into the +* independently randomized mmap region (0 load_bias +* without MAP_FIXED). +*/ +if (elf_interpreter) { + load_bias =ELF_ET_DYN_BASE ; +if (current->flags &PF_RANDOMIZE ) + load_bias +=arch_mmap_rnd (); + elf_flags |=MAP_FIXED ; + }else + load_bias =0 ; + + load_bias -= vaddr; load_bias =ELF_PAGESTART (load_bias); total_size =total_mapping_size (elf_phdata, loc->elf_ex.e_phnum);
Signature · 6 lines
2.7.4 -- Kees Cook Pixel Security
Quoted 57 lines · Kees Cook
The
ELF_ET_DYN_BASEposition was originally intended to keep loaders away fromET_EXECbinaries. (For example, running "/lib/ld-linux.so.2/bin/cat" might cause the subsequent load of/bin/catinto where the loader had been loaded.) With the advent of PIE (ET_DYNbinaries with an INTERP Program Header),ELF_ET_DYN_BASEcontinued to be used since the kernel was only looking atET_DYN. However, sinceELF_ET_DYN_BASEis traditionally set at the top 1/3rd of theTASK_SIZE, a substantial portion of the address space is unused.For 32-bit tasks when
RLIMIT_STACKis set toRLIM_INFINITY, programs are loaded below the mmap region. This means they can be made to collide
(CVE-2017-1000370) or nearly collide (CVE-2017-1000371) with pathological
stack regions. LoweringELF_ET_DYN_BASEsolves both by moving programs
above the mmap region in all cases, and will now additionally avoid programs falling back to the mmap region by enforcingMAP_FIXEDfor program loads (i.e. if it would have collided with the stack, now it will fail to load instead of falling back to the mmap region).To allow for a lower
ELF_ET_DYN_BASE, loaders (ET_DYNwithout INTERP) are loaded into the mmap region, leaving space available for either anET_EXECbinary with a fixed location or PIE being loaded into mmap by the
loader. Only PIE programs are loaded offset fromELF_ET_DYN_BASE, which
means architectures can now safely lower their values without risk of loaders colliding with their subsequently loaded programs.Thanks go to PaX for inspiration on how to approach this solution.
Fixes d1fd836dcf00 ("mm: split ET_DYN ASLR from mmap ASLR")Signed-off-by Kees Cook <[email protected]>---
arch/x86/include/asm/elf.h | 8 ++------
fs/binfmt_elf.c | 38 +++++++++++++++++++++++++++++++----- --2 files changed, 33 insertions(+), 13 deletions(-)arch/x86/include/asm/elf.h +0
-0@@ -245,12 +245,8 @@ extern int force_personality32; #define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE 4096
-/* This is the location that anET_DYNprogram is loaded if exec'ed. Typical
- use of this is to invoke "./ld.so someprog" to test out a new version of
- the loader. We need to make sure that it is out of the way of the program
- that it will "exec", and that there is sufficient room for the brk. */
arch/x86/include/asm/elf.h extern int force_personality32; · line 249
Quoted 4 lines
arch/x86/include/asm/elf.h +2
-2- -#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) +/* This is the base location for PIE (ET_DYN with INTERP) loads. */ +#define ELF_ET_DYN_BASE 0x400000UL
This value is good for 32 bit binaries, but for 64 bit binaries you probably want to put ELF_ET_DYN_BASE at 4GB or higher.
The latter is necessary because Android uses the lower 4GB of address space for its JVM runtime, with 32 bit pointers inside that part of the otherwise 64 bit address space.
In other words:
#define ELF_ET_DYN_BASE (mmap_is_ia32() ? 0x400000UL : 0x100000000UL)
Quoted 15 lines
+++
b/fs/binfmt_elf.carch/x86/include/asm/elf.h +1
-0+ * Therefore, programs are loaded offsetfrom
arch/x86/include/asm/elf.h +1
-0+ * ELF_ET_DYN_BASE and loaders are loadedinto the
arch/x86/include/asm/elf.h +1
-0+ * independently randomized mmap region (0
load_biasarch/x86/include/asm/elf.h +6
-0+* without MAP_FIXED). +*/ +if (elf_interpreter) { + load_bias =ELF_ET_DYN_BASE ; +if (current->flags &PF_RANDOMIZE ) + load_bias +=
arch_mmap_rnd();
arch/x86/include/asm/elf.h
Quoted 5 lines
arch/x86/include/asm/elf.h +5
-0+ elf_flags |=MAP_FIXED ; + }else + load_bias =0 ; + + load_bias -= vaddr;
I like this, and the big comment telling people how it works :)
Quoted 8 lines · Rik van Riel
arch/x86/include/asm/elf.h extern int force_personality32; · line 249
Quoted 2 lines · Kees Cook
arch/x86/include/asm/elf.h +2
-0+/* This is the base location for PIE (ET_DYN with INTERP) loads. */ +#define ELF_ET_DYN_BASE 0x400000ULThis value is good for 32 bit binaries, but for 64 bit binaries you probably want to put
ELF_ET_DYN_BASEat 4GB or higher.
Quoted 8 lines
The latter is necessary because Android uses the lower 4GB of address space for its JVM runtime, with 32 bit pointers inside that part of the otherwise 64 bit address space.
In other words:
#define
ELF_ET_DYN_BASE(mmap_is_ia32()?0x400000UL:0x100000000UL)
Ah, interesting. Okay, that should be fine. I'll adjust it.
Quoted 15 lines
Quoted 15 lines
+++
b/fs/binfmt_elf.c+ * Therefore, programs are loaded offset
from
+ *ELF_ET_DYN_BASEand loaders are loaded
into the
+ * independently randomized mmap region (0load_biasfs/binfmt_elf.c +6
-0+* without MAP_FIXED). +*/ +if (elf_interpreter) { + load_bias =ELF_ET_DYN_BASE ; +if (current->flags &PF_RANDOMIZE ) + load_bias +=
arch_mmap_rnd();
Quoted 8 lines
fs/binfmt_elf.c static int load_elf_binary(struct linux_binprm *bprm) · line 963
Quoted 5 lines
fs/binfmt_elf.c +5
-0+ elf_flags |=MAP_FIXED ; + }else + load_bias =0 ; + + load_bias -= vaddr;I like this, and the big comment telling people how it works :)
Thanks! It looks like your patch for commenting load_bias never got picked up, so I've added some more comments for that and some other things too. (Mostly for all the stuff I have to read a few times when I look at this code.)
-Kees
Signature · 2 lines
Kees Cook Pixel Security
Quoted 46 lines · Kees Cook
Quoted 14 lines · Rik van Riel
Quoted 3 lines · Kees Cook
+/* This is the base location for PIE (
ET_DYNwith INTERP) loads. */
+#defineELF_ET_DYN_BASE0x400000ULThis value is good for 32 bit binaries, but for 64 bit binaries you probably want to put
ELF_ET_DYN_BASEat 4GB or higher.The latter is necessary because Android uses the lower 4GB of address space for its JVM runtime, with 32 bit pointers inside that part of the otherwise 64 bit address space.
Quoted 4 lines
In other words:
#define
ELF_ET_DYN_BASE(mmap_is_ia32()?0x400000UL:0x100000000UL)Ah, interesting. Okay, that should be fine. I'll adjust it.
Quoted 15 lines
Quoted 15 lines
+++
b/fs/binfmt_elf.c+ * Therefore, programs are loaded offset
from
+ *ELF_ET_DYN_BASEand loaders are loaded
into the
+ * independently randomized mmap region (0load_biasfs/binfmt_elf.c +6
-0+* without MAP_FIXED). +*/ +if (elf_interpreter) { + load_bias =ELF_ET_DYN_BASE ; +if (current->flags &PF_RANDOMIZE ) + load_bias +=
arch_mmap_rnd();Quoted 8 lines
fs/binfmt_elf.c static int load_elf_binary(struct linux_binprm *bprm) · line 963
Quoted 5 lines
fs/binfmt_elf.c +5
-0+ elf_flags |=MAP_FIXED ; + }else + load_bias =0 ; + + load_bias -= vaddr;I like this, and the big comment telling people how it works :)
Quoted 7 lines
Thanks! It looks like your patch for commenting
load_biasnever got picked up, so I've added some more comments for that and some other things too. (Mostly for all the stuff I have to read a few times when I look at this code.)-Kees
The stack rlimit calculation fix for space potentially lost to ASLR is probably still needed too, right?
Quoted 52 lines · Daniel Micay
Quoted 51 lines · Kees Cook
Quoted 14 lines · Rik van Riel
Quoted 3 lines · Kees Cook
+/* This is the base location for PIE (
ET_DYNwith INTERP) loads. */
+#defineELF_ET_DYN_BASE0x400000ULThis value is good for 32 bit binaries, but for 64 bit binaries you probably want to put
ELF_ET_DYN_BASEat 4GB or higher.The latter is necessary because Android uses the lower 4GB of address space for its JVM runtime, with 32 bit pointers inside that part of the otherwise 64 bit address space.
Quoted 4 lines
In other words:
#define
ELF_ET_DYN_BASE(mmap_is_ia32()?0x400000UL:0x100000000UL)Ah, interesting. Okay, that should be fine. I'll adjust it.
Quoted 15 lines
Quoted 15 lines
+++
b/fs/binfmt_elf.c+ * Therefore, programs are loaded offset
from
+ *ELF_ET_DYN_BASEand loaders are loaded
into the
+ * independently randomized mmap region (0load_biasfs/binfmt_elf.c +6
-0+* without MAP_FIXED). +*/ +if (elf_interpreter) { + load_bias =ELF_ET_DYN_BASE ; +if (current->flags &PF_RANDOMIZE ) + load_bias +=
arch_mmap_rnd();Quoted 8 lines
fs/binfmt_elf.c static int load_elf_binary(struct linux_binprm *bprm) · line 963
Quoted 5 lines
fs/binfmt_elf.c +5
-0+ elf_flags |=MAP_FIXED ; + }else + load_bias =0 ; + + load_bias -= vaddr;I like this, and the big comment telling people how it works :)
Thanks! It looks like your patch for commenting
load_biasnever got picked up, so I've added some more comments for that and some other things too. (Mostly for all the stuff I have to read a few times when I look at this code.)
Quoted 5 lines
Quoted 2 lines
-Kees
The stack rlimit calculation fix for space potentially lost to ASLR is probably still needed too, right?
Yes. Was that picked up by akpm already?
-Kees
Signature · 2 lines
Kees Cook Pixel Security
Quoted 65 lines · Kees Cook
Quoted 63 lines · Daniel Micay
Quoted 61 lines · Kees Cook
Quoted 15 lines · Rik van Riel
Quoted 4 lines · Kees Cook
+/* This is the base location for PIE (
ET_DYNwith INTERP) loads.
*/
+#defineELF_ET_DYN_BASE0x400000ULThis value is good for 32 bit binaries, but for 64 bit binaries you probably want to put
ELF_ET_DYN_BASEat 4GB or higher.The latter is necessary because Android uses the lower 4GB of address space for its JVM runtime, with 32 bit pointers inside that part of the otherwise 64 bit address space.
Quoted 4 lines
In other words:
#define
ELF_ET_DYN_BASE(mmap_is_ia32()?0x400000UL:0x100000000UL)Ah, interesting. Okay, that should be fine. I'll adjust it.
Quoted 18 lines
Quoted 18 lines
+++
b/fs/binfmt_elf.c+ * Therefore, programs are loaded offset
from
+ *ELF_ET_DYN_BASEand loaders are
loaded
into the
+ * independently randomized mmap region
(0load_biasfs/binfmt_elf.c +5
-0+* without MAP_FIXED). +*/ +if (elf_interpreter) { + load_bias =ELF_ET_DYN_BASE ; + if (current->flags &
PF_RANDOMIZE)
+load_bias+=arch_mmap_rnd();Quoted 8 lines
fs/binfmt_elf.c static int load_elf_binary(struct linux_binprm *bprm) · line 963
Quoted 5 lines
fs/binfmt_elf.c +5
-0+ elf_flags |=MAP_FIXED ; + }else + load_bias =0 ; + + load_bias -= vaddr;I like this, and the big comment telling people how it works :)
Thanks! It looks like your patch for commenting
load_biasnever got
picked up, so I've added some more comments for that and some other
things too. (Mostly for all the stuff I have to read a few times when
I look at this code.)-Kees
Quoted 7 lines
Quoted 3 lines
The stack rlimit calculation fix for space potentially lost to ASLR is
probably still needed too, right?Yes. Was that picked up by akpm already?
-Kees
I think it was dropped when the ET_DYN changes were dropped.
Quoted 70 lines · Daniel Micay
Quoted 69 lines · Kees Cook
Quoted 66 lines · Daniel Micay
Quoted 59 lines · Kees Cook
Quoted 15 lines · Rik van Riel
Quoted 4 lines · Kees Cook
+/* This is the base location for PIE (
ET_DYNwith INTERP) loads.
*/
+#defineELF_ET_DYN_BASE0x400000ULThis value is good for 32 bit binaries, but for 64 bit binaries you probably want to put
ELF_ET_DYN_BASEat 4GB or higher.The latter is necessary because Android uses the lower 4GB of address space for its JVM runtime, with 32 bit pointers inside that part of the otherwise 64 bit address space.
Quoted 4 lines
In other words:
#define
ELF_ET_DYN_BASE(mmap_is_ia32()?0x400000UL:0x100000000UL)Ah, interesting. Okay, that should be fine. I'll adjust it.
Quoted 18 lines
Quoted 18 lines
+++
b/fs/binfmt_elf.c+ * Therefore, programs are loaded offset
from
+ *ELF_ET_DYN_BASEand loaders are
loaded
into the
+ * independently randomized mmap region
(0load_biasfs/binfmt_elf.c +5
-0+* without MAP_FIXED). +*/ +if (elf_interpreter) { + load_bias =ELF_ET_DYN_BASE ; + if (current->flags &
PF_RANDOMIZE)
+load_bias+=arch_mmap_rnd();Quoted 8 lines
fs/binfmt_elf.c static int load_elf_binary(struct linux_binprm *bprm) · line 963
Quoted 5 lines
fs/binfmt_elf.c +5
-0+ elf_flags |=MAP_FIXED ; + }else + load_bias =0 ; + + load_bias -= vaddr;I like this, and the big comment telling people how it works :)
Thanks! It looks like your patch for commenting
load_biasnever got
picked up, so I've added some more comments for that and some other
things too. (Mostly for all the stuff I have to read a few times when
I look at this code.)Quoted 2 lines
-Kees
The stack rlimit calculation fix for space potentially lost to ASLR is
probably still needed too, right?
Quoted 5 lines
Quoted 3 lines
Yes. Was that picked up by akpm already?
-Kees
I think it was dropped when the
ET_DYNchanges were dropped.
Ah! Rik, can you resend just the stack calculation fixes? I can Ack those.
-Kees
Signature · 2 lines
Kees Cook Pixel Security
Quoted 11 lines · Daniel Micay
Quoted 8 lines · Kees Cook
Quoted 4 lines
The stack rlimit calculation fix for space potentially lost to ASLR
is
probably still needed too, right?Yes. Was that picked up by akpm already?
-Kees
I think it was dropped when the
ET_DYNchanges were dropped.
I can resend those.