From 742a0bf3f5055444eeb07c9bdd9a0fc28f0c6c41 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Jun 13 2016 11:41:18 +0000 Subject: CVE-2016-1583 stack overflow via ecryptfs and /proc (rhbz 1344721 1344722) --- diff --git a/ecryptfs-fix-handling-of-directory-opening.patch b/ecryptfs-fix-handling-of-directory-opening.patch new file mode 100644 index 0000000..ce5f511 --- /dev/null +++ b/ecryptfs-fix-handling-of-directory-opening.patch @@ -0,0 +1,142 @@ +From 6a480a7842545ec520a91730209ec0bae41694c1 Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Wed, 4 May 2016 14:04:13 -0400 +Subject: [PATCH] ecryptfs: fix handling of directory opening + +First of all, trying to open them r/w is idiocy; it's guaranteed to fail. +Moreover, assigning ->f_pos and assuming that everything will work is +blatantly broken - try that with e.g. tmpfs as underlying layer and watch +the fireworks. There may be a non-trivial amount of state associated with +current IO position, well beyond the numeric offset. Using the single +struct file associated with underlying inode is really not a good idea; +we ought to open one for each ecryptfs directory struct file. + +Additionally, file_operations both for directories and non-directories are +full of pointless methods; non-directories should *not* have ->iterate(), +directories should not have ->flush(), ->fasync() and ->splice_read(). + +Signed-off-by: Al Viro +--- + fs/ecryptfs/file.c | 71 ++++++++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 55 insertions(+), 16 deletions(-) + +diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c +index feef8a9c4de7..f02404052b7b 100644 +--- a/fs/ecryptfs/file.c ++++ b/fs/ecryptfs/file.c +@@ -112,7 +112,6 @@ static int ecryptfs_readdir(struct file *file, struct dir_context *ctx) + .sb = inode->i_sb, + }; + lower_file = ecryptfs_file_to_lower(file); +- lower_file->f_pos = ctx->pos; + rc = iterate_dir(lower_file, &buf.ctx); + ctx->pos = buf.ctx.pos; + if (rc < 0) +@@ -223,14 +222,6 @@ static int ecryptfs_open(struct inode *inode, struct file *file) + } + ecryptfs_set_file_lower( + file, ecryptfs_inode_to_private(inode)->lower_file); +- if (d_is_dir(ecryptfs_dentry)) { +- ecryptfs_printk(KERN_DEBUG, "This is a directory\n"); +- mutex_lock(&crypt_stat->cs_mutex); +- crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); +- mutex_unlock(&crypt_stat->cs_mutex); +- rc = 0; +- goto out; +- } + rc = read_or_initialize_metadata(ecryptfs_dentry); + if (rc) + goto out_put; +@@ -247,6 +238,45 @@ out: + return rc; + } + ++/** ++ * ecryptfs_dir_open ++ * @inode: inode speciying file to open ++ * @file: Structure to return filled in ++ * ++ * Opens the file specified by inode. ++ * ++ * Returns zero on success; non-zero otherwise ++ */ ++static int ecryptfs_dir_open(struct inode *inode, struct file *file) ++{ ++ struct dentry *ecryptfs_dentry = file->f_path.dentry; ++ /* Private value of ecryptfs_dentry allocated in ++ * ecryptfs_lookup() */ ++ struct ecryptfs_file_info *file_info; ++ struct file *lower_file; ++ ++ /* Released in ecryptfs_release or end of function if failure */ ++ file_info = kmem_cache_zalloc(ecryptfs_file_info_cache, GFP_KERNEL); ++ ecryptfs_set_file_private(file, file_info); ++ if (unlikely(!file_info)) { ++ ecryptfs_printk(KERN_ERR, ++ "Error attempting to allocate memory\n"); ++ return -ENOMEM; ++ } ++ lower_file = dentry_open(ecryptfs_dentry_to_lower_path(ecryptfs_dentry), ++ file->f_flags, current_cred()); ++ if (IS_ERR(lower_file)) { ++ printk(KERN_ERR "%s: Error attempting to initialize " ++ "the lower file for the dentry with name " ++ "[%pd]; rc = [%ld]\n", __func__, ++ ecryptfs_dentry, PTR_ERR(lower_file)); ++ kmem_cache_free(ecryptfs_file_info_cache, file_info); ++ return PTR_ERR(lower_file); ++ } ++ ecryptfs_set_file_lower(file, lower_file); ++ return 0; ++} ++ + static int ecryptfs_flush(struct file *file, fl_owner_t td) + { + struct file *lower_file = ecryptfs_file_to_lower(file); +@@ -267,6 +297,19 @@ static int ecryptfs_release(struct inode *inode, struct file *file) + return 0; + } + ++static int ecryptfs_dir_release(struct inode *inode, struct file *file) ++{ ++ fput(ecryptfs_file_to_lower(file)); ++ kmem_cache_free(ecryptfs_file_info_cache, ++ ecryptfs_file_to_private(file)); ++ return 0; ++} ++ ++static loff_t ecryptfs_dir_llseek(struct file *file, loff_t offset, int whence) ++{ ++ return vfs_llseek(ecryptfs_file_to_lower(file), offset, whence); ++} ++ + static int + ecryptfs_fsync(struct file *file, loff_t start, loff_t end, int datasync) + { +@@ -346,20 +389,16 @@ const struct file_operations ecryptfs_dir_fops = { + #ifdef CONFIG_COMPAT + .compat_ioctl = ecryptfs_compat_ioctl, + #endif +- .open = ecryptfs_open, +- .flush = ecryptfs_flush, +- .release = ecryptfs_release, ++ .open = ecryptfs_dir_open, ++ .release = ecryptfs_dir_release, + .fsync = ecryptfs_fsync, +- .fasync = ecryptfs_fasync, +- .splice_read = generic_file_splice_read, +- .llseek = default_llseek, ++ .llseek = ecryptfs_dir_llseek, + }; + + const struct file_operations ecryptfs_main_fops = { + .llseek = generic_file_llseek, + .read_iter = ecryptfs_read_update_atime, + .write_iter = generic_file_write_iter, +- .iterate = ecryptfs_readdir, + .unlocked_ioctl = ecryptfs_unlocked_ioctl, + #ifdef CONFIG_COMPAT + .compat_ioctl = ecryptfs_compat_ioctl, +-- +2.5.5 + diff --git a/ecryptfs-forbid-opening-files-without-mmap-handler.patch b/ecryptfs-forbid-opening-files-without-mmap-handler.patch new file mode 100644 index 0000000..2d40e68 --- /dev/null +++ b/ecryptfs-forbid-opening-files-without-mmap-handler.patch @@ -0,0 +1,59 @@ +From 2f36db71009304b3f0b95afacd8eba1f9f046b87 Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Wed, 1 Jun 2016 11:55:06 +0200 +Subject: [PATCH] ecryptfs: forbid opening files without mmap handler + +This prevents users from triggering a stack overflow through a recursive +invocation of pagefault handling that involves mapping procfs files into +virtual memory. + +Signed-off-by: Jann Horn +Acked-by: Tyler Hicks +Cc: stable@vger.kernel.org +Signed-off-by: Linus Torvalds +--- + fs/ecryptfs/kthread.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c +index 866bb18efefe..e818f5ac7a26 100644 +--- a/fs/ecryptfs/kthread.c ++++ b/fs/ecryptfs/kthread.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include "ecryptfs_kernel.h" + + struct ecryptfs_open_req { +@@ -147,7 +148,7 @@ int ecryptfs_privileged_open(struct file **lower_file, + flags |= IS_RDONLY(d_inode(lower_dentry)) ? O_RDONLY : O_RDWR; + (*lower_file) = dentry_open(&req.path, flags, cred); + if (!IS_ERR(*lower_file)) +- goto out; ++ goto have_file; + if ((flags & O_ACCMODE) == O_RDONLY) { + rc = PTR_ERR((*lower_file)); + goto out; +@@ -165,8 +166,16 @@ int ecryptfs_privileged_open(struct file **lower_file, + mutex_unlock(&ecryptfs_kthread_ctl.mux); + wake_up(&ecryptfs_kthread_ctl.wait); + wait_for_completion(&req.done); +- if (IS_ERR(*lower_file)) ++ if (IS_ERR(*lower_file)) { + rc = PTR_ERR(*lower_file); ++ goto out; ++ } ++have_file: ++ if ((*lower_file)->f_op->mmap == NULL) { ++ fput(*lower_file); ++ *lower_file = NULL; ++ rc = -EMEDIUMTYPE; ++ } + out: + return rc; + } +-- +2.5.5 + diff --git a/kernel.spec b/kernel.spec index ce1970d..142e507 100644 --- a/kernel.spec +++ b/kernel.spec @@ -658,6 +658,12 @@ Patch721: tipc-fix-an-infoleak-in-tipc_nl_compat_link_dump.patch #CVE-2016-5244 rhbz 1343338 1343337 Patch722: rds-fix-an-infoleak-in-rds_inc_info_copy.txt +#CVE-2016-1583 rhbz 1344721 1344722 +Patch723: proc-prevent-stacking-filesystems-on-top.patch +Patch724: ecryptfs-fix-handling-of-directory-opening.patch +Patch725: ecryptfs-forbid-opening-files-without-mmap-handler.patch +Patch726: sched-panic-on-corrupted-stack-end.patch + # END OF PATCH DEFINITIONS %endif @@ -2175,6 +2181,9 @@ fi # # %changelog +* Mon Jun 13 2016 Josh Boyer +- CVE-2016-1583 stack overflow via ecryptfs and /proc (rhbz 1344721 1344722) + * Wed Jun 08 2016 Josh Boyer - 4.5.7-200 - Linux v4.5.7 diff --git a/proc-prevent-stacking-filesystems-on-top.patch b/proc-prevent-stacking-filesystems-on-top.patch new file mode 100644 index 0000000..178aa3b --- /dev/null +++ b/proc-prevent-stacking-filesystems-on-top.patch @@ -0,0 +1,41 @@ +From e54ad7f1ee263ffa5a2de9c609d58dfa27b21cd9 Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Wed, 1 Jun 2016 11:55:05 +0200 +Subject: [PATCH] proc: prevent stacking filesystems on top + +This prevents stacking filesystems (ecryptfs and overlayfs) from using +procfs as lower filesystem. There is too much magic going on inside +procfs, and there is no good reason to stack stuff on top of procfs. + +(For example, procfs does access checks in VFS open handlers, and +ecryptfs by design calls open handlers from a kernel thread that doesn't +drop privileges or so.) + +Signed-off-by: Jann Horn +Cc: stable@vger.kernel.org +Signed-off-by: Linus Torvalds +--- + fs/proc/root.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/fs/proc/root.c b/fs/proc/root.c +index 361ab4ee42fc..ec649c92d270 100644 +--- a/fs/proc/root.c ++++ b/fs/proc/root.c +@@ -121,6 +121,13 @@ static struct dentry *proc_mount(struct file_system_type *fs_type, + if (IS_ERR(sb)) + return ERR_CAST(sb); + ++ /* ++ * procfs isn't actually a stacking filesystem; however, there is ++ * too much magic going on inside it to permit stacking things on ++ * top of it ++ */ ++ sb->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH; ++ + if (!proc_parse_options(options, ns)) { + deactivate_locked_super(sb); + return ERR_PTR(-EINVAL); +-- +2.5.5 + diff --git a/sched-panic-on-corrupted-stack-end.patch b/sched-panic-on-corrupted-stack-end.patch new file mode 100644 index 0000000..1d6bbaf --- /dev/null +++ b/sched-panic-on-corrupted-stack-end.patch @@ -0,0 +1,36 @@ +From 29d6455178a09e1dc340380c582b13356227e8df Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Wed, 1 Jun 2016 11:55:07 +0200 +Subject: [PATCH] sched: panic on corrupted stack end + +Until now, hitting this BUG_ON caused a recursive oops (because oops +handling involves do_exit(), which calls into the scheduler, which in +turn raises an oops), which caused stuff below the stack to be +overwritten until a panic happened (e.g. via an oops in interrupt +context, caused by the overwritten CPU index in the thread_info). + +Just panic directly. + +Signed-off-by: Jann Horn +Signed-off-by: Linus Torvalds +--- + kernel/sched/core.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index d1f7149f8704..11546a6ed5df 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -3047,7 +3047,8 @@ static noinline void __schedule_bug(struct task_struct *prev) + static inline void schedule_debug(struct task_struct *prev) + { + #ifdef CONFIG_SCHED_STACK_END_CHECK +- BUG_ON(task_stack_end_corrupted(prev)); ++ if (task_stack_end_corrupted(prev)) ++ panic("corrupted stack end detected inside scheduler\n"); + #endif + + if (unlikely(in_atomic_preempt_off())) { +-- +2.5.5 +