Skip to content

Commit

Permalink
fs, elf: drop MAP_FIXED usage from elf_map
Browse files Browse the repository at this point in the history
Both load_elf_interp and load_elf_binary rely on elf_map to map segments
on a controlled address and they use MAP_FIXED to enforce that.  This is
however dangerous thing prone to silent data corruption which can be even
exploitable.  Let's take CVE-2017-1000253 as an example.  At the time
(before eab0953 ("binfmt_elf: use ELF_ET_DYN_BASE only for PIE"))
ELF_ET_DYN_BASE was at TASK_SIZE / 3 * 2 which is not that far away from
the stack top on 32b (legacy) memory layout (only 1GB away).  Therefore we
could end up mapping over the existing stack with some luck.

The issue has been fixed since then (a87938b ("fs/binfmt_elf.c: fix
bug in loading of PIE binaries")), ELF_ET_DYN_BASE moved moved much
further from the stack (eab0953 and later by c715b72 ("mm:
revert x86_64 and arm64 ELF_ET_DYN_BASE base changes")) and excessive
stack consumption early during execve fully stopped by da029c1
("exec: Limit arg stack to at most 75% of _STK_LIM").  So we should be
safe and any attack should be impractical.  On the other hand this is just
too subtle assumption so it can break quite easily and hard to spot.

I believe that the MAP_FIXED usage in load_elf_binary (et.  al) is still
fundamentally dangerous.  Moreover it shouldn't be even needed.  We are at
the early process stage and so there shouldn't be unrelated mappings
(except for stack and loader) existing so mmap for a given address should
succeed even without MAP_FIXED.  Something is terribly wrong if this is
not the case and we should rather fail than silently corrupt the
underlying mapping.

Address this issue by changing MAP_FIXED to the newly added
MAP_FIXED_SAFE.  This will mean that mmap will fail if there is an
existing mapping clashing with the requested one without clobbering it.

Link: http://lkml.kernel.org/r/20171213092550.2774-3-mhocko@kernel.org
Signed-off-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: Khalid Aziz <khalid.aziz@oracle.com>
Acked-by: Kees Cook <keescook@chromium.org>
Cc: Abdul Haleem <abdhalee@linux.vnet.ibm.com>
Cc: Joel Stanley <joel@jms.id.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
  • Loading branch information
Michal Hocko authored and hnaz committed Dec 15, 2017
1 parent 8569f87 commit 0ecdc10
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 5 deletions.
6 changes: 5 additions & 1 deletion arch/metag/kernel/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ unsigned long __metag_elf_map(struct file *filep, unsigned long addr,
tcm_tag = tcm_lookup_tag(addr);

if (tcm_tag != TCM_INVALID_TAG)
type &= ~MAP_FIXED;
type &= ~(MAP_FIXED | MAP_FIXED_SAFE);

/*
* total_size is the size of the ELF (interpreter) image.
Expand All @@ -417,6 +417,10 @@ unsigned long __metag_elf_map(struct file *filep, unsigned long addr,
} else
map_addr = vm_mmap(filep, addr, size, prot, type, off);

if ((type & MAP_FIXED_SAFE) && BAD_ADDR(map_addr))
pr_info("%d (%s): Uhuuh, elf segement at %p requested but the memory is mapped already\n",
task_pid_nr(current), tsk->comm, (void*)addr);

if (!BAD_ADDR(map_addr) && tcm_tag != TCM_INVALID_TAG) {
struct tcm_allocation *tcm;
unsigned long tcm_addr;
Expand Down
12 changes: 8 additions & 4 deletions fs/binfmt_elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,10 @@ static unsigned long elf_map(struct file *filep, unsigned long addr,
} else
map_addr = vm_mmap(filep, addr, size, prot, type, off);

if ((type & MAP_FIXED_SAFE) && BAD_ADDR(map_addr))
pr_info("%d (%s): Uhuuh, elf segement at %p requested but the memory is mapped already\n",
task_pid_nr(current), current->comm, (void*)addr);

return(map_addr);
}

Expand Down Expand Up @@ -575,7 +579,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
elf_prot |= PROT_EXEC;
vaddr = eppnt->p_vaddr;
if (interp_elf_ex->e_type == ET_EXEC || load_addr_set)
elf_type |= MAP_FIXED;
elf_type |= MAP_FIXED_SAFE;
else if (no_base && interp_elf_ex->e_type == ET_DYN)
load_addr = -vaddr;

Expand Down Expand Up @@ -939,7 +943,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
* the ET_DYN load_addr calculations, proceed normally.
*/
if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
elf_flags |= MAP_FIXED;
elf_flags |= MAP_FIXED_SAFE;
} else if (loc->elf_ex.e_type == ET_DYN) {
/*
* This logic is run once for the first LOAD Program
Expand Down Expand Up @@ -975,7 +979,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
load_bias = ELF_ET_DYN_BASE;
if (current->flags & PF_RANDOMIZE)
load_bias += arch_mmap_rnd();
elf_flags |= MAP_FIXED;
elf_flags |= MAP_FIXED_SAFE;
} else
load_bias = 0;

Expand Down Expand Up @@ -1234,7 +1238,7 @@ static int load_elf_library(struct file *file)
(eppnt->p_filesz +
ELF_PAGEOFFSET(eppnt->p_vaddr)),
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
MAP_FIXED_SAFE | MAP_PRIVATE | MAP_DENYWRITE,
(eppnt->p_offset -
ELF_PAGEOFFSET(eppnt->p_vaddr)));
if (error != ELF_PAGESTART(eppnt->p_vaddr))
Expand Down

0 comments on commit 0ecdc10

Please sign in to comment.