summaryrefslogtreecommitdiffstats
path: root/fix-abrtd.patch
diff options
context:
space:
mode:
authorJesse Keating <jkeating@redhat.com>2010-07-29 17:18:45 -0700
committerJesse Keating <jkeating@redhat.com>2010-07-29 17:18:45 -0700
commit2f82dda4a9bf41e64e864889bf06564bdf826e25 (patch)
tree118a7b483ae5de4dbf83d20001302f1404866ef0 /fix-abrtd.patch
parent64ba2e5ffde5f2418eb26c700cb0ab62b04e5013 (diff)
downloaddom0-kernel-2f82dda4a9bf41e64e864889bf06564bdf826e25.tar.gz
dom0-kernel-2f82dda4a9bf41e64e864889bf06564bdf826e25.tar.xz
dom0-kernel-2f82dda4a9bf41e64e864889bf06564bdf826e25.zip
initial srpm import
Diffstat (limited to 'fix-abrtd.patch')
-rw-r--r--fix-abrtd.patch774
1 files changed, 774 insertions, 0 deletions
diff --git a/fix-abrtd.patch b/fix-abrtd.patch
new file mode 100644
index 0000000..4a8db58
--- /dev/null
+++ b/fix-abrtd.patch
@@ -0,0 +1,774 @@
+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);
+