diff --git a/debian/changelog b/debian/changelog index bae8c0359..8a4718a12 100644 --- a/debian/changelog +++ b/debian/changelog @@ -95,6 +95,7 @@ linux (3.2.35-1) UNRELEASED; urgency=low HP Folio 13-2000 (Closes: #692361) * [x86] KVM: x86: invalid opcode oops on SET_SREGS with OSXSAVE bit set (CVE-2012-4461) + * kmod: make __request_module() killable (CVE-2012-4398) [ Ian Campbell ] * [xen] add support for microcode updating. (Closes: #693053) diff --git a/debian/patches/bugfix/all/kmod-introduce-call_modprobe-helper.patch b/debian/patches/bugfix/all/kmod-introduce-call_modprobe-helper.patch new file mode 100644 index 000000000..5d16d8755 --- /dev/null +++ b/debian/patches/bugfix/all/kmod-introduce-call_modprobe-helper.patch @@ -0,0 +1,69 @@ +From: Oleg Nesterov +Date: Fri, 23 Mar 2012 15:02:49 -0700 +Subject: [4/5] kmod: introduce call_modprobe() helper + +commit 3e63a93b987685f02421e18b2aa452d20553a88b upstream. + +No functional changes. Move the call_usermodehelper code from +__request_module() into the new simple helper, call_modprobe(). + +Signed-off-by: Oleg Nesterov +Cc: Tetsuo Handa +Cc: Rusty Russell +Cc: Tejun Heo +Cc: David Rientjes +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + kernel/kmod.c | 24 ++++++++++++++++-------- + 1 file changed, 16 insertions(+), 8 deletions(-) + +diff --git a/kernel/kmod.c b/kernel/kmod.c +index 685b246..56a29e8 100644 +--- a/kernel/kmod.c ++++ b/kernel/kmod.c +@@ -60,6 +60,21 @@ static DECLARE_RWSEM(umhelper_sem); + */ + char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe"; + ++static int call_modprobe(char *module_name, int wait) ++{ ++ static char *envp[] = { ++ "HOME=/", ++ "TERM=linux", ++ "PATH=/sbin:/usr/sbin:/bin:/usr/bin", ++ NULL ++ }; ++ ++ char *argv[] = { modprobe_path, "-q", "--", module_name, NULL }; ++ ++ return call_usermodehelper_fns(modprobe_path, argv, envp, ++ wait, NULL, NULL, NULL); ++} ++ + /** + * __request_module - try to load a kernel module + * @wait: wait (or not) for the operation to complete +@@ -81,11 +96,6 @@ int __request_module(bool wait, const char *fmt, ...) + char module_name[MODULE_NAME_LEN]; + unsigned int max_modprobes; + int ret; +- char *argv[] = { modprobe_path, "-q", "--", module_name, NULL }; +- static char *envp[] = { "HOME=/", +- "TERM=linux", +- "PATH=/sbin:/usr/sbin:/bin:/usr/bin", +- NULL }; + static atomic_t kmod_concurrent = ATOMIC_INIT(0); + #define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */ + static int kmod_loop_msg; +@@ -128,9 +138,7 @@ int __request_module(bool wait, const char *fmt, ...) + + trace_module_request(module_name, wait, _RET_IP_); + +- ret = call_usermodehelper_fns(modprobe_path, argv, envp, +- wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC, +- NULL, NULL, NULL); ++ ret = call_modprobe(module_name, wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC); + + atomic_dec(&kmod_concurrent); + return ret; diff --git a/debian/patches/bugfix/all/kmod-make-__request_module-killable.patch b/debian/patches/bugfix/all/kmod-make-__request_module-killable.patch new file mode 100644 index 000000000..dc87e7979 --- /dev/null +++ b/debian/patches/bugfix/all/kmod-make-__request_module-killable.patch @@ -0,0 +1,78 @@ +From: Oleg Nesterov +Date: Fri, 23 Mar 2012 15:02:50 -0700 +Subject: [5/5] kmod: make __request_module() killable + +commit 1cc684ab75123efe7ff446eb821d44375ba8fa30 upstream. + +As Tetsuo Handa pointed out, request_module() can stress the system +while the oom-killed caller sleeps in TASK_UNINTERRUPTIBLE. + +The task T uses "almost all" memory, then it does something which +triggers request_module(). Say, it can simply call sys_socket(). This +in turn needs more memory and leads to OOM. oom-killer correctly +chooses T and kills it, but this can't help because it sleeps in +TASK_UNINTERRUPTIBLE and after that oom-killer becomes "disabled" by the +TIF_MEMDIE task T. + +Make __request_module() killable. The only necessary change is that +call_modprobe() should kmalloc argv and module_name, they can't live in +the stack if we use UMH_KILLABLE. This memory is freed via +call_usermodehelper_freeinfo()->cleanup. + +Reported-by: Tetsuo Handa +Signed-off-by: Oleg Nesterov +Cc: Rusty Russell +Cc: Tejun Heo +Cc: David Rientjes +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + kernel/kmod.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/kernel/kmod.c b/kernel/kmod.c +index 56a29e8..957a7aa 100644 +--- a/kernel/kmod.c ++++ b/kernel/kmod.c +@@ -60,6 +60,12 @@ static DECLARE_RWSEM(umhelper_sem); + */ + char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe"; + ++static void free_modprobe_argv(struct subprocess_info *info) ++{ ++ kfree(info->argv[3]); /* check call_modprobe() */ ++ kfree(info->argv); ++} ++ + static int call_modprobe(char *module_name, int wait) + { + static char *envp[] = { +@@ -69,10 +75,26 @@ static int call_modprobe(char *module_name, int wait) + NULL + }; + +- char *argv[] = { modprobe_path, "-q", "--", module_name, NULL }; ++ char **argv = kmalloc(sizeof(char *[5]), GFP_KERNEL); ++ if (!argv) ++ goto out; ++ ++ module_name = kstrdup(module_name, GFP_KERNEL); ++ if (!module_name) ++ goto free_argv; ++ ++ argv[0] = modprobe_path; ++ argv[1] = "-q"; ++ argv[2] = "--"; ++ argv[3] = module_name; /* check free_modprobe_argv() */ ++ argv[4] = NULL; + + return call_usermodehelper_fns(modprobe_path, argv, envp, +- wait, NULL, NULL, NULL); ++ wait | UMH_KILLABLE, NULL, free_modprobe_argv, NULL); ++free_argv: ++ kfree(argv); ++out: ++ return -ENOMEM; + } + + /** diff --git a/debian/patches/bugfix/all/usermodehelper-____call_usermodehelper-doesnt-need-do_exit.patch b/debian/patches/bugfix/all/usermodehelper-____call_usermodehelper-doesnt-need-do_exit.patch new file mode 100644 index 000000000..80eaa712f --- /dev/null +++ b/debian/patches/bugfix/all/usermodehelper-____call_usermodehelper-doesnt-need-do_exit.patch @@ -0,0 +1,34 @@ +From: Oleg Nesterov +Date: Fri, 23 Mar 2012 15:02:49 -0700 +Subject: [3/5] usermodehelper: ____call_usermodehelper() doesn't need + do_exit() + +commit 5b9bd473e3b8a8c6c4ae99be475e6e9b27568555 upstream. + +Minor cleanup. ____call_usermodehelper() can simply return, no need to +call do_exit() explicitely. + +Signed-off-by: Oleg Nesterov +Cc: Tetsuo Handa +Cc: Rusty Russell +Cc: Tejun Heo +Cc: David Rientjes +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + kernel/kmod.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/kernel/kmod.c b/kernel/kmod.c +index 8341de9..685b246 100644 +--- a/kernel/kmod.c ++++ b/kernel/kmod.c +@@ -188,7 +188,7 @@ static int ____call_usermodehelper(void *data) + /* Exec failed? */ + fail: + sub_info->retval = retval; +- do_exit(0); ++ return 0; + } + + void call_usermodehelper_freeinfo(struct subprocess_info *info) diff --git a/debian/patches/bugfix/all/usermodehelper-implement-UMH_KILLABLE.patch b/debian/patches/bugfix/all/usermodehelper-implement-UMH_KILLABLE.patch new file mode 100644 index 000000000..90592c514 --- /dev/null +++ b/debian/patches/bugfix/all/usermodehelper-implement-UMH_KILLABLE.patch @@ -0,0 +1,105 @@ +From: Oleg Nesterov +Date: Fri, 23 Mar 2012 15:02:47 -0700 +Subject: [2/5] usermodehelper: implement UMH_KILLABLE + +commit d0bd587a80960d7ba7e0c8396e154028c9045c54 upstream. + +Implement UMH_KILLABLE, should be used along with UMH_WAIT_EXEC/PROC. +The caller must ensure that subprocess_info->path/etc can not go away +until call_usermodehelper_freeinfo(). + +call_usermodehelper_exec(UMH_KILLABLE) does +wait_for_completion_killable. If it fails, it uses +xchg(&sub_info->complete, NULL) to serialize with umh_complete() which +does the same xhcg() to access sub_info->complete. + +If call_usermodehelper_exec wins, it can safely return. umh_complete() +should get NULL and call call_usermodehelper_freeinfo(). + +Otherwise we know that umh_complete() was already called, in this case +call_usermodehelper_exec() falls back to wait_for_completion() which +should succeed "very soon". + +Note: UMH_NO_WAIT == -1 but it obviously should not be used with +UMH_KILLABLE. We delay the neccessary cleanup to simplify the back +porting. + +Signed-off-by: Oleg Nesterov +Cc: Tetsuo Handa +Cc: Rusty Russell +Cc: Tejun Heo +Cc: David Rientjes +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + include/linux/kmod.h | 2 ++ + kernel/kmod.c | 27 +++++++++++++++++++++++++-- + 2 files changed, 27 insertions(+), 2 deletions(-) + +diff --git a/include/linux/kmod.h b/include/linux/kmod.h +index 722f477..1b598585 100644 +--- a/include/linux/kmod.h ++++ b/include/linux/kmod.h +@@ -54,6 +54,8 @@ enum umh_wait { + UMH_WAIT_PROC = 1, /* wait for the process to complete */ + }; + ++#define UMH_KILLABLE 4 /* wait for EXEC/PROC killable */ ++ + struct subprocess_info { + struct work_struct work; + struct completion *complete; +diff --git a/kernel/kmod.c b/kernel/kmod.c +index 8ea2594..f92f917 100644 +--- a/kernel/kmod.c ++++ b/kernel/kmod.c +@@ -201,7 +201,15 @@ EXPORT_SYMBOL(call_usermodehelper_freeinfo); + + static void umh_complete(struct subprocess_info *sub_info) + { +- complete(sub_info->complete); ++ struct completion *comp = xchg(&sub_info->complete, NULL); ++ /* ++ * See call_usermodehelper_exec(). If xchg() returns NULL ++ * we own sub_info, the UMH_KILLABLE caller has gone away. ++ */ ++ if (comp) ++ complete(comp); ++ else ++ call_usermodehelper_freeinfo(sub_info); + } + + /* Keventd can't block, but this (a child) can. */ +@@ -252,6 +260,9 @@ static void __call_usermodehelper(struct work_struct *work) + enum umh_wait wait = sub_info->wait; + pid_t pid; + ++ if (wait != UMH_NO_WAIT) ++ wait &= ~UMH_KILLABLE; ++ + /* CLONE_VFORK: wait until the usermode helper has execve'd + * successfully We need the data structures to stay around + * until that is done. */ +@@ -461,9 +472,21 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, + queue_work(khelper_wq, &sub_info->work); + if (wait == UMH_NO_WAIT) /* task has freed sub_info */ + goto unlock; ++ ++ if (wait & UMH_KILLABLE) { ++ retval = wait_for_completion_killable(&done); ++ if (!retval) ++ goto wait_done; ++ ++ /* umh_complete() will see NULL and free sub_info */ ++ if (xchg(&sub_info->complete, NULL)) ++ goto unlock; ++ /* fallthrough, umh_complete() was already called */ ++ } ++ + wait_for_completion(&done); ++wait_done: + retval = sub_info->retval; +- + out: + call_usermodehelper_freeinfo(sub_info); + unlock: diff --git a/debian/patches/bugfix/all/usermodehelper-introduce-umh_complete.patch b/debian/patches/bugfix/all/usermodehelper-introduce-umh_complete.patch new file mode 100644 index 000000000..18007e191 --- /dev/null +++ b/debian/patches/bugfix/all/usermodehelper-introduce-umh_complete.patch @@ -0,0 +1,54 @@ +From: Oleg Nesterov +Date: Fri, 23 Mar 2012 15:02:47 -0700 +Subject: [1/5] usermodehelper: introduce umh_complete(sub_info) + +commit b3449922502f5a161ee2b5022a33aec8472fbf18 upstream. + +Preparation. Add the new trivial helper, umh_complete(). Currently it +simply does complete(sub_info->complete). + +Signed-off-by: Oleg Nesterov +Cc: Tetsuo Handa +Cc: Rusty Russell +Cc: Tejun Heo +Cc: David Rientjes +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + kernel/kmod.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/kernel/kmod.c b/kernel/kmod.c +index a0a8854..8ea2594 100644 +--- a/kernel/kmod.c ++++ b/kernel/kmod.c +@@ -199,6 +199,11 @@ void call_usermodehelper_freeinfo(struct subprocess_info *info) + } + EXPORT_SYMBOL(call_usermodehelper_freeinfo); + ++static void umh_complete(struct subprocess_info *sub_info) ++{ ++ complete(sub_info->complete); ++} ++ + /* Keventd can't block, but this (a child) can. */ + static int wait_for_helper(void *data) + { +@@ -235,7 +240,7 @@ static int wait_for_helper(void *data) + sub_info->retval = ret; + } + +- complete(sub_info->complete); ++ umh_complete(sub_info); + return 0; + } + +@@ -269,7 +274,7 @@ static void __call_usermodehelper(struct work_struct *work) + case UMH_WAIT_EXEC: + if (pid < 0) + sub_info->retval = pid; +- complete(sub_info->complete); ++ umh_complete(sub_info); + } + } + diff --git a/debian/patches/series b/debian/patches/series index a9422d966..1f2792ccd 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -423,3 +423,8 @@ bugfix/all/firmware-remove-redundant-log-messages-from-drivers.patch bugfix/x86/ACPI-video-ignore-BIOS-initial-backlight-value-for-H.patch bugfix/x86/KVM-x86-invalid-opcode-oops-on-SET_SREGS-with-OSXSAV.patch +bugfix/all/usermodehelper-introduce-umh_complete.patch +bugfix/all/usermodehelper-implement-UMH_KILLABLE.patch +bugfix/all/usermodehelper-____call_usermodehelper-doesnt-need-do_exit.patch +bugfix/all/kmod-introduce-call_modprobe-helper.patch +bugfix/all/kmod-make-__request_module-killable.patch