diff options
author | Jan Kratochvil <honza@jikos.cz> | 2007-07-16 02:40:06 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 12:05:42 -0400 |
commit | 60bfba7e85f88fe834e623ead799cf580de20971 (patch) | |
tree | d27468bc99bfbe8dcfa6fa0e8b8599d2a6e6c7de /fs/binfmt_elf.c | |
parent | f057eac0d7ad967138390a9dd7fd8267e1e39d19 (diff) |
PIE randomization
This patch is using mmap()'s randomization functionality in such a way that
it maps the main executable of (specially compiled/linked -pie/-fpie)
ET_DYN binaries onto a random address (in cases in which mmap() is allowed
to perform a randomization).
Origin of this patch is in exec-shield
(http://people.redhat.com/mingo/exec-shield/)
[jkosina@suse.cz: pie randomization: fix BAD_ADDR macro]
Signed-off-by: Jan Kratochvil <honza@jikos.cz>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Roland McGrath <roland@redhat.com>
Cc: Jakub Jelinek <jakub@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/binfmt_elf.c')
-rw-r--r-- | fs/binfmt_elf.c | 109 |
1 files changed, 86 insertions, 23 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 08e4414b8374..5cfa735639ae 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c | |||
@@ -45,7 +45,7 @@ | |||
45 | 45 | ||
46 | static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs); | 46 | static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs); |
47 | static int load_elf_library(struct file *); | 47 | static int load_elf_library(struct file *); |
48 | static unsigned long elf_map (struct file *, unsigned long, struct elf_phdr *, int, int); | 48 | static unsigned long elf_map (struct file *, unsigned long, struct elf_phdr *, int, int, unsigned long); |
49 | 49 | ||
50 | /* | 50 | /* |
51 | * If we don't support core dumping, then supply a NULL so we | 51 | * If we don't support core dumping, then supply a NULL so we |
@@ -80,7 +80,7 @@ static struct linux_binfmt elf_format = { | |||
80 | .hasvdso = 1 | 80 | .hasvdso = 1 |
81 | }; | 81 | }; |
82 | 82 | ||
83 | #define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE) | 83 | #define BAD_ADDR(x) IS_ERR_VALUE(x) |
84 | 84 | ||
85 | static int set_brk(unsigned long start, unsigned long end) | 85 | static int set_brk(unsigned long start, unsigned long end) |
86 | { | 86 | { |
@@ -285,33 +285,70 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, | |||
285 | #ifndef elf_map | 285 | #ifndef elf_map |
286 | 286 | ||
287 | static unsigned long elf_map(struct file *filep, unsigned long addr, | 287 | static unsigned long elf_map(struct file *filep, unsigned long addr, |
288 | struct elf_phdr *eppnt, int prot, int type) | 288 | struct elf_phdr *eppnt, int prot, int type, |
289 | unsigned long total_size) | ||
289 | { | 290 | { |
290 | unsigned long map_addr; | 291 | unsigned long map_addr; |
291 | unsigned long pageoffset = ELF_PAGEOFFSET(eppnt->p_vaddr); | 292 | unsigned long size = eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr); |
293 | unsigned long off = eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr); | ||
294 | addr = ELF_PAGESTART(addr); | ||
295 | size = ELF_PAGEALIGN(size); | ||
292 | 296 | ||
293 | down_write(¤t->mm->mmap_sem); | ||
294 | /* mmap() will return -EINVAL if given a zero size, but a | 297 | /* mmap() will return -EINVAL if given a zero size, but a |
295 | * segment with zero filesize is perfectly valid */ | 298 | * segment with zero filesize is perfectly valid */ |
296 | if (eppnt->p_filesz + pageoffset) | 299 | if (!size) |
297 | map_addr = do_mmap(filep, ELF_PAGESTART(addr), | 300 | return addr; |
298 | eppnt->p_filesz + pageoffset, prot, type, | 301 | |
299 | eppnt->p_offset - pageoffset); | 302 | down_write(¤t->mm->mmap_sem); |
300 | else | 303 | /* |
301 | map_addr = ELF_PAGESTART(addr); | 304 | * total_size is the size of the ELF (interpreter) image. |
305 | * The _first_ mmap needs to know the full size, otherwise | ||
306 | * randomization might put this image into an overlapping | ||
307 | * position with the ELF binary image. (since size < total_size) | ||
308 | * So we first map the 'big' image - and unmap the remainder at | ||
309 | * the end. (which unmap is needed for ELF images with holes.) | ||
310 | */ | ||
311 | if (total_size) { | ||
312 | total_size = ELF_PAGEALIGN(total_size); | ||
313 | map_addr = do_mmap(filep, addr, total_size, prot, type, off); | ||
314 | if (!BAD_ADDR(map_addr)) | ||
315 | do_munmap(current->mm, map_addr+size, total_size-size); | ||
316 | } else | ||
317 | map_addr = do_mmap(filep, addr, size, prot, type, off); | ||
318 | |||
302 | up_write(¤t->mm->mmap_sem); | 319 | up_write(¤t->mm->mmap_sem); |
303 | return(map_addr); | 320 | return(map_addr); |
304 | } | 321 | } |
305 | 322 | ||
306 | #endif /* !elf_map */ | 323 | #endif /* !elf_map */ |
307 | 324 | ||
325 | static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr) | ||
326 | { | ||
327 | int i, first_idx = -1, last_idx = -1; | ||
328 | |||
329 | for (i = 0; i < nr; i++) { | ||
330 | if (cmds[i].p_type == PT_LOAD) { | ||
331 | last_idx = i; | ||
332 | if (first_idx == -1) | ||
333 | first_idx = i; | ||
334 | } | ||
335 | } | ||
336 | if (first_idx == -1) | ||
337 | return 0; | ||
338 | |||
339 | return cmds[last_idx].p_vaddr + cmds[last_idx].p_memsz - | ||
340 | ELF_PAGESTART(cmds[first_idx].p_vaddr); | ||
341 | } | ||
342 | |||
343 | |||
308 | /* This is much more generalized than the library routine read function, | 344 | /* This is much more generalized than the library routine read function, |
309 | so we keep this separate. Technically the library read function | 345 | so we keep this separate. Technically the library read function |
310 | is only provided so that we can read a.out libraries that have | 346 | is only provided so that we can read a.out libraries that have |
311 | an ELF header */ | 347 | an ELF header */ |
312 | 348 | ||
313 | static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, | 349 | static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, |
314 | struct file *interpreter, unsigned long *interp_load_addr) | 350 | struct file *interpreter, unsigned long *interp_map_addr, |
351 | unsigned long no_base) | ||
315 | { | 352 | { |
316 | struct elf_phdr *elf_phdata; | 353 | struct elf_phdr *elf_phdata; |
317 | struct elf_phdr *eppnt; | 354 | struct elf_phdr *eppnt; |
@@ -319,6 +356,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, | |||
319 | int load_addr_set = 0; | 356 | int load_addr_set = 0; |
320 | unsigned long last_bss = 0, elf_bss = 0; | 357 | unsigned long last_bss = 0, elf_bss = 0; |
321 | unsigned long error = ~0UL; | 358 | unsigned long error = ~0UL; |
359 | unsigned long total_size; | ||
322 | int retval, i, size; | 360 | int retval, i, size; |
323 | 361 | ||
324 | /* First of all, some simple consistency checks */ | 362 | /* First of all, some simple consistency checks */ |
@@ -357,6 +395,12 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, | |||
357 | goto out_close; | 395 | goto out_close; |
358 | } | 396 | } |
359 | 397 | ||
398 | total_size = total_mapping_size(elf_phdata, interp_elf_ex->e_phnum); | ||
399 | if (!total_size) { | ||
400 | error = -EINVAL; | ||
401 | goto out_close; | ||
402 | } | ||
403 | |||
360 | eppnt = elf_phdata; | 404 | eppnt = elf_phdata; |
361 | for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) { | 405 | for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) { |
362 | if (eppnt->p_type == PT_LOAD) { | 406 | if (eppnt->p_type == PT_LOAD) { |
@@ -374,9 +418,14 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, | |||
374 | vaddr = eppnt->p_vaddr; | 418 | vaddr = eppnt->p_vaddr; |
375 | if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) | 419 | if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) |
376 | elf_type |= MAP_FIXED; | 420 | elf_type |= MAP_FIXED; |
421 | else if (no_base && interp_elf_ex->e_type == ET_DYN) | ||
422 | load_addr = -vaddr; | ||
377 | 423 | ||
378 | map_addr = elf_map(interpreter, load_addr + vaddr, | 424 | map_addr = elf_map(interpreter, load_addr + vaddr, |
379 | eppnt, elf_prot, elf_type); | 425 | eppnt, elf_prot, elf_type, total_size); |
426 | total_size = 0; | ||
427 | if (!*interp_map_addr) | ||
428 | *interp_map_addr = map_addr; | ||
380 | error = map_addr; | 429 | error = map_addr; |
381 | if (BAD_ADDR(map_addr)) | 430 | if (BAD_ADDR(map_addr)) |
382 | goto out_close; | 431 | goto out_close; |
@@ -442,8 +491,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, | |||
442 | goto out_close; | 491 | goto out_close; |
443 | } | 492 | } |
444 | 493 | ||
445 | *interp_load_addr = load_addr; | 494 | error = load_addr; |
446 | error = ((unsigned long)interp_elf_ex->e_entry) + load_addr; | ||
447 | 495 | ||
448 | out_close: | 496 | out_close: |
449 | kfree(elf_phdata); | 497 | kfree(elf_phdata); |
@@ -540,7 +588,8 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) | |||
540 | int elf_exec_fileno; | 588 | int elf_exec_fileno; |
541 | int retval, i; | 589 | int retval, i; |
542 | unsigned int size; | 590 | unsigned int size; |
543 | unsigned long elf_entry, interp_load_addr = 0; | 591 | unsigned long elf_entry; |
592 | unsigned long interp_load_addr = 0; | ||
544 | unsigned long start_code, end_code, start_data, end_data; | 593 | unsigned long start_code, end_code, start_data, end_data; |
545 | unsigned long reloc_func_desc = 0; | 594 | unsigned long reloc_func_desc = 0; |
546 | char passed_fileno[6]; | 595 | char passed_fileno[6]; |
@@ -808,9 +857,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) | |||
808 | current->mm->start_stack = bprm->p; | 857 | current->mm->start_stack = bprm->p; |
809 | 858 | ||
810 | /* Now we do a little grungy work by mmaping the ELF image into | 859 | /* Now we do a little grungy work by mmaping the ELF image into |
811 | the correct location in memory. At this point, we assume that | 860 | the correct location in memory. */ |
812 | the image should be loaded at fixed address, not at a variable | ||
813 | address. */ | ||
814 | for(i = 0, elf_ppnt = elf_phdata; | 861 | for(i = 0, elf_ppnt = elf_phdata; |
815 | i < loc->elf_ex.e_phnum; i++, elf_ppnt++) { | 862 | i < loc->elf_ex.e_phnum; i++, elf_ppnt++) { |
816 | int elf_prot = 0, elf_flags; | 863 | int elf_prot = 0, elf_flags; |
@@ -864,11 +911,15 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) | |||
864 | * default mmap base, as well as whatever program they | 911 | * default mmap base, as well as whatever program they |
865 | * might try to exec. This is because the brk will | 912 | * might try to exec. This is because the brk will |
866 | * follow the loader, and is not movable. */ | 913 | * follow the loader, and is not movable. */ |
914 | #ifdef CONFIG_X86 | ||
915 | load_bias = 0; | ||
916 | #else | ||
867 | load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr); | 917 | load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr); |
918 | #endif | ||
868 | } | 919 | } |
869 | 920 | ||
870 | error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, | 921 | error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, |
871 | elf_prot, elf_flags); | 922 | elf_prot, elf_flags,0); |
872 | if (BAD_ADDR(error)) { | 923 | if (BAD_ADDR(error)) { |
873 | send_sig(SIGKILL, current, 0); | 924 | send_sig(SIGKILL, current, 0); |
874 | retval = IS_ERR((void *)error) ? | 925 | retval = IS_ERR((void *)error) ? |
@@ -944,13 +995,25 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) | |||
944 | } | 995 | } |
945 | 996 | ||
946 | if (elf_interpreter) { | 997 | if (elf_interpreter) { |
947 | if (interpreter_type == INTERPRETER_AOUT) | 998 | if (interpreter_type == INTERPRETER_AOUT) { |
948 | elf_entry = load_aout_interp(&loc->interp_ex, | 999 | elf_entry = load_aout_interp(&loc->interp_ex, |
949 | interpreter); | 1000 | interpreter); |
950 | else | 1001 | } else { |
1002 | unsigned long interp_map_addr; /* unused */ | ||
1003 | |||
951 | elf_entry = load_elf_interp(&loc->interp_elf_ex, | 1004 | elf_entry = load_elf_interp(&loc->interp_elf_ex, |
952 | interpreter, | 1005 | interpreter, |
953 | &interp_load_addr); | 1006 | &interp_map_addr, |
1007 | load_bias); | ||
1008 | if (!BAD_ADDR(elf_entry)) { | ||
1009 | /* | ||
1010 | * load_elf_interp() returns relocation | ||
1011 | * adjustment | ||
1012 | */ | ||
1013 | interp_load_addr = elf_entry; | ||
1014 | elf_entry += loc->interp_elf_ex.e_entry; | ||
1015 | } | ||
1016 | } | ||
954 | if (BAD_ADDR(elf_entry)) { | 1017 | if (BAD_ADDR(elf_entry)) { |
955 | force_sig(SIGSEGV, current); | 1018 | force_sig(SIGSEGV, current); |
956 | retval = IS_ERR((void *)elf_entry) ? | 1019 | retval = IS_ERR((void *)elf_entry) ? |