775 lines
25 KiB
Diff
775 lines
25 KiB
Diff
|
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
|
||
|
index b639dcf..346b694 100644
|
||
|
--- a/fs/binfmt_aout.c
|
||
|
+++ b/fs/binfmt_aout.c
|
||
|
@@ -32,7 +32,7 @@
|
||
|
|
||
|
static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
|
||
|
static int load_aout_library(struct file*);
|
||
|
-static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
|
||
|
+static int aout_core_dump(struct coredump_params *cprm);
|
||
|
|
||
|
static struct linux_binfmt aout_format = {
|
||
|
.module = THIS_MODULE,
|
||
|
@@ -89,8 +89,9 @@ if (file->f_op->llseek) { \
|
||
|
* dumping of the process results in another error..
|
||
|
*/
|
||
|
|
||
|
-static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
|
||
|
+static int aout_core_dump(struct coredump_params *cprm)
|
||
|
{
|
||
|
+ struct file *file = cprm->file;
|
||
|
mm_segment_t fs;
|
||
|
int has_dumped = 0;
|
||
|
unsigned long dump_start, dump_size;
|
||
|
@@ -108,16 +109,16 @@ static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, u
|
||
|
current->flags |= PF_DUMPCORE;
|
||
|
strncpy(dump.u_comm, current->comm, sizeof(dump.u_comm));
|
||
|
dump.u_ar0 = offsetof(struct user, regs);
|
||
|
- dump.signal = signr;
|
||
|
- aout_dump_thread(regs, &dump);
|
||
|
+ dump.signal = cprm->signr;
|
||
|
+ aout_dump_thread(cprm->regs, &dump);
|
||
|
|
||
|
/* If the size of the dump file exceeds the rlimit, then see what would happen
|
||
|
if we wrote the stack, but not the data area. */
|
||
|
- if ((dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE > limit)
|
||
|
+ if ((dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE > cprm->limit)
|
||
|
dump.u_dsize = 0;
|
||
|
|
||
|
/* Make sure we have enough room to write the stack and data areas. */
|
||
|
- if ((dump.u_ssize + 1) * PAGE_SIZE > limit)
|
||
|
+ if ((dump.u_ssize + 1) * PAGE_SIZE > cprm->limit)
|
||
|
dump.u_ssize = 0;
|
||
|
|
||
|
/* make sure we actually have a data and stack area to dump */
|
||
|
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
|
||
|
index b9b3bb5..4ee5bb2 100644
|
||
|
--- a/fs/binfmt_elf.c
|
||
|
+++ b/fs/binfmt_elf.c
|
||
|
@@ -45,7 +45,7 @@ static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *,
|
||
|
* 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, unsigned long limit);
|
||
|
+static int elf_core_dump(struct coredump_params *cprm);
|
||
|
#else
|
||
|
#define elf_core_dump NULL
|
||
|
#endif
|
||
|
@@ -1277,8 +1277,9 @@ static int writenote(struct memelfnote *men, struct file *file,
|
||
|
}
|
||
|
#undef DUMP_WRITE
|
||
|
|
||
|
-#define DUMP_WRITE(addr, nr) \
|
||
|
- if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
|
||
|
+#define DUMP_WRITE(addr, nr) \
|
||
|
+ if ((size += (nr)) > cprm->limit || \
|
||
|
+ !dump_write(cprm->file, (addr), (nr))) \
|
||
|
goto end_coredump;
|
||
|
|
||
|
static void fill_elf_header(struct elfhdr *elf, int segs,
|
||
|
@@ -1906,7 +1907,7 @@ 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, unsigned long limit)
|
||
|
+static int elf_core_dump(struct coredump_params *cprm)
|
||
|
{
|
||
|
int has_dumped = 0;
|
||
|
mm_segment_t fs;
|
||
|
@@ -1952,7 +1953,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
|
||
|
* notes. This also sets up the file header.
|
||
|
*/
|
||
|
if (!fill_note_info(elf, segs + 1, /* including notes section */
|
||
|
- &info, signr, regs))
|
||
|
+ &info, cprm->signr, cprm->regs))
|
||
|
goto cleanup;
|
||
|
|
||
|
has_dumped = 1;
|
||
|
@@ -2014,14 +2015,14 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
|
||
|
#endif
|
||
|
|
||
|
/* write out the notes section */
|
||
|
- if (!write_note_info(&info, file, &foffset))
|
||
|
+ if (!write_note_info(&info, cprm->file, &foffset))
|
||
|
goto end_coredump;
|
||
|
|
||
|
- if (elf_coredump_extra_notes_write(file, &foffset))
|
||
|
+ if (elf_coredump_extra_notes_write(cprm->file, &foffset))
|
||
|
goto end_coredump;
|
||
|
|
||
|
/* Align to page */
|
||
|
- if (!dump_seek(file, dataoff - foffset))
|
||
|
+ if (!dump_seek(cprm->file, dataoff - foffset))
|
||
|
goto end_coredump;
|
||
|
|
||
|
for (vma = first_vma(current, gate_vma); vma != NULL;
|
||
|
@@ -2038,12 +2039,13 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
|
||
|
page = get_dump_page(addr);
|
||
|
if (page) {
|
||
|
void *kaddr = kmap(page);
|
||
|
- stop = ((size += PAGE_SIZE) > limit) ||
|
||
|
- !dump_write(file, kaddr, PAGE_SIZE);
|
||
|
+ stop = ((size += PAGE_SIZE) > cprm->limit) ||
|
||
|
+ !dump_write(cprm->file, kaddr,
|
||
|
+ PAGE_SIZE);
|
||
|
kunmap(page);
|
||
|
page_cache_release(page);
|
||
|
} else
|
||
|
- stop = !dump_seek(file, PAGE_SIZE);
|
||
|
+ stop = !dump_seek(cprm->file, PAGE_SIZE);
|
||
|
if (stop)
|
||
|
goto end_coredump;
|
||
|
}
|
||
|
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
|
||
|
index 38502c6..917e1b4 100644
|
||
|
--- a/fs/binfmt_elf_fdpic.c
|
||
|
+++ b/fs/binfmt_elf_fdpic.c
|
||
|
@@ -76,7 +76,7 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *,
|
||
|
struct file *, struct mm_struct *);
|
||
|
|
||
|
#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
|
||
|
-static int elf_fdpic_core_dump(long, struct pt_regs *, struct file *, unsigned long limit);
|
||
|
+static int elf_fdpic_core_dump(struct coredump_params *cprm);
|
||
|
#endif
|
||
|
|
||
|
static struct linux_binfmt elf_fdpic_format = {
|
||
|
@@ -1325,8 +1325,9 @@ static int writenote(struct memelfnote *men, struct file *file)
|
||
|
#undef DUMP_WRITE
|
||
|
#undef DUMP_SEEK
|
||
|
|
||
|
-#define DUMP_WRITE(addr, nr) \
|
||
|
- if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
|
||
|
+#define DUMP_WRITE(addr, nr) \
|
||
|
+ if ((size += (nr)) > cprm->limit || \
|
||
|
+ !dump_write(cprm->file, (addr), (nr))) \
|
||
|
goto end_coredump;
|
||
|
|
||
|
static inline void fill_elf_fdpic_header(struct elfhdr *elf, int segs)
|
||
|
@@ -1581,8 +1582,7 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size,
|
||
|
* and then they are actually written out. If we run out of core limit
|
||
|
* we just truncate.
|
||
|
*/
|
||
|
-static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
|
||
|
- struct file *file, unsigned long limit)
|
||
|
+static int elf_fdpic_core_dump(struct coredump_params *cprm)
|
||
|
{
|
||
|
#define NUM_NOTES 6
|
||
|
int has_dumped = 0;
|
||
|
@@ -1641,7 +1641,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
|
||
|
goto cleanup;
|
||
|
#endif
|
||
|
|
||
|
- if (signr) {
|
||
|
+ if (cprm->signr) {
|
||
|
struct core_thread *ct;
|
||
|
struct elf_thread_status *tmp;
|
||
|
|
||
|
@@ -1660,14 +1660,14 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
|
||
|
int sz;
|
||
|
|
||
|
tmp = list_entry(t, struct elf_thread_status, list);
|
||
|
- sz = elf_dump_thread_status(signr, tmp);
|
||
|
+ sz = elf_dump_thread_status(cprm->signr, tmp);
|
||
|
thread_status_size += sz;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* now collect the dump for the current */
|
||
|
- fill_prstatus(prstatus, current, signr);
|
||
|
- elf_core_copy_regs(&prstatus->pr_reg, regs);
|
||
|
+ fill_prstatus(prstatus, current, cprm->signr);
|
||
|
+ elf_core_copy_regs(&prstatus->pr_reg, cprm->regs);
|
||
|
|
||
|
segs = current->mm->map_count;
|
||
|
#ifdef ELF_CORE_EXTRA_PHDRS
|
||
|
@@ -1702,7 +1702,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
|
||
|
|
||
|
/* Try to dump the FPU. */
|
||
|
if ((prstatus->pr_fpvalid =
|
||
|
- elf_core_copy_task_fpregs(current, regs, fpu)))
|
||
|
+ elf_core_copy_task_fpregs(current, cprm->regs, fpu)))
|
||
|
fill_note(notes + numnote++,
|
||
|
"CORE", NT_PRFPREG, sizeof(*fpu), fpu);
|
||
|
#ifdef ELF_CORE_COPY_XFPREGS
|
||
|
@@ -1773,7 +1773,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
|
||
|
|
||
|
/* write out the notes section */
|
||
|
for (i = 0; i < numnote; i++)
|
||
|
- if (!writenote(notes + i, file))
|
||
|
+ if (!writenote(notes + i, cprm->file))
|
||
|
goto end_coredump;
|
||
|
|
||
|
/* write out the thread status notes section */
|
||
|
@@ -1782,14 +1782,15 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
|
||
|
list_entry(t, struct elf_thread_status, list);
|
||
|
|
||
|
for (i = 0; i < tmp->num_notes; i++)
|
||
|
- if (!writenote(&tmp->notes[i], file))
|
||
|
+ if (!writenote(&tmp->notes[i], cprm->file))
|
||
|
goto end_coredump;
|
||
|
}
|
||
|
|
||
|
- if (!dump_seek(file, dataoff))
|
||
|
+ if (!dump_seek(cprm->file, dataoff))
|
||
|
goto end_coredump;
|
||
|
|
||
|
- if (elf_fdpic_dump_segments(file, &size, &limit, mm_flags) < 0)
|
||
|
+ if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit,
|
||
|
+ mm_flags) < 0)
|
||
|
goto end_coredump;
|
||
|
|
||
|
#ifdef ELF_CORE_WRITE_EXTRA_DATA
|
||
|
diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c
|
||
|
index a279665..d4a00ea 100644
|
||
|
--- a/fs/binfmt_flat.c
|
||
|
+++ b/fs/binfmt_flat.c
|
||
|
@@ -87,7 +87,7 @@ static int load_flat_shared_library(int id, struct lib_info *p);
|
||
|
#endif
|
||
|
|
||
|
static int load_flat_binary(struct linux_binprm *, struct pt_regs * regs);
|
||
|
-static int flat_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
|
||
|
+static int flat_core_dump(struct coredump_params *cprm);
|
||
|
|
||
|
static struct linux_binfmt flat_format = {
|
||
|
.module = THIS_MODULE,
|
||
|
@@ -102,10 +102,10 @@ static struct linux_binfmt flat_format = {
|
||
|
* Currently only a stub-function.
|
||
|
*/
|
||
|
|
||
|
-static int flat_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
|
||
|
+static int flat_core_dump(struct coredump_params *cprm)
|
||
|
{
|
||
|
printk("Process %s:%d received signr %d and should have core dumped\n",
|
||
|
- current->comm, current->pid, (int) signr);
|
||
|
+ current->comm, current->pid, (int) cprm->signr);
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c
|
||
|
index eff74b9..2a9b533 100644
|
||
|
--- a/fs/binfmt_som.c
|
||
|
+++ b/fs/binfmt_som.c
|
||
|
@@ -43,7 +43,7 @@ static int load_som_library(struct file *);
|
||
|
* don't even try.
|
||
|
*/
|
||
|
#if 0
|
||
|
-static int som_core_dump(long signr, struct pt_regs *regs, unsigned long limit);
|
||
|
+static int som_core_dump(struct coredump_params *cprm);
|
||
|
#else
|
||
|
#define som_core_dump NULL
|
||
|
#endif
|
||
|
diff --git a/fs/exec.c b/fs/exec.c
|
||
|
index ba112bd..08ec506 100644
|
||
|
--- a/fs/exec.c
|
||
|
+++ b/fs/exec.c
|
||
|
@@ -1749,6 +1749,50 @@ static void wait_for_dump_helpers(struct file *file)
|
||
|
}
|
||
|
|
||
|
|
||
|
+/*
|
||
|
+ * uhm_pipe_setup
|
||
|
+ * helper function to customize the process used
|
||
|
+ * to collect the core in userspace. Specifically
|
||
|
+ * it sets up a pipe and installs it as fd 0 (stdin)
|
||
|
+ * for the process. Returns 0 on success, or
|
||
|
+ * PTR_ERR on failure.
|
||
|
+ * Note that it also sets the core limit to 1. This
|
||
|
+ * is a special value that we use to trap recursive
|
||
|
+ * core dumps
|
||
|
+ */
|
||
|
+static int umh_pipe_setup(struct subprocess_info *info)
|
||
|
+{
|
||
|
+ struct file *rp, *wp;
|
||
|
+ struct fdtable *fdt;
|
||
|
+ struct coredump_params *cp = (struct coredump_params *)info->data;
|
||
|
+ struct files_struct *cf = current->files;
|
||
|
+
|
||
|
+ wp = create_write_pipe(0);
|
||
|
+ if (IS_ERR(wp))
|
||
|
+ return PTR_ERR(wp);
|
||
|
+
|
||
|
+ rp = create_read_pipe(wp, 0);
|
||
|
+ if (IS_ERR(rp)) {
|
||
|
+ free_write_pipe(wp);
|
||
|
+ return PTR_ERR(rp);
|
||
|
+ }
|
||
|
+
|
||
|
+ cp->file = wp;
|
||
|
+
|
||
|
+ sys_close(0);
|
||
|
+ fd_install(0, rp);
|
||
|
+ spin_lock(&cf->file_lock);
|
||
|
+ fdt = files_fdtable(cf);
|
||
|
+ FD_SET(0, fdt->open_fds);
|
||
|
+ FD_CLR(0, fdt->close_on_exec);
|
||
|
+ spin_unlock(&cf->file_lock);
|
||
|
+
|
||
|
+ /* and disallow core files too */
|
||
|
+ current->signal->rlim[RLIMIT_CORE] = (struct rlimit){1, 1};
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
||
|
{
|
||
|
struct core_state core_state;
|
||
|
@@ -1756,17 +1800,20 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
||
|
struct mm_struct *mm = current->mm;
|
||
|
struct linux_binfmt * binfmt;
|
||
|
struct inode * inode;
|
||
|
- struct file * file;
|
||
|
const struct cred *old_cred;
|
||
|
struct cred *cred;
|
||
|
int retval = 0;
|
||
|
int flag = 0;
|
||
|
int ispipe = 0;
|
||
|
- unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
|
||
|
char **helper_argv = NULL;
|
||
|
int helper_argc = 0;
|
||
|
int dump_count = 0;
|
||
|
static atomic_t core_dump_count = ATOMIC_INIT(0);
|
||
|
+ struct coredump_params cprm = {
|
||
|
+ .signr = signr,
|
||
|
+ .regs = regs,
|
||
|
+ .limit = current->signal->rlim[RLIMIT_CORE].rlim_cur,
|
||
|
+ };
|
||
|
|
||
|
audit_core_dumps(signr);
|
||
|
|
||
|
@@ -1822,19 +1869,19 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
||
|
ispipe = format_corename(corename, signr);
|
||
|
unlock_kernel();
|
||
|
|
||
|
- if ((!ispipe) && (core_limit < binfmt->min_coredump))
|
||
|
+ if ((!ispipe) && (cprm.limit < binfmt->min_coredump))
|
||
|
goto fail_unlock;
|
||
|
|
||
|
if (ispipe) {
|
||
|
- if (core_limit == 0) {
|
||
|
+ if (cprm.limit == 1) {
|
||
|
/*
|
||
|
* Normally core limits are irrelevant to pipes, since
|
||
|
* we're not writing to the file system, but we use
|
||
|
- * core_limit of 0 here as a speacial value. Any
|
||
|
- * non-zero limit gets set to RLIM_INFINITY below, but
|
||
|
+ * cprm.limit of 1 here as a speacial value. Any
|
||
|
+ * non-1 limit gets set to RLIM_INFINITY below, but
|
||
|
* a limit of 0 skips the dump. This is a consistent
|
||
|
* way to catch recursive crashes. We can still crash
|
||
|
- * if the core_pattern binary sets RLIM_CORE = !0
|
||
|
+ * if the core_pattern binary sets RLIM_CORE = !1
|
||
|
* but it runs as root, and can do lots of stupid things
|
||
|
* Note that we use task_tgid_vnr here to grab the pid
|
||
|
* of the process group leader. That way we get the
|
||
|
@@ -1842,7 +1889,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
||
|
* core_pattern process dies.
|
||
|
*/
|
||
|
printk(KERN_WARNING
|
||
|
- "Process %d(%s) has RLIMIT_CORE set to 0\n",
|
||
|
+ "Process %d(%s) has RLIMIT_CORE set to 1\n",
|
||
|
task_tgid_vnr(current), current->comm);
|
||
|
printk(KERN_WARNING "Aborting core\n");
|
||
|
goto fail_unlock;
|
||
|
@@ -1863,25 +1910,30 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
||
|
goto fail_dropcount;
|
||
|
}
|
||
|
|
||
|
- core_limit = RLIM_INFINITY;
|
||
|
+ cprm.limit = RLIM_INFINITY;
|
||
|
|
||
|
/* SIGPIPE can happen, but it's just never processed */
|
||
|
- if (call_usermodehelper_pipe(helper_argv[0], helper_argv, NULL,
|
||
|
- &file)) {
|
||
|
+ cprm.file = NULL;
|
||
|
+ if (call_usermodehelper_fns(helper_argv[0], helper_argv, NULL,
|
||
|
+ UMH_WAIT_EXEC, umh_pipe_setup,
|
||
|
+ NULL, &cprm)) {
|
||
|
+ if (cprm.file)
|
||
|
+ filp_close(cprm.file, NULL);
|
||
|
+
|
||
|
printk(KERN_INFO "Core dump to %s pipe failed\n",
|
||
|
corename);
|
||
|
goto fail_dropcount;
|
||
|
}
|
||
|
} else
|
||
|
- file = filp_open(corename,
|
||
|
+ cprm.file = filp_open(corename,
|
||
|
O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
|
||
|
0600);
|
||
|
- if (IS_ERR(file))
|
||
|
+ if (IS_ERR(cprm.file))
|
||
|
goto fail_dropcount;
|
||
|
- inode = file->f_path.dentry->d_inode;
|
||
|
+ inode = cprm.file->f_path.dentry->d_inode;
|
||
|
if (inode->i_nlink > 1)
|
||
|
goto close_fail; /* multiple links - don't dump */
|
||
|
- if (!ispipe && d_unhashed(file->f_path.dentry))
|
||
|
+ if (!ispipe && d_unhashed(cprm.file->f_path.dentry))
|
||
|
goto close_fail;
|
||
|
|
||
|
/* AK: actually i see no reason to not allow this for named pipes etc.,
|
||
|
@@ -1894,21 +1946,22 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
||
|
*/
|
||
|
if (!ispipe && (inode->i_uid != current_fsuid()))
|
||
|
goto close_fail;
|
||
|
- if (!file->f_op)
|
||
|
+ if (!cprm.file->f_op)
|
||
|
goto close_fail;
|
||
|
- if (!file->f_op->write)
|
||
|
+ if (!cprm.file->f_op->write)
|
||
|
goto close_fail;
|
||
|
- if (!ispipe && do_truncate(file->f_path.dentry, 0, 0, file) != 0)
|
||
|
+ if (!ispipe &&
|
||
|
+ do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file) != 0)
|
||
|
goto close_fail;
|
||
|
|
||
|
- retval = binfmt->core_dump(signr, regs, file, core_limit);
|
||
|
+ retval = binfmt->core_dump(&cprm);
|
||
|
|
||
|
if (retval)
|
||
|
current->signal->group_exit_code |= 0x80;
|
||
|
close_fail:
|
||
|
if (ispipe && core_pipe_limit)
|
||
|
- wait_for_dump_helpers(file);
|
||
|
- filp_close(file, NULL);
|
||
|
+ wait_for_dump_helpers(cprm.file);
|
||
|
+ filp_close(cprm.file, NULL);
|
||
|
fail_dropcount:
|
||
|
if (dump_count)
|
||
|
atomic_dec(&core_dump_count);
|
||
|
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
|
||
|
index aece486..cd4349b 100644
|
||
|
--- a/include/linux/binfmts.h
|
||
|
+++ b/include/linux/binfmts.h
|
||
|
@@ -68,6 +68,14 @@ struct linux_binprm{
|
||
|
|
||
|
#define BINPRM_MAX_RECURSION 4
|
||
|
|
||
|
+/* Function parameter for binfmt->coredump */
|
||
|
+struct coredump_params {
|
||
|
+ long signr;
|
||
|
+ struct pt_regs *regs;
|
||
|
+ struct file *file;
|
||
|
+ unsigned long limit;
|
||
|
+};
|
||
|
+
|
||
|
/*
|
||
|
* This structure defines the functions that are used to load the binary formats that
|
||
|
* linux accepts.
|
||
|
@@ -77,7 +85,7 @@ struct linux_binfmt {
|
||
|
struct module *module;
|
||
|
int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);
|
||
|
int (*load_shlib)(struct file *);
|
||
|
- int (*core_dump)(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
|
||
|
+ int (*core_dump)(struct coredump_params *cprm);
|
||
|
unsigned long min_coredump; /* minimal dump size */
|
||
|
int hasvdso;
|
||
|
};
|
||
|
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
|
||
|
index 384ca8b..ec69956 100644
|
||
|
--- a/include/linux/kmod.h
|
||
|
+++ b/include/linux/kmod.h
|
||
|
@@ -23,6 +23,7 @@
|
||
|
#include <linux/stddef.h>
|
||
|
#include <linux/errno.h>
|
||
|
#include <linux/compiler.h>
|
||
|
+#include <linux/workqueue.h>
|
||
|
|
||
|
#define KMOD_PATH_LEN 256
|
||
|
|
||
|
@@ -44,7 +45,26 @@ static inline int request_module_nowait(const char *name, ...) { return -ENOSYS;
|
||
|
|
||
|
struct key;
|
||
|
struct file;
|
||
|
-struct subprocess_info;
|
||
|
+
|
||
|
+enum umh_wait {
|
||
|
+ UMH_NO_WAIT = -1, /* don't wait at all */
|
||
|
+ UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
|
||
|
+ UMH_WAIT_PROC = 1, /* wait for the process to complete */
|
||
|
+};
|
||
|
+
|
||
|
+struct subprocess_info {
|
||
|
+ struct work_struct work;
|
||
|
+ struct completion *complete;
|
||
|
+ struct cred *cred;
|
||
|
+ char *path;
|
||
|
+ char **argv;
|
||
|
+ char **envp;
|
||
|
+ enum umh_wait wait;
|
||
|
+ int retval;
|
||
|
+ int (*init)(struct subprocess_info *info);
|
||
|
+ void (*cleanup)(struct subprocess_info *info);
|
||
|
+ void *data;
|
||
|
+};
|
||
|
|
||
|
/* Allocate a subprocess_info structure */
|
||
|
struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
|
||
|
@@ -55,14 +75,10 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
|
||
|
struct key *session_keyring);
|
||
|
int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
|
||
|
struct file **filp);
|
||
|
-void call_usermodehelper_setcleanup(struct subprocess_info *info,
|
||
|
- void (*cleanup)(char **argv, char **envp));
|
||
|
-
|
||
|
-enum umh_wait {
|
||
|
- UMH_NO_WAIT = -1, /* don't wait at all */
|
||
|
- UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
|
||
|
- UMH_WAIT_PROC = 1, /* wait for the process to complete */
|
||
|
-};
|
||
|
+void call_usermodehelper_setfns(struct subprocess_info *info,
|
||
|
+ int (*init)(struct subprocess_info *info),
|
||
|
+ void (*cleanup)(struct subprocess_info *info),
|
||
|
+ void *data);
|
||
|
|
||
|
/* Actually execute the sub-process */
|
||
|
int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait);
|
||
|
@@ -72,7 +88,10 @@ int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait);
|
||
|
void call_usermodehelper_freeinfo(struct subprocess_info *info);
|
||
|
|
||
|
static inline int
|
||
|
-call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
|
||
|
+call_usermodehelper_fns(char *path, char **argv, char **envp,
|
||
|
+ enum umh_wait wait,
|
||
|
+ int (*init)(struct subprocess_info *info),
|
||
|
+ void (*cleanup)(struct subprocess_info *), void *data)
|
||
|
{
|
||
|
struct subprocess_info *info;
|
||
|
gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
|
||
|
@@ -80,10 +99,18 @@ call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
|
||
|
info = call_usermodehelper_setup(path, argv, envp, gfp_mask);
|
||
|
if (info == NULL)
|
||
|
return -ENOMEM;
|
||
|
+ call_usermodehelper_setfns(info, init, cleanup, data);
|
||
|
return call_usermodehelper_exec(info, wait);
|
||
|
}
|
||
|
|
||
|
static inline int
|
||
|
+call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
|
||
|
+{
|
||
|
+ return call_usermodehelper_fns(path, argv, envp,
|
||
|
+ wait, NULL, NULL, NULL);
|
||
|
+}
|
||
|
+
|
||
|
+static inline int
|
||
|
call_usermodehelper_keys(char *path, char **argv, char **envp,
|
||
|
struct key *session_keyring, enum umh_wait wait)
|
||
|
{
|
||
|
@@ -100,10 +127,6 @@ call_usermodehelper_keys(char *path, char **argv, char **envp,
|
||
|
|
||
|
extern void usermodehelper_init(void);
|
||
|
|
||
|
-struct file;
|
||
|
-extern int call_usermodehelper_pipe(char *path, char *argv[], char *envp[],
|
||
|
- struct file **filp);
|
||
|
-
|
||
|
extern int usermodehelper_disable(void);
|
||
|
extern void usermodehelper_enable(void);
|
||
|
|
||
|
diff --git a/kernel/kmod.c b/kernel/kmod.c
|
||
|
index 9fcb53a..7281229 100644
|
||
|
--- a/kernel/kmod.c
|
||
|
+++ b/kernel/kmod.c
|
||
|
@@ -124,19 +124,6 @@ int __request_module(bool wait, const char *fmt, ...)
|
||
|
EXPORT_SYMBOL(__request_module);
|
||
|
#endif /* CONFIG_MODULES */
|
||
|
|
||
|
-struct subprocess_info {
|
||
|
- struct work_struct work;
|
||
|
- struct completion *complete;
|
||
|
- struct cred *cred;
|
||
|
- char *path;
|
||
|
- char **argv;
|
||
|
- char **envp;
|
||
|
- enum umh_wait wait;
|
||
|
- int retval;
|
||
|
- struct file *stdin;
|
||
|
- void (*cleanup)(char **argv, char **envp);
|
||
|
-};
|
||
|
-
|
||
|
/*
|
||
|
* This is the task which runs the usermode application
|
||
|
*/
|
||
|
@@ -158,26 +145,15 @@ static int ____call_usermodehelper(void *data)
|
||
|
commit_creds(sub_info->cred);
|
||
|
sub_info->cred = NULL;
|
||
|
|
||
|
- /* Install input pipe when needed */
|
||
|
- if (sub_info->stdin) {
|
||
|
- struct files_struct *f = current->files;
|
||
|
- struct fdtable *fdt;
|
||
|
- /* no races because files should be private here */
|
||
|
- sys_close(0);
|
||
|
- fd_install(0, sub_info->stdin);
|
||
|
- spin_lock(&f->file_lock);
|
||
|
- fdt = files_fdtable(f);
|
||
|
- FD_SET(0, fdt->open_fds);
|
||
|
- FD_CLR(0, fdt->close_on_exec);
|
||
|
- spin_unlock(&f->file_lock);
|
||
|
-
|
||
|
- /* and disallow core files too */
|
||
|
- current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0};
|
||
|
- }
|
||
|
-
|
||
|
/* We can run anywhere, unlike our parent keventd(). */
|
||
|
set_cpus_allowed_ptr(current, cpu_all_mask);
|
||
|
|
||
|
+ if (sub_info->init) {
|
||
|
+ retval = sub_info->init(sub_info);
|
||
|
+ if (retval)
|
||
|
+ goto fail;
|
||
|
+ }
|
||
|
+
|
||
|
/*
|
||
|
* Our parent is keventd, which runs with elevated scheduling priority.
|
||
|
* Avoid propagating that into the userspace child.
|
||
|
@@ -187,6 +163,7 @@ static int ____call_usermodehelper(void *data)
|
||
|
retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp);
|
||
|
|
||
|
/* Exec failed? */
|
||
|
+fail:
|
||
|
sub_info->retval = retval;
|
||
|
do_exit(0);
|
||
|
}
|
||
|
@@ -194,7 +171,7 @@ static int ____call_usermodehelper(void *data)
|
||
|
void call_usermodehelper_freeinfo(struct subprocess_info *info)
|
||
|
{
|
||
|
if (info->cleanup)
|
||
|
- (*info->cleanup)(info->argv, info->envp);
|
||
|
+ (*info->cleanup)(info);
|
||
|
if (info->cred)
|
||
|
put_cred(info->cred);
|
||
|
kfree(info);
|
||
|
@@ -406,50 +383,31 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
|
||
|
EXPORT_SYMBOL(call_usermodehelper_setkeys);
|
||
|
|
||
|
/**
|
||
|
- * call_usermodehelper_setcleanup - set a cleanup function
|
||
|
+ * call_usermodehelper_setfns - set a cleanup/init function
|
||
|
* @info: a subprocess_info returned by call_usermodehelper_setup
|
||
|
* @cleanup: a cleanup function
|
||
|
+ * @init: an init function
|
||
|
+ * @data: arbitrary context sensitive data
|
||
|
+ *
|
||
|
+ * The init function is used to customize the helper process prior to
|
||
|
+ * exec. A non-zero return code causes the process to error out, exit,
|
||
|
+ * and return the failure to the calling process
|
||
|
*
|
||
|
- * The cleanup function is just befor ethe subprocess_info is about to
|
||
|
+ * The cleanup function is just before ethe subprocess_info is about to
|
||
|
* be freed. This can be used for freeing the argv and envp. The
|
||
|
* Function must be runnable in either a process context or the
|
||
|
* context in which call_usermodehelper_exec is called.
|
||
|
*/
|
||
|
-void call_usermodehelper_setcleanup(struct subprocess_info *info,
|
||
|
- void (*cleanup)(char **argv, char **envp))
|
||
|
+void call_usermodehelper_setfns(struct subprocess_info *info,
|
||
|
+ int (*init)(struct subprocess_info *info),
|
||
|
+ void (*cleanup)(struct subprocess_info *info),
|
||
|
+ void *data)
|
||
|
{
|
||
|
info->cleanup = cleanup;
|
||
|
+ info->init = init;
|
||
|
+ info->data = data;
|
||
|
}
|
||
|
-EXPORT_SYMBOL(call_usermodehelper_setcleanup);
|
||
|
-
|
||
|
-/**
|
||
|
- * call_usermodehelper_stdinpipe - set up a pipe to be used for stdin
|
||
|
- * @sub_info: a subprocess_info returned by call_usermodehelper_setup
|
||
|
- * @filp: set to the write-end of a pipe
|
||
|
- *
|
||
|
- * This constructs a pipe, and sets the read end to be the stdin of the
|
||
|
- * subprocess, and returns the write-end in *@filp.
|
||
|
- */
|
||
|
-int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
|
||
|
- struct file **filp)
|
||
|
-{
|
||
|
- struct file *f;
|
||
|
-
|
||
|
- f = create_write_pipe(0);
|
||
|
- if (IS_ERR(f))
|
||
|
- return PTR_ERR(f);
|
||
|
- *filp = f;
|
||
|
-
|
||
|
- f = create_read_pipe(f, 0);
|
||
|
- if (IS_ERR(f)) {
|
||
|
- free_write_pipe(*filp);
|
||
|
- return PTR_ERR(f);
|
||
|
- }
|
||
|
- sub_info->stdin = f;
|
||
|
-
|
||
|
- return 0;
|
||
|
-}
|
||
|
-EXPORT_SYMBOL(call_usermodehelper_stdinpipe);
|
||
|
+EXPORT_SYMBOL(call_usermodehelper_setfns);
|
||
|
|
||
|
/**
|
||
|
* call_usermodehelper_exec - start a usermode application
|
||
|
@@ -498,39 +456,6 @@ unlock:
|
||
|
}
|
||
|
EXPORT_SYMBOL(call_usermodehelper_exec);
|
||
|
|
||
|
-/**
|
||
|
- * call_usermodehelper_pipe - call a usermode helper process with a pipe stdin
|
||
|
- * @path: path to usermode executable
|
||
|
- * @argv: arg vector for process
|
||
|
- * @envp: environment for process
|
||
|
- * @filp: set to the write-end of a pipe
|
||
|
- *
|
||
|
- * This is a simple wrapper which executes a usermode-helper function
|
||
|
- * with a pipe as stdin. It is implemented entirely in terms of
|
||
|
- * lower-level call_usermodehelper_* functions.
|
||
|
- */
|
||
|
-int call_usermodehelper_pipe(char *path, char **argv, char **envp,
|
||
|
- struct file **filp)
|
||
|
-{
|
||
|
- struct subprocess_info *sub_info;
|
||
|
- int ret;
|
||
|
-
|
||
|
- sub_info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL);
|
||
|
- if (sub_info == NULL)
|
||
|
- return -ENOMEM;
|
||
|
-
|
||
|
- ret = call_usermodehelper_stdinpipe(sub_info, filp);
|
||
|
- if (ret < 0)
|
||
|
- goto out;
|
||
|
-
|
||
|
- return call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
|
||
|
-
|
||
|
- out:
|
||
|
- call_usermodehelper_freeinfo(sub_info);
|
||
|
- return ret;
|
||
|
-}
|
||
|
-EXPORT_SYMBOL(call_usermodehelper_pipe);
|
||
|
-
|
||
|
void __init usermodehelper_init(void)
|
||
|
{
|
||
|
khelper_wq = create_singlethread_workqueue("khelper");
|
||
|
diff --git a/kernel/sys.c b/kernel/sys.c
|
||
|
index ce17760..0b8a55e 100644
|
||
|
--- a/kernel/sys.c
|
||
|
+++ b/kernel/sys.c
|
||
|
@@ -1600,9 +1600,9 @@ SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
|
||
|
|
||
|
char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
|
||
|
|
||
|
-static void argv_cleanup(char **argv, char **envp)
|
||
|
+static void argv_cleanup(struct subprocess_info *info)
|
||
|
{
|
||
|
- argv_free(argv);
|
||
|
+ argv_free(info->argv);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
@@ -1636,7 +1636,7 @@ int orderly_poweroff(bool force)
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
- call_usermodehelper_setcleanup(info, argv_cleanup);
|
||
|
+ call_usermodehelper_setfns(info, NULL, argv_cleanup, NULL);
|
||
|
|
||
|
ret = call_usermodehelper_exec(info, UMH_NO_WAIT);
|
||
|
|