diff options
Diffstat (limited to 'fs/binfmt_elf.c')
| -rw-r--r-- | fs/binfmt_elf.c | 172 | 
1 files changed, 143 insertions, 29 deletions
| diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 1b52956afe33..e800dec958c3 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -27,6 +27,7 @@  #include <linux/compiler.h>  #include <linux/highmem.h>  #include <linux/pagemap.h> +#include <linux/vmalloc.h>  #include <linux/security.h>  #include <linux/random.h>  #include <linux/elf.h> @@ -37,6 +38,13 @@  #include <asm/page.h>  #include <asm/exec.h> +#ifndef user_long_t +#define user_long_t long +#endif +#ifndef user_siginfo_t +#define user_siginfo_t siginfo_t +#endif +  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 *, @@ -881,7 +889,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)  	}  	if (elf_interpreter) { -		unsigned long uninitialized_var(interp_map_addr); +		unsigned long interp_map_addr = 0;  		elf_entry = load_elf_interp(&loc->interp_elf_ex,  					    interpreter, @@ -1115,7 +1123,7 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma,  	if (always_dump_vma(vma))  		goto whole; -	if (vma->vm_flags & VM_NODUMP) +	if (vma->vm_flags & VM_DONTDUMP)  		return 0;  	/* Hugetlb memory check */ @@ -1127,7 +1135,7 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma,  	}  	/* Do not dump I/O mapped devices or special mappings */ -	if (vma->vm_flags & (VM_IO | VM_RESERVED)) +	if (vma->vm_flags & VM_IO)  		return 0;  	/* By default, dump shared memory if mapped from an anonymous file. */ @@ -1372,6 +1380,103 @@ static void fill_auxv_note(struct memelfnote *note, struct mm_struct *mm)  	fill_note(note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv);  } +static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata, +		siginfo_t *siginfo) +{ +	mm_segment_t old_fs = get_fs(); +	set_fs(KERNEL_DS); +	copy_siginfo_to_user((user_siginfo_t __user *) csigdata, siginfo); +	set_fs(old_fs); +	fill_note(note, "CORE", NT_SIGINFO, sizeof(*csigdata), csigdata); +} + +#define MAX_FILE_NOTE_SIZE (4*1024*1024) +/* + * Format of NT_FILE note: + * + * long count     -- how many files are mapped + * long page_size -- units for file_ofs + * array of [COUNT] elements of + *   long start + *   long end + *   long file_ofs + * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... + */ +static void fill_files_note(struct memelfnote *note) +{ +	struct vm_area_struct *vma; +	unsigned count, size, names_ofs, remaining, n; +	user_long_t *data; +	user_long_t *start_end_ofs; +	char *name_base, *name_curpos; + +	/* *Estimated* file count and total data size needed */ +	count = current->mm->map_count; +	size = count * 64; + +	names_ofs = (2 + 3 * count) * sizeof(data[0]); + alloc: +	if (size >= MAX_FILE_NOTE_SIZE) /* paranoia check */ +		goto err; +	size = round_up(size, PAGE_SIZE); +	data = vmalloc(size); +	if (!data) +		goto err; + +	start_end_ofs = data + 2; +	name_base = name_curpos = ((char *)data) + names_ofs; +	remaining = size - names_ofs; +	count = 0; +	for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) { +		struct file *file; +		const char *filename; + +		file = vma->vm_file; +		if (!file) +			continue; +		filename = d_path(&file->f_path, name_curpos, remaining); +		if (IS_ERR(filename)) { +			if (PTR_ERR(filename) == -ENAMETOOLONG) { +				vfree(data); +				size = size * 5 / 4; +				goto alloc; +			} +			continue; +		} + +		/* d_path() fills at the end, move name down */ +		/* n = strlen(filename) + 1: */ +		n = (name_curpos + remaining) - filename; +		remaining = filename - name_curpos; +		memmove(name_curpos, filename, n); +		name_curpos += n; + +		*start_end_ofs++ = vma->vm_start; +		*start_end_ofs++ = vma->vm_end; +		*start_end_ofs++ = vma->vm_pgoff; +		count++; +	} + +	/* Now we know exact count of files, can store it */ +	data[0] = count; +	data[1] = PAGE_SIZE; +	/* +	 * Count usually is less than current->mm->map_count, +	 * we need to move filenames down. +	 */ +	n = current->mm->map_count - count; +	if (n != 0) { +		unsigned shift_bytes = n * 3 * sizeof(data[0]); +		memmove(name_base - shift_bytes, name_base, +			name_curpos - name_base); +		name_curpos -= shift_bytes; +	} + +	size = name_curpos - (char *)data; +	fill_note(note, "CORE", NT_FILE, size, data); + err: ; +} +  #ifdef CORE_DUMP_USE_REGSET  #include <linux/regset.h> @@ -1385,7 +1490,10 @@ struct elf_thread_core_info {  struct elf_note_info {  	struct elf_thread_core_info *thread;  	struct memelfnote psinfo; +	struct memelfnote signote;  	struct memelfnote auxv; +	struct memelfnote files; +	user_siginfo_t csigdata;  	size_t size;  	int thread_notes;  }; @@ -1480,7 +1588,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,  static int fill_note_info(struct elfhdr *elf, int phdrs,  			  struct elf_note_info *info, -			  long signr, struct pt_regs *regs) +			  siginfo_t *siginfo, struct pt_regs *regs)  {  	struct task_struct *dump_task = current;  	const struct user_regset_view *view = task_user_regset_view(dump_task); @@ -1550,7 +1658,7 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,  	 * 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)) +		if (!fill_thread_core_info(t, view, siginfo->si_signo, &info->size))  			return 0;  	/* @@ -1559,9 +1667,15 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,  	fill_psinfo(psinfo, dump_task->group_leader, dump_task->mm);  	info->size += notesize(&info->psinfo); +	fill_siginfo_note(&info->signote, &info->csigdata, siginfo); +	info->size += notesize(&info->signote); +  	fill_auxv_note(&info->auxv, current->mm);  	info->size += notesize(&info->auxv); +	fill_files_note(&info->files); +	info->size += notesize(&info->files); +  	return 1;  } @@ -1588,8 +1702,12 @@ static int write_note_info(struct elf_note_info *info,  		if (first && !writenote(&info->psinfo, file, foffset))  			return 0; +		if (first && !writenote(&info->signote, file, foffset)) +			return 0;  		if (first && !writenote(&info->auxv, file, foffset))  			return 0; +		if (first && !writenote(&info->files, file, foffset)) +			return 0;  		for (i = 1; i < info->thread_notes; ++i)  			if (t->notes[i].data && @@ -1616,6 +1734,7 @@ static void free_note_info(struct elf_note_info *info)  		kfree(t);  	}  	kfree(info->psinfo.data); +	vfree(info->files.data);  }  #else @@ -1681,6 +1800,7 @@ struct elf_note_info {  #ifdef ELF_CORE_COPY_XFPREGS  	elf_fpxregset_t *xfpu;  #endif +	user_siginfo_t csigdata;  	int thread_status_size;  	int numnote;  }; @@ -1690,48 +1810,37 @@ static int elf_note_info_init(struct elf_note_info *info)  	memset(info, 0, sizeof(*info));  	INIT_LIST_HEAD(&info->thread_list); -	/* Allocate space for six ELF notes */ -	info->notes = kmalloc(6 * sizeof(struct memelfnote), GFP_KERNEL); +	/* Allocate space for ELF notes */ +	info->notes = kmalloc(8 * sizeof(struct memelfnote), GFP_KERNEL);  	if (!info->notes)  		return 0;  	info->psinfo = kmalloc(sizeof(*info->psinfo), GFP_KERNEL);  	if (!info->psinfo) -		goto notes_free; +		return 0;  	info->prstatus = kmalloc(sizeof(*info->prstatus), GFP_KERNEL);  	if (!info->prstatus) -		goto psinfo_free; +		return 0;  	info->fpu = kmalloc(sizeof(*info->fpu), GFP_KERNEL);  	if (!info->fpu) -		goto prstatus_free; +		return 0;  #ifdef ELF_CORE_COPY_XFPREGS  	info->xfpu = kmalloc(sizeof(*info->xfpu), GFP_KERNEL);  	if (!info->xfpu) -		goto fpu_free; +		return 0;  #endif  	return 1; -#ifdef ELF_CORE_COPY_XFPREGS - fpu_free: -	kfree(info->fpu); -#endif - prstatus_free: -	kfree(info->prstatus); - psinfo_free: -	kfree(info->psinfo); - notes_free: -	kfree(info->notes); -	return 0;  }  static int fill_note_info(struct elfhdr *elf, int phdrs,  			  struct elf_note_info *info, -			  long signr, struct pt_regs *regs) +			  siginfo_t *siginfo, struct pt_regs *regs)  {  	struct list_head *t;  	if (!elf_note_info_init(info))  		return 0; -	if (signr) { +	if (siginfo->si_signo) {  		struct core_thread *ct;  		struct elf_thread_status *ets; @@ -1749,13 +1858,13 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,  			int sz;  			ets = list_entry(t, struct elf_thread_status, list); -			sz = elf_dump_thread_status(signr, ets); +			sz = elf_dump_thread_status(siginfo->si_signo, 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); +	fill_prstatus(info->prstatus, current, siginfo->si_signo);  	elf_core_copy_regs(&info->prstatus->pr_reg, regs);  	/* Set up header */ @@ -1772,9 +1881,11 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,  	fill_note(info->notes + 1, "CORE", NT_PRPSINFO,  		  sizeof(*info->psinfo), info->psinfo); -	info->numnote = 2; +	fill_siginfo_note(info->notes + 2, &info->csigdata, siginfo); +	fill_auxv_note(info->notes + 3, current->mm); +	fill_files_note(info->notes + 4); -	fill_auxv_note(&info->notes[info->numnote++], current->mm); +	info->numnote = 5;  	/* Try to dump the FPU. */  	info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs, @@ -1836,6 +1947,9 @@ static void free_note_info(struct elf_note_info *info)  		kfree(list_entry(tmp, struct elf_thread_status, list));  	} +	/* Free data allocated by fill_files_note(): */ +	vfree(info->notes[4].data); +  	kfree(info->prstatus);  	kfree(info->psinfo);  	kfree(info->notes); @@ -1962,7 +2076,7 @@ static int elf_core_dump(struct coredump_params *cprm)  	 * Collect all the non-memory information about the process for the  	 * notes.  This also sets up the file header.  	 */ -	if (!fill_note_info(elf, e_phnum, &info, cprm->signr, cprm->regs)) +	if (!fill_note_info(elf, e_phnum, &info, cprm->siginfo, cprm->regs))  		goto cleanup;  	has_dumped = 1; | 
