From 9350d3cf6b9a0f9236ad0db0fed625d1a7ab488a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Jul 2013 11:09:53 -0700 Subject: [PATCH] 3.4-stable patches added patches: perf-disable-monitoring-on-setuid-processes-for-regular-users.patch ubifs-fix-a-horrid-bug.patch ubifs-prepare-to-fix-a-horrid-bug.patch --- ...n-setuid-processes-for-regular-users.patch | 61 +++++++++ queue-3.4/series | 3 + queue-3.4/ubifs-fix-a-horrid-bug.patch | 96 ++++++++++++++ .../ubifs-prepare-to-fix-a-horrid-bug.patch | 125 ++++++++++++++++++ 4 files changed, 285 insertions(+) create mode 100644 queue-3.4/perf-disable-monitoring-on-setuid-processes-for-regular-users.patch create mode 100644 queue-3.4/ubifs-fix-a-horrid-bug.patch create mode 100644 queue-3.4/ubifs-prepare-to-fix-a-horrid-bug.patch diff --git a/queue-3.4/perf-disable-monitoring-on-setuid-processes-for-regular-users.patch b/queue-3.4/perf-disable-monitoring-on-setuid-processes-for-regular-users.patch new file mode 100644 index 00000000000..8813d7ec230 --- /dev/null +++ b/queue-3.4/perf-disable-monitoring-on-setuid-processes-for-regular-users.patch @@ -0,0 +1,61 @@ +From 2976b10f05bd7f6dab9f9e7524451ddfed656a89 Mon Sep 17 00:00:00 2001 +From: Stephane Eranian +Date: Thu, 20 Jun 2013 11:36:28 +0200 +Subject: perf: Disable monitoring on setuid processes for regular users + +From: Stephane Eranian + +commit 2976b10f05bd7f6dab9f9e7524451ddfed656a89 upstream. + +There was a a bug in setup_new_exec(), whereby +the test to disabled perf monitoring was not +correct because the new credentials for the +process were not yet committed and therefore +the get_dumpable() test was never firing. + +The patch fixes the problem by moving the +perf_event test until after the credentials +are committed. + +Signed-off-by: Stephane Eranian +Tested-by: Jiri Olsa +Acked-by: Peter Zijlstra +Signed-off-by: Ingo Molnar +Signed-off-by: Greg Kroah-Hartman + +--- + fs/exec.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -1163,13 +1163,6 @@ void setup_new_exec(struct linux_binprm + set_dumpable(current->mm, suid_dumpable); + } + +- /* +- * Flush performance counters when crossing a +- * security domain: +- */ +- if (!get_dumpable(current->mm)) +- perf_event_exit_task(current); +- + /* An exec changes our domain. We are no longer part of the thread + group */ + +@@ -1233,6 +1226,15 @@ void install_exec_creds(struct linux_bin + + commit_creds(bprm->cred); + bprm->cred = NULL; ++ ++ /* ++ * Disable monitoring for regular users ++ * when executing setuid binaries. Must ++ * wait until new credentials are committed ++ * by commit_creds() above ++ */ ++ if (get_dumpable(current->mm) != SUID_DUMP_USER) ++ perf_event_exit_task(current); + /* + * cred_guard_mutex must be held at least to this point to prevent + * ptrace_attach() from altering our determination of the task's diff --git a/queue-3.4/series b/queue-3.4/series index 959821766c4..977b2228ccf 100644 --- a/queue-3.4/series +++ b/queue-3.4/series @@ -6,3 +6,6 @@ hw_breakpoint-use-cpu_possible_mask-in-reserve-release-_bp_slot.patch dlci-acquire-rtnl_lock-before-calling-__dev_get_by_name.patch dlci-validate-the-net-device-in-dlci_del.patch net-tg3-avoid-delay-during-mmio-access.patch +perf-disable-monitoring-on-setuid-processes-for-regular-users.patch +ubifs-prepare-to-fix-a-horrid-bug.patch +ubifs-fix-a-horrid-bug.patch diff --git a/queue-3.4/ubifs-fix-a-horrid-bug.patch b/queue-3.4/ubifs-fix-a-horrid-bug.patch new file mode 100644 index 00000000000..8ac832e5397 --- /dev/null +++ b/queue-3.4/ubifs-fix-a-horrid-bug.patch @@ -0,0 +1,96 @@ +From 605c912bb843c024b1ed173dc427cd5c08e5d54d Mon Sep 17 00:00:00 2001 +From: Artem Bityutskiy +Date: Fri, 28 Jun 2013 14:15:15 +0300 +Subject: UBIFS: fix a horrid bug + +From: Artem Bityutskiy + +commit 605c912bb843c024b1ed173dc427cd5c08e5d54d upstream. + +Al Viro pointed me to the fact that '->readdir()' and '->llseek()' have no +mutual exclusion, which means the 'ubifs_dir_llseek()' can be run while we are +in the middle of 'ubifs_readdir()'. + +This means that 'file->private_data' can be freed while 'ubifs_readdir()' uses +it, and this is a very bad bug: not only 'ubifs_readdir()' can return garbage, +but this may corrupt memory and lead to all kinds of problems like crashes an +security holes. + +This patch fixes the problem by using the 'file->f_version' field, which +'->llseek()' always unconditionally sets to zero. We set it to 1 in +'ubifs_readdir()' and whenever we detect that it became 0, we know there was a +seek and it is time to clear the state saved in 'file->private_data'. + +I tested this patch by writing a user-space program which runds readdir and +seek in parallell. I could easily crash the kernel without these patches, but +could not crash it with these patches. + +Reported-by: Al Viro +Tested-by: Artem Bityutskiy +Signed-off-by: Artem Bityutskiy +Signed-off-by: Al Viro +Signed-off-by: Greg Kroah-Hartman + +--- + fs/ubifs/dir.c | 30 +++++++++++++++++++++++++++--- + 1 file changed, 27 insertions(+), 3 deletions(-) + +--- a/fs/ubifs/dir.c ++++ b/fs/ubifs/dir.c +@@ -373,6 +373,24 @@ static int ubifs_readdir(struct file *fi + */ + return 0; + ++ if (file->f_version == 0) { ++ /* ++ * The file was seek'ed, which means that @file->private_data ++ * is now invalid. This may also be just the first ++ * 'ubifs_readdir()' invocation, in which case ++ * @file->private_data is NULL, and the below code is ++ * basically a no-op. ++ */ ++ kfree(file->private_data); ++ file->private_data = NULL; ++ } ++ ++ /* ++ * 'generic_file_llseek()' unconditionally sets @file->f_version to ++ * zero, and we use this for detecting whether the file was seek'ed. ++ */ ++ file->f_version = 1; ++ + /* File positions 0 and 1 correspond to "." and ".." */ + if (pos == 0) { + ubifs_assert(!file->private_data); +@@ -446,6 +464,14 @@ static int ubifs_readdir(struct file *fi + file->f_pos = pos = key_hash_flash(c, &dent->key); + file->private_data = dent; + cond_resched(); ++ ++ if (file->f_version == 0) ++ /* ++ * The file was seek'ed meanwhile, lets return and start ++ * reading direntries from the new position on the next ++ * invocation. ++ */ ++ return 0; + } + + out: +@@ -456,15 +482,13 @@ out: + + kfree(file->private_data); + file->private_data = NULL; ++ /* 2 is a special value indicating that there are no more direntries */ + file->f_pos = 2; + return 0; + } + +-/* If a directory is seeked, we have to free saved readdir() state */ + static loff_t ubifs_dir_llseek(struct file *file, loff_t offset, int origin) + { +- kfree(file->private_data); +- file->private_data = NULL; + return generic_file_llseek(file, offset, origin); + } + diff --git a/queue-3.4/ubifs-prepare-to-fix-a-horrid-bug.patch b/queue-3.4/ubifs-prepare-to-fix-a-horrid-bug.patch new file mode 100644 index 00000000000..f6c1f956cc3 --- /dev/null +++ b/queue-3.4/ubifs-prepare-to-fix-a-horrid-bug.patch @@ -0,0 +1,125 @@ +From 33f1a63ae84dfd9ad298cf275b8f1887043ced36 Mon Sep 17 00:00:00 2001 +From: Artem Bityutskiy +Date: Fri, 28 Jun 2013 14:15:14 +0300 +Subject: UBIFS: prepare to fix a horrid bug + +From: Artem Bityutskiy + +commit 33f1a63ae84dfd9ad298cf275b8f1887043ced36 upstream. + +Al Viro pointed me to the fact that '->readdir()' and '->llseek()' have no +mutual exclusion, which means the 'ubifs_dir_llseek()' can be run while we are +in the middle of 'ubifs_readdir()'. + +First of all, this means that 'file->private_data' can be freed while +'ubifs_readdir()' uses it. But this particular patch does not fix the problem. +This patch is only a preparation, and the fix will follow next. + +In this patch we make 'ubifs_readdir()' stop using 'file->f_pos' directly, +because 'file->f_pos' can be changed by '->llseek()' at any point. This may +lead 'ubifs_readdir()' to returning inconsistent data: directory entry names +may correspond to incorrect file positions. + +So here we introduce a local variable 'pos', read 'file->f_pose' once at very +the beginning, and then stick to 'pos'. The result of this is that when +'ubifs_dir_llseek()' changes 'file->f_pos' while we are in the middle of +'ubifs_readdir()', the latter "wins". + +Reported-by: Al Viro +Tested-by: Artem Bityutskiy +Signed-off-by: Artem Bityutskiy +Signed-off-by: Al Viro +Signed-off-by: Greg Kroah-Hartman + +--- + fs/ubifs/dir.c | 24 ++++++++++++------------ + 1 file changed, 12 insertions(+), 12 deletions(-) + +--- a/fs/ubifs/dir.c ++++ b/fs/ubifs/dir.c +@@ -357,15 +357,16 @@ static unsigned int vfs_dent_type(uint8_ + static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) + { + int err, over = 0; ++ loff_t pos = file->f_pos; + struct qstr nm; + union ubifs_key key; + struct ubifs_dent_node *dent; + struct inode *dir = file->f_path.dentry->d_inode; + struct ubifs_info *c = dir->i_sb->s_fs_info; + +- dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos); ++ dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, pos); + +- if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2) ++ if (pos > UBIFS_S_KEY_HASH_MASK || pos == 2) + /* + * The directory was seek'ed to a senseless position or there + * are no more entries. +@@ -373,15 +374,15 @@ static int ubifs_readdir(struct file *fi + return 0; + + /* File positions 0 and 1 correspond to "." and ".." */ +- if (file->f_pos == 0) { ++ if (pos == 0) { + ubifs_assert(!file->private_data); + over = filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR); + if (over) + return 0; +- file->f_pos = 1; ++ file->f_pos = pos = 1; + } + +- if (file->f_pos == 1) { ++ if (pos == 1) { + ubifs_assert(!file->private_data); + over = filldir(dirent, "..", 2, 1, + parent_ino(file->f_path.dentry), DT_DIR); +@@ -397,7 +398,7 @@ static int ubifs_readdir(struct file *fi + goto out; + } + +- file->f_pos = key_hash_flash(c, &dent->key); ++ file->f_pos = pos = key_hash_flash(c, &dent->key); + file->private_data = dent; + } + +@@ -405,17 +406,16 @@ static int ubifs_readdir(struct file *fi + if (!dent) { + /* + * The directory was seek'ed to and is now readdir'ed. +- * Find the entry corresponding to @file->f_pos or the +- * closest one. ++ * Find the entry corresponding to @pos or the closest one. + */ +- dent_key_init_hash(c, &key, dir->i_ino, file->f_pos); ++ dent_key_init_hash(c, &key, dir->i_ino, pos); + nm.name = NULL; + dent = ubifs_tnc_next_ent(c, &key, &nm); + if (IS_ERR(dent)) { + err = PTR_ERR(dent); + goto out; + } +- file->f_pos = key_hash_flash(c, &dent->key); ++ file->f_pos = pos = key_hash_flash(c, &dent->key); + file->private_data = dent; + } + +@@ -427,7 +427,7 @@ static int ubifs_readdir(struct file *fi + ubifs_inode(dir)->creat_sqnum); + + nm.len = le16_to_cpu(dent->nlen); +- over = filldir(dirent, dent->name, nm.len, file->f_pos, ++ over = filldir(dirent, dent->name, nm.len, pos, + le64_to_cpu(dent->inum), + vfs_dent_type(dent->type)); + if (over) +@@ -443,7 +443,7 @@ static int ubifs_readdir(struct file *fi + } + + kfree(file->private_data); +- file->f_pos = key_hash_flash(c, &dent->key); ++ file->f_pos = pos = key_hash_flash(c, &dent->key); + file->private_data = dent; + cond_resched(); + } -- 2.47.3