X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fbinfmt_elf.c;h=b7c1603cd4bd4ded0f70d06dcb8e84546bfdd4ba;hb=1be10a88cac5e589cdd2bcb0cf6a13ed30bcc233;hp=295cbaa0e58ac6592c07aea6934170ac91bc3b3a;hpb=b6a2fea39318e43fee84fa7b0b90d68bed92d2ba;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 295cbaa..b7c1603 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -12,30 +12,22 @@ #include #include #include -#include -#include #include #include -#include #include #include #include #include #include -#include -#include #include -#include #include #include #include #include -#include #include #include #include #include -#include #include #include #include @@ -45,14 +37,15 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs); static int load_elf_library(struct file *); -static unsigned long elf_map (struct file *, unsigned long, struct elf_phdr *, int, int, unsigned long); +static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *, + int, int, unsigned long); /* * If we don't support core dumping, then supply a NULL so we * don't even try. */ #if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE) -static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file); +static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit); #else #define elf_core_dump NULL #endif @@ -80,7 +73,7 @@ static struct linux_binfmt elf_format = { .hasvdso = 1 }; -#define BAD_ADDR(x) IS_ERR_VALUE(x) +#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE) static int set_brk(unsigned long start, unsigned long end) { @@ -116,7 +109,7 @@ static int padzero(unsigned long elf_bss) return 0; } -/* Let's use some macros to make this stack manipulation a litle clearer */ +/* Let's use some macros to make this stack manipulation a little clearer */ #ifdef CONFIG_STACK_GROWSUP #define STACK_ADD(sp, items) ((elf_addr_t __user *)(sp) + (items)) #define STACK_ROUND(sp, items) \ @@ -131,10 +124,18 @@ static int padzero(unsigned long elf_bss) #define STACK_ALLOC(sp, len) ({ sp -= len ; sp; }) #endif +#ifndef ELF_BASE_PLATFORM +/* + * AT_BASE_PLATFORM indicates the "real" hardware/microarchitecture. + * If the arch defines ELF_BASE_PLATFORM (in asm/elf.h), the value + * will be copied to the user stack in the same manner as AT_PLATFORM. + */ +#define ELF_BASE_PLATFORM NULL +#endif + static int create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, - int interp_aout, unsigned long load_addr, - unsigned long interp_load_addr) + unsigned long load_addr, unsigned long interp_load_addr) { unsigned long p = bprm->p; int argc = bprm->argc; @@ -143,14 +144,26 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, elf_addr_t __user *envp; elf_addr_t __user *sp; elf_addr_t __user *u_platform; + elf_addr_t __user *u_base_platform; + elf_addr_t __user *u_rand_bytes; const char *k_platform = ELF_PLATFORM; + const char *k_base_platform = ELF_BASE_PLATFORM; + unsigned char k_rand_bytes[16]; int items; elf_addr_t *elf_info; int ei_index = 0; - struct task_struct *tsk = current; + const struct cred *cred = current_cred(); struct vm_area_struct *vma; /* + * In some cases (e.g. Hyper-Threading), we want to avoid L1 + * evictions by the processes running on the same package. One + * thing we can do is to shuffle the initial stack for them. + */ + + p = arch_align_stack(p); + + /* * If this architecture has a platform capability string, copy it * to userspace. In some cases (Sparc), this info is impossible * for userspace to get any other way, in others (i386) it is @@ -160,21 +173,36 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, if (k_platform) { size_t len = strlen(k_platform) + 1; - /* - * In some cases (e.g. Hyper-Threading), we want to avoid L1 - * evictions by the processes running on the same package. One - * thing we can do is to shuffle the initial stack for them. - */ - - p = arch_align_stack(p); - u_platform = (elf_addr_t __user *)STACK_ALLOC(p, len); if (__copy_to_user(u_platform, k_platform, len)) return -EFAULT; } + /* + * If this architecture has a "base" platform capability + * string, copy it to userspace. + */ + u_base_platform = NULL; + if (k_base_platform) { + size_t len = strlen(k_base_platform) + 1; + + u_base_platform = (elf_addr_t __user *)STACK_ALLOC(p, len); + if (__copy_to_user(u_base_platform, k_base_platform, len)) + return -EFAULT; + } + + /* + * Generate 16 random bytes for userspace PRNG seeding. + */ + get_random_bytes(k_rand_bytes, sizeof(k_rand_bytes)); + u_rand_bytes = (elf_addr_t __user *) + STACK_ALLOC(p, sizeof(k_rand_bytes)); + if (__copy_to_user(u_rand_bytes, k_rand_bytes, sizeof(k_rand_bytes))) + return -EFAULT; + /* Create the ELF interpreter info */ elf_info = (elf_addr_t *)current->mm->saved_auxv; + /* update AT_VECTOR_SIZE_BASE if the number of NEW_AUX_ENT() changes */ #define NEW_AUX_ENT(id, val) \ do { \ elf_info[ei_index++] = id; \ @@ -185,6 +213,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, /* * ARCH_DLINFO must come first so PPC can do its special alignment of * AUXV. + * update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT() in + * ARCH_DLINFO changes */ ARCH_DLINFO; #endif @@ -197,15 +227,21 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, NEW_AUX_ENT(AT_BASE, interp_load_addr); NEW_AUX_ENT(AT_FLAGS, 0); NEW_AUX_ENT(AT_ENTRY, exec->e_entry); - NEW_AUX_ENT(AT_UID, tsk->uid); - NEW_AUX_ENT(AT_EUID, tsk->euid); - NEW_AUX_ENT(AT_GID, tsk->gid); - NEW_AUX_ENT(AT_EGID, tsk->egid); + NEW_AUX_ENT(AT_UID, cred->uid); + NEW_AUX_ENT(AT_EUID, cred->euid); + NEW_AUX_ENT(AT_GID, cred->gid); + NEW_AUX_ENT(AT_EGID, cred->egid); NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm)); + NEW_AUX_ENT(AT_RANDOM, (elf_addr_t)(unsigned long)u_rand_bytes); + NEW_AUX_ENT(AT_EXECFN, bprm->exec); if (k_platform) { NEW_AUX_ENT(AT_PLATFORM, (elf_addr_t)(unsigned long)u_platform); } + if (k_base_platform) { + NEW_AUX_ENT(AT_BASE_PLATFORM, + (elf_addr_t)(unsigned long)u_base_platform); + } if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) { NEW_AUX_ENT(AT_EXECFD, bprm->interp_data); } @@ -219,12 +255,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, sp = STACK_ADD(p, ei_index); - items = (argc + 1) + (envc + 1); - if (interp_aout) { - items += 3; /* a.out interpreters require argv & envp too */ - } else { - items += 1; /* ELF interpreters only put argc on the stack */ - } + items = (argc + 1) + (envc + 1) + 1; bprm->p = STACK_ROUND(sp, items); /* Point sp at the lowest address on the stack */ @@ -247,16 +278,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, /* Now, let's put argc (and argv, envp if appropriate) on the stack */ if (__put_user(argc, sp++)) return -EFAULT; - if (interp_aout) { - argv = sp + 2; - envp = argv + argc + 1; - if (__put_user((elf_addr_t)(unsigned long)argv, sp++) || - __put_user((elf_addr_t)(unsigned long)envp, sp++)) - return -EFAULT; - } else { - argv = sp; - envp = argv + argc + 1; - } + argv = sp; + envp = argv + argc + 1; /* Populate argv and envp */ p = current->mm->arg_end = current->mm->arg_start; @@ -266,7 +289,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, return -EFAULT; len = strnlen_user((void __user *)p, MAX_ARG_STRLEN); if (!len || len > MAX_ARG_STRLEN) - return 0; + return -EINVAL; p += len; } if (__put_user(0, argv)) @@ -278,7 +301,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, return -EFAULT; len = strnlen_user((void __user *)p, MAX_ARG_STRLEN); if (!len || len > MAX_ARG_STRLEN) - return 0; + return -EINVAL; p += len; } if (__put_user(0, envp)) @@ -432,7 +455,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, load_addr = -vaddr; map_addr = elf_map(interpreter, load_addr + vaddr, - eppnt, elf_prot, elf_type, total_size); + eppnt, elf_prot, elf_type, total_size); total_size = 0; if (!*interp_map_addr) *interp_map_addr = map_addr; @@ -509,59 +532,12 @@ out: return error; } -static unsigned long load_aout_interp(struct exec *interp_ex, - struct file *interpreter) -{ - unsigned long text_data, elf_entry = ~0UL; - char __user * addr; - loff_t offset; - - current->mm->end_code = interp_ex->a_text; - text_data = interp_ex->a_text + interp_ex->a_data; - current->mm->end_data = text_data; - current->mm->brk = interp_ex->a_bss + text_data; - - switch (N_MAGIC(*interp_ex)) { - case OMAGIC: - offset = 32; - addr = (char __user *)0; - break; - case ZMAGIC: - case QMAGIC: - offset = N_TXTOFF(*interp_ex); - addr = (char __user *)N_TXTADDR(*interp_ex); - break; - default: - goto out; - } - - down_write(¤t->mm->mmap_sem); - do_brk(0, text_data); - up_write(¤t->mm->mmap_sem); - if (!interpreter->f_op || !interpreter->f_op->read) - goto out; - if (interpreter->f_op->read(interpreter, addr, text_data, &offset) < 0) - goto out; - flush_icache_range((unsigned long)addr, - (unsigned long)addr + text_data); - - down_write(¤t->mm->mmap_sem); - do_brk(ELF_PAGESTART(text_data + ELF_MIN_ALIGN - 1), - interp_ex->a_bss); - up_write(¤t->mm->mmap_sem); - elf_entry = interp_ex->a_entry; - -out: - return elf_entry; -} - /* * These are the functions used to load ELF style executables and shared * libraries. There is no binary dependent code anywhere else. */ #define INTERPRETER_NONE 0 -#define INTERPRETER_AOUT 1 #define INTERPRETER_ELF 2 #ifndef STACK_RND_MASK @@ -590,26 +566,20 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) unsigned long load_addr = 0, load_bias = 0; int load_addr_set = 0; char * elf_interpreter = NULL; - unsigned int interpreter_type = INTERPRETER_NONE; - unsigned char ibcs2_interpreter = 0; unsigned long error; struct elf_phdr *elf_ppnt, *elf_phdata; unsigned long elf_bss, elf_brk; - int elf_exec_fileno; int retval, i; unsigned int size; unsigned long elf_entry; unsigned long interp_load_addr = 0; unsigned long start_code, end_code, start_data, end_data; unsigned long reloc_func_desc = 0; - char passed_fileno[6]; - struct files_struct *files; int executable_stack = EXSTACK_DEFAULT; unsigned long def_flags = 0; struct { struct elfhdr elf_ex; struct elfhdr interp_elf_ex; - struct exec interp_ex; } *loc; loc = kmalloc(sizeof(*loc), GFP_KERNEL); @@ -653,23 +623,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) goto out_free_ph; } - files = current->files; /* Refcounted so ok */ - retval = unshare_files(); - if (retval < 0) - goto out_free_ph; - if (files == current->files) { - put_files_struct(files); - files = NULL; - } - - /* exec will make our files private anyway, but for the a.out - loader stuff we need to do it earlier */ - retval = get_unused_fd(); - if (retval < 0) - goto out_free_fh; - get_file(bprm->file); - fd_install(elf_exec_fileno = retval, bprm->file); - elf_ppnt = elf_phdata; elf_bss = 0; elf_brk = 0; @@ -688,13 +641,13 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) retval = -ENOEXEC; if (elf_ppnt->p_filesz > PATH_MAX || elf_ppnt->p_filesz < 2) - goto out_free_file; + goto out_free_ph; retval = -ENOMEM; elf_interpreter = kmalloc(elf_ppnt->p_filesz, GFP_KERNEL); if (!elf_interpreter) - goto out_free_file; + goto out_free_ph; retval = kernel_read(bprm->file, elf_ppnt->p_offset, elf_interpreter, @@ -709,14 +662,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0') goto out_free_interp; - /* If the program interpreter is one of these two, - * then assume an iBCS2 image. Otherwise assume - * a native linux image. - */ - if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 || - strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) - ibcs2_interpreter = 1; - /* * The early SET_PERSONALITY here is so that the lookup * for the interpreter happens in the namespace of the @@ -736,7 +681,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) * switch really is going to happen - do this in * flush_thread(). - akpm */ - SET_PERSONALITY(loc->elf_ex, ibcs2_interpreter); + SET_PERSONALITY(loc->elf_ex); interpreter = open_exec(elf_interpreter); retval = PTR_ERR(interpreter); @@ -760,7 +705,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) } /* Get the exec headers */ - loc->interp_ex = *((struct exec *)bprm->buf); loc->interp_elf_ex = *((struct elfhdr *)bprm->buf); break; } @@ -779,49 +723,16 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) /* Some simple consistency checks for the interpreter */ if (elf_interpreter) { - interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT; - - /* Now figure out which format our binary is */ - if ((N_MAGIC(loc->interp_ex) != OMAGIC) && - (N_MAGIC(loc->interp_ex) != ZMAGIC) && - (N_MAGIC(loc->interp_ex) != QMAGIC)) - interpreter_type = INTERPRETER_ELF; - - if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0) - interpreter_type &= ~INTERPRETER_ELF; - retval = -ELIBBAD; - if (!interpreter_type) + /* Not an ELF interpreter */ + if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0) goto out_free_dentry; - - /* Make sure only one type was selected */ - if ((interpreter_type & INTERPRETER_ELF) && - interpreter_type != INTERPRETER_ELF) { - // FIXME - ratelimit this before re-enabling - // printk(KERN_WARNING "ELF: Ambiguous type, using ELF\n"); - interpreter_type = INTERPRETER_ELF; - } /* Verify the interpreter has a valid arch */ - if ((interpreter_type == INTERPRETER_ELF) && - !elf_check_arch(&loc->interp_elf_ex)) + if (!elf_check_arch(&loc->interp_elf_ex)) goto out_free_dentry; } else { /* Executables without an interpreter also need a personality */ - SET_PERSONALITY(loc->elf_ex, ibcs2_interpreter); - } - - /* OK, we are done with that, now set up the arg stuff, - and then start this sucker up */ - if ((!bprm->sh_bang) && (interpreter_type == INTERPRETER_AOUT)) { - char *passed_p = passed_fileno; - sprintf(passed_fileno, "%d", elf_exec_fileno); - - if (elf_interpreter) { - retval = copy_strings_kernel(1, &passed_p, bprm); - if (retval) - goto out_free_dentry; - bprm->argc++; - } + SET_PERSONALITY(loc->elf_ex); } /* Flush all traces of the currently running executable */ @@ -829,19 +740,13 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) if (retval) goto out_free_dentry; - /* Discard our unneeded old files struct */ - if (files) { - put_files_struct(files); - files = NULL; - } - /* OK, This is the point of no return */ current->flags &= ~PF_FORKNOEXEC; current->mm->def_flags = def_flags; /* Do this immediately, since STACK_TOP as used in setup_arg_pages may depend on the personality. */ - SET_PERSONALITY(loc->elf_ex, ibcs2_interpreter); + SET_PERSONALITY(loc->elf_ex); if (elf_read_implies_exec(loc->elf_ex, executable_stack)) current->personality |= READ_IMPLIES_EXEC; @@ -925,7 +830,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) } error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, - elf_prot, elf_flags,0); + elf_prot, elf_flags, 0); if (BAD_ADDR(error)) { send_sig(SIGKILL, current, 0); retval = IS_ERR((void *)error) ? @@ -1001,24 +906,19 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) } if (elf_interpreter) { - if (interpreter_type == INTERPRETER_AOUT) { - elf_entry = load_aout_interp(&loc->interp_ex, - interpreter); - } else { - unsigned long uninitialized_var(interp_map_addr); - - elf_entry = load_elf_interp(&loc->interp_elf_ex, - interpreter, - &interp_map_addr, - load_bias); - if (!BAD_ADDR(elf_entry)) { - /* - * load_elf_interp() returns relocation - * adjustment - */ - interp_load_addr = elf_entry; - elf_entry += loc->interp_elf_ex.e_entry; - } + unsigned long uninitialized_var(interp_map_addr); + + elf_entry = load_elf_interp(&loc->interp_elf_ex, + interpreter, + &interp_map_addr, + load_bias); + if (!IS_ERR((void *)elf_entry)) { + /* + * load_elf_interp() returns relocation + * adjustment + */ + interp_load_addr = elf_entry; + elf_entry += loc->interp_elf_ex.e_entry; } if (BAD_ADDR(elf_entry)) { force_sig(SIGSEGV, current); @@ -1042,37 +942,37 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) kfree(elf_phdata); - if (interpreter_type != INTERPRETER_AOUT) - sys_close(elf_exec_fileno); - set_binfmt(&elf_format); #ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES - retval = arch_setup_additional_pages(bprm, executable_stack); + retval = arch_setup_additional_pages(bprm, !!elf_interpreter); if (retval < 0) { send_sig(SIGKILL, current, 0); goto out; } #endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */ - compute_creds(bprm); + install_exec_creds(bprm); current->flags &= ~PF_FORKNOEXEC; retval = create_elf_tables(bprm, &loc->elf_ex, - (interpreter_type == INTERPRETER_AOUT), load_addr, interp_load_addr); if (retval < 0) { send_sig(SIGKILL, current, 0); goto out; } /* N.B. passed_fileno might not be initialized? */ - if (interpreter_type == INTERPRETER_AOUT) - current->mm->arg_start += strlen(passed_fileno) + 1; current->mm->end_code = end_code; current->mm->start_code = start_code; current->mm->start_data = start_data; current->mm->end_data = end_data; current->mm->start_stack = bprm->p; +#ifdef arch_randomize_brk + if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) + current->mm->brk = current->mm->start_brk = + arch_randomize_brk(current->mm); +#endif + if (current->personality & MMAP_PAGE_ZERO) { /* Why this, you ask??? Well SVr4 maps page 0 as read-only, and some applications "depend" upon this behavior. @@ -1099,12 +999,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) #endif start_thread(regs, elf_entry, bprm->p); - if (unlikely(current->ptrace & PT_PTRACED)) { - if (current->ptrace & PT_TRACE_EXEC) - ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP); - else - send_sig(SIGTRAP, current, 0); - } retval = 0; out: kfree(loc); @@ -1118,11 +1012,6 @@ out_free_dentry: fput(interpreter); out_free_interp: kfree(elf_interpreter); -out_free_file: - sys_close(elf_exec_fileno); -out_free_fh: - if (files) - reset_files_struct(current, files); out_free_ph: kfree(elf_phdata); goto out; @@ -1256,31 +1145,86 @@ static int dump_seek(struct file *file, loff_t off) } /* - * Decide whether a segment is worth dumping; default is yes to be - * sure (missing info is worse than too much; etc). - * Personally I'd include everything, and use the coredump limit... - * - * I think we should skip something. But I am not sure how. H.J. + * Decide what to dump of a segment, part, all or none. */ -static int maydump(struct vm_area_struct *vma) +static unsigned long vma_dump_size(struct vm_area_struct *vma, + unsigned long mm_flags) { +#define FILTER(type) (mm_flags & (1UL << MMF_DUMP_##type)) + /* The vma can be set up to tell us the answer directly. */ if (vma->vm_flags & VM_ALWAYSDUMP) - return 1; + goto whole; + + /* Hugetlb memory check */ + if (vma->vm_flags & VM_HUGETLB) { + if ((vma->vm_flags & VM_SHARED) && FILTER(HUGETLB_SHARED)) + goto whole; + if (!(vma->vm_flags & VM_SHARED) && FILTER(HUGETLB_PRIVATE)) + goto whole; + } /* Do not dump I/O mapped devices or special mappings */ if (vma->vm_flags & (VM_IO | VM_RESERVED)) return 0; - /* Dump shared memory only if mapped from an anonymous file. */ - if (vma->vm_flags & VM_SHARED) - return vma->vm_file->f_path.dentry->d_inode->i_nlink == 0; + /* By default, dump shared memory if mapped from an anonymous file. */ + if (vma->vm_flags & VM_SHARED) { + if (vma->vm_file->f_path.dentry->d_inode->i_nlink == 0 ? + FILTER(ANON_SHARED) : FILTER(MAPPED_SHARED)) + goto whole; + return 0; + } - /* If it hasn't been written to, don't write it out */ - if (!vma->anon_vma) + /* Dump segments that have been written to. */ + if (vma->anon_vma && FILTER(ANON_PRIVATE)) + goto whole; + if (vma->vm_file == NULL) return 0; - return 1; + if (FILTER(MAPPED_PRIVATE)) + goto whole; + + /* + * If this looks like the beginning of a DSO or executable mapping, + * check for an ELF header. If we find one, dump the first page to + * aid in determining what was mapped here. + */ + if (FILTER(ELF_HEADERS) && + vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ)) { + u32 __user *header = (u32 __user *) vma->vm_start; + u32 word; + mm_segment_t fs = get_fs(); + /* + * Doing it this way gets the constant folded by GCC. + */ + union { + u32 cmp; + char elfmag[SELFMAG]; + } magic; + BUILD_BUG_ON(SELFMAG != sizeof word); + magic.elfmag[EI_MAG0] = ELFMAG0; + magic.elfmag[EI_MAG1] = ELFMAG1; + magic.elfmag[EI_MAG2] = ELFMAG2; + magic.elfmag[EI_MAG3] = ELFMAG3; + /* + * Switch to the user "segment" for get_user(), + * then put back what elf_core_dump() had in place. + */ + set_fs(USER_DS); + if (unlikely(get_user(word, header))) + word = 0; + set_fs(fs); + if (word == magic.cmp) + return PAGE_SIZE; + } + +#undef FILTER + + return 0; + +whole: + return vma->vm_end - vma->vm_start; } /* An ELF note in memory */ @@ -1340,28 +1284,26 @@ static int writenote(struct memelfnote *men, struct file *file, if (!dump_seek(file, (off))) \ goto end_coredump; -static void fill_elf_header(struct elfhdr *elf, int segs) +static void fill_elf_header(struct elfhdr *elf, int segs, + u16 machine, u32 flags, u8 osabi) { + memset(elf, 0, sizeof(*elf)); + memcpy(elf->e_ident, ELFMAG, SELFMAG); elf->e_ident[EI_CLASS] = ELF_CLASS; elf->e_ident[EI_DATA] = ELF_DATA; elf->e_ident[EI_VERSION] = EV_CURRENT; elf->e_ident[EI_OSABI] = ELF_OSABI; - memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); elf->e_type = ET_CORE; - elf->e_machine = ELF_ARCH; + elf->e_machine = machine; elf->e_version = EV_CURRENT; - elf->e_entry = 0; elf->e_phoff = sizeof(struct elfhdr); - elf->e_shoff = 0; - elf->e_flags = ELF_CORE_EFLAGS; + elf->e_flags = flags; elf->e_ehsize = sizeof(struct elfhdr); elf->e_phentsize = sizeof(struct elf_phdr); elf->e_phnum = segs; - elf->e_shentsize = 0; - elf->e_shnum = 0; - elf->e_shstrndx = 0; + return; } @@ -1398,25 +1340,22 @@ static void fill_prstatus(struct elf_prstatus *prstatus, prstatus->pr_info.si_signo = prstatus->pr_cursig = signr; prstatus->pr_sigpend = p->pending.signal.sig[0]; prstatus->pr_sighold = p->blocked.sig[0]; - prstatus->pr_pid = p->pid; - prstatus->pr_ppid = p->parent->pid; - prstatus->pr_pgrp = process_group(p); - prstatus->pr_sid = process_session(p); + rcu_read_lock(); + prstatus->pr_ppid = task_pid_vnr(rcu_dereference(p->real_parent)); + rcu_read_unlock(); + prstatus->pr_pid = task_pid_vnr(p); + prstatus->pr_pgrp = task_pgrp_vnr(p); + prstatus->pr_sid = task_session_vnr(p); if (thread_group_leader(p)) { + struct task_cputime cputime; + /* - * This is the record for the group leader. Add in the - * cumulative times of previous dead threads. This total - * won't include the time of each live thread whose state - * is included in the core dump. The final total reported - * to our parent process when it calls wait4 will include - * those sums as well as the little bit more time it takes - * this and each other thread to finish dying after the - * core dump synchronization phase. + * This is the record for the group leader. It shows the + * group-wide total, not its individual thread total. */ - cputime_to_timeval(cputime_add(p->utime, p->signal->utime), - &prstatus->pr_utime); - cputime_to_timeval(cputime_add(p->stime, p->signal->stime), - &prstatus->pr_stime); + thread_group_cputime(p, &cputime); + cputime_to_timeval(cputime.utime, &prstatus->pr_utime); + cputime_to_timeval(cputime.stime, &prstatus->pr_stime); } else { cputime_to_timeval(p->utime, &prstatus->pr_utime); cputime_to_timeval(p->stime, &prstatus->pr_stime); @@ -1428,6 +1367,7 @@ static void fill_prstatus(struct elf_prstatus *prstatus, static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, struct mm_struct *mm) { + const struct cred *cred; unsigned int i, len; /* first copy the parameters from user space */ @@ -1444,10 +1384,12 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, psinfo->pr_psargs[i] = ' '; psinfo->pr_psargs[len] = 0; - psinfo->pr_pid = p->pid; - psinfo->pr_ppid = p->parent->pid; - psinfo->pr_pgrp = process_group(p); - psinfo->pr_sid = process_session(p); + rcu_read_lock(); + psinfo->pr_ppid = task_pid_vnr(rcu_dereference(p->real_parent)); + rcu_read_unlock(); + psinfo->pr_pid = task_pid_vnr(p); + psinfo->pr_pgrp = task_pgrp_vnr(p); + psinfo->pr_sid = task_session_vnr(p); i = p->state ? ffz(~p->state) + 1 : 0; psinfo->pr_state = i; @@ -1455,13 +1397,258 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, psinfo->pr_zomb = psinfo->pr_sname == 'Z'; psinfo->pr_nice = task_nice(p); psinfo->pr_flag = p->flags; - SET_UID(psinfo->pr_uid, p->uid); - SET_GID(psinfo->pr_gid, p->gid); + rcu_read_lock(); + cred = __task_cred(p); + SET_UID(psinfo->pr_uid, cred->uid); + SET_GID(psinfo->pr_gid, cred->gid); + rcu_read_unlock(); strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname)); return 0; } +static void fill_auxv_note(struct memelfnote *note, struct mm_struct *mm) +{ + elf_addr_t *auxv = (elf_addr_t *) mm->saved_auxv; + int i = 0; + do + i += 2; + while (auxv[i - 2] != AT_NULL); + fill_note(note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv); +} + +#ifdef CORE_DUMP_USE_REGSET +#include + +struct elf_thread_core_info { + struct elf_thread_core_info *next; + struct task_struct *task; + struct elf_prstatus prstatus; + struct memelfnote notes[0]; +}; + +struct elf_note_info { + struct elf_thread_core_info *thread; + struct memelfnote psinfo; + struct memelfnote auxv; + size_t size; + int thread_notes; +}; + +/* + * When a regset has a writeback hook, we call it on each thread before + * dumping user memory. On register window machines, this makes sure the + * user memory backing the register data is up to date before we read it. + */ +static void do_thread_regset_writeback(struct task_struct *task, + const struct user_regset *regset) +{ + if (regset->writeback) + regset->writeback(task, regset, 1); +} + +static int fill_thread_core_info(struct elf_thread_core_info *t, + const struct user_regset_view *view, + long signr, size_t *total) +{ + unsigned int i; + + /* + * NT_PRSTATUS is the one special case, because the regset data + * goes into the pr_reg field inside the note contents, rather + * than being the whole note contents. We fill the reset in here. + * We assume that regset 0 is NT_PRSTATUS. + */ + fill_prstatus(&t->prstatus, t->task, signr); + (void) view->regsets[0].get(t->task, &view->regsets[0], + 0, sizeof(t->prstatus.pr_reg), + &t->prstatus.pr_reg, NULL); + + fill_note(&t->notes[0], "CORE", NT_PRSTATUS, + sizeof(t->prstatus), &t->prstatus); + *total += notesize(&t->notes[0]); + + do_thread_regset_writeback(t->task, &view->regsets[0]); + + /* + * Each other regset might generate a note too. For each regset + * that has no core_note_type or is inactive, we leave t->notes[i] + * all zero and we'll know to skip writing it later. + */ + for (i = 1; i < view->n; ++i) { + const struct user_regset *regset = &view->regsets[i]; + do_thread_regset_writeback(t->task, regset); + if (regset->core_note_type && + (!regset->active || regset->active(t->task, regset))) { + int ret; + size_t size = regset->n * regset->size; + void *data = kmalloc(size, GFP_KERNEL); + if (unlikely(!data)) + return 0; + ret = regset->get(t->task, regset, + 0, size, data, NULL); + if (unlikely(ret)) + kfree(data); + else { + if (regset->core_note_type != NT_PRFPREG) + fill_note(&t->notes[i], "LINUX", + regset->core_note_type, + size, data); + else { + t->prstatus.pr_fpvalid = 1; + fill_note(&t->notes[i], "CORE", + NT_PRFPREG, size, data); + } + *total += notesize(&t->notes[i]); + } + } + } + + return 1; +} + +static int fill_note_info(struct elfhdr *elf, int phdrs, + struct elf_note_info *info, + long signr, struct pt_regs *regs) +{ + struct task_struct *dump_task = current; + const struct user_regset_view *view = task_user_regset_view(dump_task); + struct elf_thread_core_info *t; + struct elf_prpsinfo *psinfo; + struct core_thread *ct; + unsigned int i; + + info->size = 0; + info->thread = NULL; + + psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); + if (psinfo == NULL) + return 0; + + fill_note(&info->psinfo, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); + + /* + * Figure out how many notes we're going to need for each thread. + */ + info->thread_notes = 0; + for (i = 0; i < view->n; ++i) + if (view->regsets[i].core_note_type != 0) + ++info->thread_notes; + + /* + * Sanity check. We rely on regset 0 being in NT_PRSTATUS, + * since it is our one special case. + */ + if (unlikely(info->thread_notes == 0) || + unlikely(view->regsets[0].core_note_type != NT_PRSTATUS)) { + WARN_ON(1); + return 0; + } + + /* + * Initialize the ELF file header. + */ + fill_elf_header(elf, phdrs, + view->e_machine, view->e_flags, view->ei_osabi); + + /* + * Allocate a structure for each thread. + */ + for (ct = &dump_task->mm->core_state->dumper; ct; ct = ct->next) { + t = kzalloc(offsetof(struct elf_thread_core_info, + notes[info->thread_notes]), + GFP_KERNEL); + if (unlikely(!t)) + return 0; + + t->task = ct->task; + if (ct->task == dump_task || !info->thread) { + t->next = info->thread; + info->thread = t; + } else { + /* + * Make sure to keep the original task at + * the head of the list. + */ + t->next = info->thread->next; + info->thread->next = t; + } + } + + /* + * Now fill in each thread's information. + */ + for (t = info->thread; t != NULL; t = t->next) + if (!fill_thread_core_info(t, view, signr, &info->size)) + return 0; + + /* + * Fill in the two process-wide notes. + */ + fill_psinfo(psinfo, dump_task->group_leader, dump_task->mm); + info->size += notesize(&info->psinfo); + + fill_auxv_note(&info->auxv, current->mm); + info->size += notesize(&info->auxv); + + return 1; +} + +static size_t get_note_info_size(struct elf_note_info *info) +{ + return info->size; +} + +/* + * Write all the notes for each thread. When writing the first thread, the + * process-wide notes are interleaved after the first thread-specific note. + */ +static int write_note_info(struct elf_note_info *info, + struct file *file, loff_t *foffset) +{ + bool first = 1; + struct elf_thread_core_info *t = info->thread; + + do { + int i; + + if (!writenote(&t->notes[0], file, foffset)) + return 0; + + if (first && !writenote(&info->psinfo, file, foffset)) + return 0; + if (first && !writenote(&info->auxv, file, foffset)) + return 0; + + for (i = 1; i < info->thread_notes; ++i) + if (t->notes[i].data && + !writenote(&t->notes[i], file, foffset)) + return 0; + + first = 0; + t = t->next; + } while (t); + + return 1; +} + +static void free_note_info(struct elf_note_info *info) +{ + struct elf_thread_core_info *threads = info->thread; + while (threads) { + unsigned int i; + struct elf_thread_core_info *t = threads; + threads = t->next; + WARN_ON(t->notes[0].data && t->notes[0].data != &t->prstatus); + for (i = 1; i < info->thread_notes; ++i) + kfree(t->notes[i].data); + kfree(t); + } + kfree(info->psinfo.data); +} + +#else + /* Here is the structure in which status of each thread is captured. */ struct elf_thread_status { @@ -1470,7 +1657,7 @@ struct elf_thread_status elf_fpregset_t fpu; /* NT_PRFPREG */ struct task_struct *thread; #ifdef ELF_CORE_COPY_XFPREGS - elf_fpxregset_t xfpu; /* NT_PRXFPREG */ + elf_fpxregset_t xfpu; /* ELF_CORE_XFPREG_TYPE */ #endif struct memelfnote notes[3]; int num_notes; @@ -1505,8 +1692,8 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) #ifdef ELF_CORE_COPY_XFPREGS if (elf_core_copy_task_xfpregs(p, &t->xfpu)) { - fill_note(&t->notes[2], "LINUX", NT_PRXFPREG, sizeof(t->xfpu), - &t->xfpu); + fill_note(&t->notes[2], "LINUX", ELF_CORE_XFPREG_TYPE, + sizeof(t->xfpu), &t->xfpu); t->num_notes++; sz += notesize(&t->notes[2]); } @@ -1514,6 +1701,173 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) return sz; } +struct elf_note_info { + struct memelfnote *notes; + struct elf_prstatus *prstatus; /* NT_PRSTATUS */ + struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */ + struct list_head thread_list; + elf_fpregset_t *fpu; +#ifdef ELF_CORE_COPY_XFPREGS + elf_fpxregset_t *xfpu; +#endif + int thread_status_size; + int numnote; +}; + +static int fill_note_info(struct elfhdr *elf, int phdrs, + struct elf_note_info *info, + long signr, struct pt_regs *regs) +{ +#define NUM_NOTES 6 + struct list_head *t; + + info->notes = NULL; + info->prstatus = NULL; + info->psinfo = NULL; + info->fpu = NULL; +#ifdef ELF_CORE_COPY_XFPREGS + info->xfpu = NULL; +#endif + INIT_LIST_HEAD(&info->thread_list); + + info->notes = kmalloc(NUM_NOTES * sizeof(struct memelfnote), + GFP_KERNEL); + if (!info->notes) + return 0; + info->psinfo = kmalloc(sizeof(*info->psinfo), GFP_KERNEL); + if (!info->psinfo) + return 0; + info->prstatus = kmalloc(sizeof(*info->prstatus), GFP_KERNEL); + if (!info->prstatus) + return 0; + info->fpu = kmalloc(sizeof(*info->fpu), GFP_KERNEL); + if (!info->fpu) + return 0; +#ifdef ELF_CORE_COPY_XFPREGS + info->xfpu = kmalloc(sizeof(*info->xfpu), GFP_KERNEL); + if (!info->xfpu) + return 0; +#endif + + info->thread_status_size = 0; + if (signr) { + struct core_thread *ct; + struct elf_thread_status *ets; + + for (ct = current->mm->core_state->dumper.next; + ct; ct = ct->next) { + ets = kzalloc(sizeof(*ets), GFP_KERNEL); + if (!ets) + return 0; + + ets->thread = ct->task; + list_add(&ets->list, &info->thread_list); + } + + list_for_each(t, &info->thread_list) { + int sz; + + ets = list_entry(t, struct elf_thread_status, list); + sz = elf_dump_thread_status(signr, ets); + info->thread_status_size += sz; + } + } + /* now collect the dump for the current */ + memset(info->prstatus, 0, sizeof(*info->prstatus)); + fill_prstatus(info->prstatus, current, signr); + elf_core_copy_regs(&info->prstatus->pr_reg, regs); + + /* Set up header */ + fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS, ELF_OSABI); + + /* + * Set up the notes in similar form to SVR4 core dumps made + * with info from their /proc. + */ + + fill_note(info->notes + 0, "CORE", NT_PRSTATUS, + sizeof(*info->prstatus), info->prstatus); + fill_psinfo(info->psinfo, current->group_leader, current->mm); + fill_note(info->notes + 1, "CORE", NT_PRPSINFO, + sizeof(*info->psinfo), info->psinfo); + + info->numnote = 2; + + fill_auxv_note(&info->notes[info->numnote++], current->mm); + + /* Try to dump the FPU. */ + info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs, + info->fpu); + if (info->prstatus->pr_fpvalid) + fill_note(info->notes + info->numnote++, + "CORE", NT_PRFPREG, sizeof(*info->fpu), info->fpu); +#ifdef ELF_CORE_COPY_XFPREGS + if (elf_core_copy_task_xfpregs(current, info->xfpu)) + fill_note(info->notes + info->numnote++, + "LINUX", ELF_CORE_XFPREG_TYPE, + sizeof(*info->xfpu), info->xfpu); +#endif + + return 1; + +#undef NUM_NOTES +} + +static size_t get_note_info_size(struct elf_note_info *info) +{ + int sz = 0; + int i; + + for (i = 0; i < info->numnote; i++) + sz += notesize(info->notes + i); + + sz += info->thread_status_size; + + return sz; +} + +static int write_note_info(struct elf_note_info *info, + struct file *file, loff_t *foffset) +{ + int i; + struct list_head *t; + + for (i = 0; i < info->numnote; i++) + if (!writenote(info->notes + i, file, foffset)) + return 0; + + /* write out the thread status notes section */ + list_for_each(t, &info->thread_list) { + struct elf_thread_status *tmp = + list_entry(t, struct elf_thread_status, list); + + for (i = 0; i < tmp->num_notes; i++) + if (!writenote(&tmp->notes[i], file, foffset)) + return 0; + } + + return 1; +} + +static void free_note_info(struct elf_note_info *info) +{ + while (!list_empty(&info->thread_list)) { + struct list_head *tmp = info->thread_list.next; + list_del(tmp); + kfree(list_entry(tmp, struct elf_thread_status, list)); + } + + kfree(info->prstatus); + kfree(info->psinfo); + kfree(info->notes); + kfree(info->fpu); +#ifdef ELF_CORE_COPY_XFPREGS + kfree(info->xfpu); +#endif +} + +#endif + static struct vm_area_struct *first_vma(struct task_struct *tsk, struct vm_area_struct *gate_vma) { @@ -1547,34 +1901,17 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma, * and then they are actually written out. If we run out of core limit * we just truncate. */ -static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) +static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit) { -#define NUM_NOTES 6 int has_dumped = 0; mm_segment_t fs; int segs; size_t size = 0; - int i; struct vm_area_struct *vma, *gate_vma; struct elfhdr *elf = NULL; loff_t offset = 0, dataoff, foffset; - unsigned long limit = current->signal->rlim[RLIMIT_CORE].rlim_cur; - int numnote; - struct memelfnote *notes = NULL; - struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */ - struct elf_prpsinfo *psinfo = NULL; /* NT_PRPSINFO */ - struct task_struct *g, *p; - LIST_HEAD(thread_list); - struct list_head *t; - elf_fpregset_t *fpu = NULL; -#ifdef ELF_CORE_COPY_XFPREGS - elf_fpxregset_t *xfpu = NULL; -#endif - int thread_status_size = 0; - elf_addr_t *auxv; -#ifdef ELF_CORE_WRITE_EXTRA_NOTES - int extra_notes_size; -#endif + unsigned long mm_flags; + struct elf_note_info info; /* * We no longer stop all VM operations. @@ -1591,54 +1928,11 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) /* alloc memory for large data structures: too large to be on stack */ elf = kmalloc(sizeof(*elf), GFP_KERNEL); if (!elf) - goto cleanup; - prstatus = kmalloc(sizeof(*prstatus), GFP_KERNEL); - if (!prstatus) - goto cleanup; - psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); - if (!psinfo) - goto cleanup; - notes = kmalloc(NUM_NOTES * sizeof(struct memelfnote), GFP_KERNEL); - if (!notes) - goto cleanup; - fpu = kmalloc(sizeof(*fpu), GFP_KERNEL); - if (!fpu) - goto cleanup; -#ifdef ELF_CORE_COPY_XFPREGS - xfpu = kmalloc(sizeof(*xfpu), GFP_KERNEL); - if (!xfpu) - goto cleanup; -#endif - - if (signr) { - struct elf_thread_status *tmp; - rcu_read_lock(); - do_each_thread(g,p) - if (current->mm == p->mm && current != p) { - tmp = kzalloc(sizeof(*tmp), GFP_ATOMIC); - if (!tmp) { - rcu_read_unlock(); - goto cleanup; - } - tmp->thread = p; - list_add(&tmp->list, &thread_list); - } - while_each_thread(g,p); - rcu_read_unlock(); - list_for_each(t, &thread_list) { - struct elf_thread_status *tmp; - int sz; - - tmp = list_entry(t, struct elf_thread_status, list); - sz = elf_dump_thread_status(signr, tmp); - thread_status_size += sz; - } - } - /* now collect the dump for the current */ - memset(prstatus, 0, sizeof(*prstatus)); - fill_prstatus(prstatus, current, signr); - elf_core_copy_regs(&prstatus->pr_reg, regs); - + goto out; + /* + * The number of segs are recored into ELF header as 16bit value. + * Please check DEFAULT_MAX_MAP_COUNT definition when you modify here. + */ segs = current->mm->map_count; #ifdef ELF_CORE_EXTRA_PHDRS segs += ELF_CORE_EXTRA_PHDRS; @@ -1648,42 +1942,16 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) if (gate_vma != NULL) segs++; - /* Set up header */ - fill_elf_header(elf, segs + 1); /* including notes section */ - - has_dumped = 1; - current->flags |= PF_DUMPCORE; - /* - * Set up the notes in similar form to SVR4 core dumps made - * with info from their /proc. + * Collect all the non-memory information about the process for the + * notes. This also sets up the file header. */ + if (!fill_note_info(elf, segs + 1, /* including notes section */ + &info, signr, regs)) + goto cleanup; - fill_note(notes + 0, "CORE", NT_PRSTATUS, sizeof(*prstatus), prstatus); - fill_psinfo(psinfo, current->group_leader, current->mm); - fill_note(notes + 1, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); - - numnote = 2; - - auxv = (elf_addr_t *)current->mm->saved_auxv; - - i = 0; - do - i += 2; - while (auxv[i - 2] != AT_NULL); - fill_note(¬es[numnote++], "CORE", NT_AUXV, - i * sizeof(elf_addr_t), auxv); - - /* Try to dump the FPU. */ - if ((prstatus->pr_fpvalid = - elf_core_copy_task_fpregs(current, regs, fpu))) - fill_note(notes + numnote++, - "CORE", NT_PRFPREG, sizeof(*fpu), fpu); -#ifdef ELF_CORE_COPY_XFPREGS - if (elf_core_copy_task_xfpregs(current, xfpu)) - fill_note(notes + numnote++, - "LINUX", NT_PRXFPREG, sizeof(*xfpu), xfpu); -#endif + has_dumped = 1; + current->flags |= PF_DUMPCORE; fs = get_fs(); set_fs(KERNEL_DS); @@ -1696,17 +1964,9 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) /* Write notes phdr entry */ { struct elf_phdr phdr; - int sz = 0; - - for (i = 0; i < numnote; i++) - sz += notesize(notes + i); - - sz += thread_status_size; + size_t sz = get_note_info_size(&info); -#ifdef ELF_CORE_WRITE_EXTRA_NOTES - extra_notes_size = ELF_CORE_EXTRA_NOTES_SIZE; - sz += extra_notes_size; -#endif + sz += elf_coredump_extra_notes_size(); fill_elf_note_phdr(&phdr, sz, offset); offset += sz; @@ -1715,20 +1975,24 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); + /* + * We must use the same mm->flags while dumping core to avoid + * inconsistency between the program headers and bodies, otherwise an + * unusable core file can be generated. + */ + mm_flags = current->mm->flags; + /* Write program headers for segments dump */ for (vma = first_vma(current, gate_vma); vma != NULL; vma = next_vma(vma, gate_vma)) { struct elf_phdr phdr; - size_t sz; - - sz = vma->vm_end - vma->vm_start; phdr.p_type = PT_LOAD; phdr.p_offset = offset; phdr.p_vaddr = vma->vm_start; phdr.p_paddr = 0; - phdr.p_filesz = maydump(vma) ? sz : 0; - phdr.p_memsz = sz; + phdr.p_filesz = vma_dump_size(vma, mm_flags); + phdr.p_memsz = vma->vm_end - vma->vm_start; offset += phdr.p_filesz; phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; if (vma->vm_flags & VM_WRITE) @@ -1745,24 +2009,11 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) #endif /* write out the notes section */ - for (i = 0; i < numnote; i++) - if (!writenote(notes + i, file, &foffset)) - goto end_coredump; - -#ifdef ELF_CORE_WRITE_EXTRA_NOTES - ELF_CORE_WRITE_EXTRA_NOTES; - foffset += extra_notes_size; -#endif - - /* write out the thread status notes section */ - list_for_each(t, &thread_list) { - struct elf_thread_status *tmp = - list_entry(t, struct elf_thread_status, list); + if (!write_note_info(&info, file, &foffset)) + goto end_coredump; - for (i = 0; i < tmp->num_notes; i++) - if (!writenote(&tmp->notes[i], file, &foffset)) - goto end_coredump; - } + if (elf_coredump_extra_notes_write(file, &foffset)) + goto end_coredump; /* Align to page */ DUMP_SEEK(dataoff - foffset); @@ -1770,28 +2021,26 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) for (vma = first_vma(current, gate_vma); vma != NULL; vma = next_vma(vma, gate_vma)) { unsigned long addr; + unsigned long end; - if (!maydump(vma)) - continue; + end = vma->vm_start + vma_dump_size(vma, mm_flags); - for (addr = vma->vm_start; - addr < vma->vm_end; - addr += PAGE_SIZE) { + for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) { struct page *page; - struct vm_area_struct *vma; + struct vm_area_struct *tmp_vma; if (get_user_pages(current, current->mm, addr, 1, 0, 1, - &page, &vma) <= 0) { + &page, &tmp_vma) <= 0) { DUMP_SEEK(PAGE_SIZE); } else { - if (page == ZERO_PAGE(addr)) { + if (page == ZERO_PAGE(0)) { if (!dump_seek(file, PAGE_SIZE)) { page_cache_release(page); goto end_coredump; } } else { void *kaddr; - flush_cache_page(vma, addr, + flush_cache_page(tmp_vma, addr, page_to_pfn(page)); kaddr = kmap(page); if ((size += PAGE_SIZE) > limit || @@ -1816,22 +2065,10 @@ end_coredump: set_fs(fs); cleanup: - while (!list_empty(&thread_list)) { - struct list_head *tmp = thread_list.next; - list_del(tmp); - kfree(list_entry(tmp, struct elf_thread_status, list)); - } - + free_note_info(&info); kfree(elf); - kfree(prstatus); - kfree(psinfo); - kfree(notes); - kfree(fpu); -#ifdef ELF_CORE_COPY_XFPREGS - kfree(xfpu); -#endif +out: return has_dumped; -#undef NUM_NOTES } #endif /* USE_ELF_CORE_DUMP */