From 859a1735be48a2ff960354772832c65b15e3377c Mon Sep 17 00:00:00 2001 From: Sona Sarmadi Date: Wed, 27 Jan 2016 07:47:06 +0100 Subject: kernel-vfs: CVE-2015-2925 Fixes a flaw which was found in the way the Linux kernel's file system implementation handled rename operations in which the source was inside and the destination was outside of a bind mount. A privileged user inside a container could use this flaw to escape the bind mount and, potentially, escalate their privileges on the system. References: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-2925 http://www.openwall.com/lists/oss-security/2015/04/03/7 Reference to the upstream fixes: vfs: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ commit/?id=397d425dc26da728396e66d392d5dcb8dac30c37 dcache: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ commit/?id=cde93be45a8a90d8c264c776fab63487b5038a65 Signed-off-by: Sona Sarmadi Signed-off-by: Paul Vaduva --- .../linux-qoriq-3.12/dcache-CVE-2015-2925.patch | 70 ++++++++++++ .../linux/linux-qoriq-3.12/vfs-CVE-2015-2925.patch | 126 +++++++++++++++++++++ recipes-kernel/linux/linux-qoriq-common.inc | 3 + .../linux-yocto-3.14/dcache-CVE-2015-2925.patch | 69 +++++++++++ .../linux/linux-yocto-3.14/vfs-CVE-2015-2925.patch | 126 +++++++++++++++++++++ recipes-kernel/linux/linux-yocto_3.14.bbappend | 3 + 6 files changed, 397 insertions(+) create mode 100644 recipes-kernel/linux/linux-qoriq-3.12/dcache-CVE-2015-2925.patch create mode 100644 recipes-kernel/linux/linux-qoriq-3.12/vfs-CVE-2015-2925.patch create mode 100644 recipes-kernel/linux/linux-yocto-3.14/dcache-CVE-2015-2925.patch create mode 100644 recipes-kernel/linux/linux-yocto-3.14/vfs-CVE-2015-2925.patch diff --git a/recipes-kernel/linux/linux-qoriq-3.12/dcache-CVE-2015-2925.patch b/recipes-kernel/linux/linux-qoriq-3.12/dcache-CVE-2015-2925.patch new file mode 100644 index 0000000..1d70c0d --- /dev/null +++ b/recipes-kernel/linux/linux-qoriq-3.12/dcache-CVE-2015-2925.patch @@ -0,0 +1,70 @@ +From b32388c040d559d26feef31fa02b4119c76be474 Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Sat, 15 Aug 2015 13:36:12 -0500 +Subject: dcache: Handle escaped paths in prepend_path + +commit cde93be45a8a90d8c264c776fab63487b5038a65 upstream. + +A rename can result in a dentry that by walking up d_parent +will never reach it's mnt_root. For lack of a better term +I call this an escaped path. + +prepend_path is called by four different functions __d_path, +d_absolute_path, d_path, and getcwd. + +__d_path only wants to see paths are connected to the root it passes +in. So __d_path needs prepend_path to return an error. + +d_absolute_path similarly wants to see paths that are connected to +some root. Escaped paths are not connected to any mnt_root so +d_absolute_path needs prepend_path to return an error greater +than 1. So escaped paths will be treated like paths on lazily +unmounted mounts. + +getcwd needs to prepend "(unreachable)" so getcwd also needs +prepend_path to return an error. + +d_path is the interesting hold out. d_path just wants to print +something, and does not care about the weird cases. Which raises +the question what should be printed? + +Given that / should result in -ENOENT I +believe it is desirable for escaped paths to be printed as empty +paths. As there are not really any meaninful path components when +considered from the perspective of a mount tree. + +So tweak prepend_path to return an empty path with an new error +code of 3 when it encounters an escaped path. + +Fixes CVE-2015-2925. +Upstream-Status: Backport + +Signed-off-by: "Eric W. Biederman" +Signed-off-by: Al Viro +Signed-off-by: Jiri Slaby +Signed-off-by: Sona Sarmadi +--- + fs/dcache.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/fs/dcache.c b/fs/dcache.c +index 4c227f8..0fa3b3d 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -2808,6 +2808,13 @@ restart: + struct dentry * parent; + + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { ++ /* Escaped? */ ++ if (dentry != vfsmnt->mnt_root) { ++ bptr = *buffer; ++ blen = *buflen; ++ error = 3; ++ break; ++ } + /* Global root? */ + if (mnt_has_parent(mnt)) { + dentry = mnt->mnt_mountpoint; +-- +cgit v0.12 + diff --git a/recipes-kernel/linux/linux-qoriq-3.12/vfs-CVE-2015-2925.patch b/recipes-kernel/linux/linux-qoriq-3.12/vfs-CVE-2015-2925.patch new file mode 100644 index 0000000..e01b0e5 --- /dev/null +++ b/recipes-kernel/linux/linux-qoriq-3.12/vfs-CVE-2015-2925.patch @@ -0,0 +1,126 @@ +From 2ca1ae468673a36d1a2dc5946995ba617f8cba96 Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Sat, 15 Aug 2015 20:27:13 -0500 +Subject: vfs: Test for and handle paths that are unreachable from their + mnt_root + +commit 397d425dc26da728396e66d392d5dcb8dac30c37 upstream. + +In rare cases a directory can be renamed out from under a bind mount. +In those cases without special handling it becomes possible to walk up +the directory tree to the root dentry of the filesystem and down +from the root dentry to every other file or directory on the filesystem. + +Like division by zero .. from an unconnected path can not be given +a useful semantic as there is no predicting at which path component +the code will realize it is unconnected. We certainly can not match +the current behavior as the current behavior is a security hole. + +Therefore when encounting .. when following an unconnected path +return -ENOENT. + +- Add a function path_connected to verify path->dentry is reachable + from path->mnt.mnt_root. AKA to validate that rename did not do + something nasty to the bind mount. + + To avoid races path_connected must be called after following a path + component to it's next path component. + +Fixes CVE-2015-2925. +Upstream-Status: Backport + +Signed-off-by: "Eric W. Biederman" +Signed-off-by: Al Viro +Signed-off-by: Jiri Slaby +Signed-off-by: Sona Sarmadi +--- + fs/namei.c | 31 ++++++++++++++++++++++++++++--- + 1 file changed, 28 insertions(+), 3 deletions(-) + +diff --git a/fs/namei.c b/fs/namei.c +index 097bbeac..d1c0b91 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -473,6 +473,24 @@ void path_put(const struct path *path) + } + EXPORT_SYMBOL(path_put); + ++/** ++ * path_connected - Verify that a path->dentry is below path->mnt.mnt_root ++ * @path: nameidate to verify ++ * ++ * Rename can sometimes move a file or directory outside of a bind ++ * mount, path_connected allows those cases to be detected. ++ */ ++static bool path_connected(const struct path *path) ++{ ++ struct vfsmount *mnt = path->mnt; ++ ++ /* Only bind mounts can have disconnected paths */ ++ if (mnt->mnt_root == mnt->mnt_sb->s_root) ++ return true; ++ ++ return is_subdir(path->dentry, mnt->mnt_root); ++} ++ + /* + * Path walking has 2 modes, rcu-walk and ref-walk (see + * Documentation/filesystems/path-lookup.txt). In situations when we can't +@@ -1162,6 +1180,8 @@ static int follow_dotdot_rcu(struct nameidata *nd) + goto failed; + nd->path.dentry = parent; + nd->seq = seq; ++ if (unlikely(!path_connected(&nd->path))) ++ goto failed; + break; + } + if (!follow_up_rcu(&nd->path)) +@@ -1245,7 +1265,7 @@ static void follow_mount(struct path *path) + } + } + +-static void follow_dotdot(struct nameidata *nd) ++static int follow_dotdot(struct nameidata *nd) + { + if (!nd->root.mnt) + set_root(nd); +@@ -1261,6 +1281,10 @@ static void follow_dotdot(struct nameidata *nd) + /* rare case of legitimate dget_parent()... */ + nd->path.dentry = dget_parent(nd->path.dentry); + dput(old); ++ if (unlikely(!path_connected(&nd->path))) { ++ path_put(&nd->path); ++ return -ENOENT; ++ } + break; + } + if (!follow_up(&nd->path)) +@@ -1268,6 +1292,7 @@ static void follow_dotdot(struct nameidata *nd) + } + follow_mount(&nd->path); + nd->inode = nd->path.dentry->d_inode; ++ return 0; + } + + /* +@@ -1491,7 +1516,7 @@ static inline int handle_dots(struct nameidata *nd, int type) + if (follow_dotdot_rcu(nd)) + return -ECHILD; + } else +- follow_dotdot(nd); ++ return follow_dotdot(nd); + } + return 0; + } +@@ -2248,7 +2273,7 @@ mountpoint_last(struct nameidata *nd, struct path *path) + if (unlikely(nd->last_type != LAST_NORM)) { + error = handle_dots(nd, nd->last_type); + if (error) +- goto out; ++ return error; + dentry = dget(nd->path.dentry); + goto done; + } +-- +cgit v0.12 + diff --git a/recipes-kernel/linux/linux-qoriq-common.inc b/recipes-kernel/linux/linux-qoriq-common.inc index c5cb4fb..4b3d483 100644 --- a/recipes-kernel/linux/linux-qoriq-common.inc +++ b/recipes-kernel/linux/linux-qoriq-common.inc @@ -1,4 +1,5 @@ FILESEXTRAPATHS_prepend := "${THISDIR}/files:" +FILESEXTRAPATHS_prepend := "${THISDIR}/linux-qoriq-3.12:" SRC_URI += "file://b4860-hard_irq_disable-bug.patch \ file://0001-sdhci-fix-Timeout-error-messages.patch \ @@ -15,6 +16,8 @@ SRC_URI += "file://b4860-hard_irq_disable-bug.patch \ file://net-sctp-CVE-2015-1421.patch \ file://usb-whiteheat-CVE-2015-5257.patch \ file://security-keys-CVE-2016-0728.patch \ + file://vfs-CVE-2015-2925.patch \ + file://dcache-CVE-2015-2925.patch \ " SRC_URI += "file://cfg/00013-localversion.cfg \ diff --git a/recipes-kernel/linux/linux-yocto-3.14/dcache-CVE-2015-2925.patch b/recipes-kernel/linux/linux-yocto-3.14/dcache-CVE-2015-2925.patch new file mode 100644 index 0000000..84b72ba --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-3.14/dcache-CVE-2015-2925.patch @@ -0,0 +1,69 @@ +From cb1320693b9d8d32651a2bb7cd15498408732b8f Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Sat, 15 Aug 2015 13:36:12 -0500 +Subject: dcache: Handle escaped paths in prepend_path + +commit cde93be45a8a90d8c264c776fab63487b5038a65 upstream. + +A rename can result in a dentry that by walking up d_parent +will never reach it's mnt_root. For lack of a better term +I call this an escaped path. + +prepend_path is called by four different functions __d_path, +d_absolute_path, d_path, and getcwd. + +__d_path only wants to see paths are connected to the root it passes +in. So __d_path needs prepend_path to return an error. + +d_absolute_path similarly wants to see paths that are connected to +some root. Escaped paths are not connected to any mnt_root so +d_absolute_path needs prepend_path to return an error greater +than 1. So escaped paths will be treated like paths on lazily +unmounted mounts. + +getcwd needs to prepend "(unreachable)" so getcwd also needs +prepend_path to return an error. + +d_path is the interesting hold out. d_path just wants to print +something, and does not care about the weird cases. Which raises +the question what should be printed? + +Given that / should result in -ENOENT I +believe it is desirable for escaped paths to be printed as empty +paths. As there are not really any meaninful path components when +considered from the perspective of a mount tree. + +So tweak prepend_path to return an empty path with an new error +code of 3 when it encounters an escaped path. + +Fixes CVE-2015-2925. +Upstream-Status: Backport + +Signed-off-by: "Eric W. Biederman" +Signed-off-by: Al Viro +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sona Sarmadi +--- + fs/dcache.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/fs/dcache.c b/fs/dcache.c +index df323f8..65ccdf0 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -2787,6 +2787,13 @@ restart: + + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { + struct mount *parent = ACCESS_ONCE(mnt->mnt_parent); ++ /* Escaped? */ ++ if (dentry != vfsmnt->mnt_root) { ++ bptr = *buffer; ++ blen = *buflen; ++ error = 3; ++ break; ++ } + /* Global root? */ + if (mnt != parent) { + dentry = ACCESS_ONCE(mnt->mnt_mountpoint); +-- +cgit v0.12 diff --git a/recipes-kernel/linux/linux-yocto-3.14/vfs-CVE-2015-2925.patch b/recipes-kernel/linux/linux-yocto-3.14/vfs-CVE-2015-2925.patch new file mode 100644 index 0000000..c7fa8bf --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-3.14/vfs-CVE-2015-2925.patch @@ -0,0 +1,126 @@ +From 27fa5a5ee0e2b2556ce59c0ad04f606635fa6392 Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Sat, 15 Aug 2015 20:27:13 -0500 +Subject: vfs: Test for and handle paths that are unreachable from their + mnt_root + +commit 397d425dc26da728396e66d392d5dcb8dac30c37 upstream. + +In rare cases a directory can be renamed out from under a bind mount. +In those cases without special handling it becomes possible to walk up +the directory tree to the root dentry of the filesystem and down +from the root dentry to every other file or directory on the filesystem. + +Like division by zero .. from an unconnected path can not be given +a useful semantic as there is no predicting at which path component +the code will realize it is unconnected. We certainly can not match +the current behavior as the current behavior is a security hole. + +Therefore when encounting .. when following an unconnected path +return -ENOENT. + +- Add a function path_connected to verify path->dentry is reachable + from path->mnt.mnt_root. AKA to validate that rename did not do + something nasty to the bind mount. + + To avoid races path_connected must be called after following a path + component to it's next path component. + +Fixes CVE-2015-2925. +Upstream-Status: Backport + +Signed-off-by: "Eric W. Biederman" +Signed-off-by: Al Viro +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sona Sarmadi +--- + fs/namei.c | 31 ++++++++++++++++++++++++++++--- + 1 file changed, 28 insertions(+), 3 deletions(-) + +diff --git a/fs/namei.c b/fs/namei.c +index c6fa079..f4f6460 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -484,6 +484,24 @@ void path_put(const struct path *path) + } + EXPORT_SYMBOL(path_put); + ++/** ++ * path_connected - Verify that a path->dentry is below path->mnt.mnt_root ++ * @path: nameidate to verify ++ * ++ * Rename can sometimes move a file or directory outside of a bind ++ * mount, path_connected allows those cases to be detected. ++ */ ++static bool path_connected(const struct path *path) ++{ ++ struct vfsmount *mnt = path->mnt; ++ ++ /* Only bind mounts can have disconnected paths */ ++ if (mnt->mnt_root == mnt->mnt_sb->s_root) ++ return true; ++ ++ return is_subdir(path->dentry, mnt->mnt_root); ++} ++ + /* + * Path walking has 2 modes, rcu-walk and ref-walk (see + * Documentation/filesystems/path-lookup.txt). In situations when we can't +@@ -1149,6 +1167,8 @@ static int follow_dotdot_rcu(struct nameidata *nd) + goto failed; + nd->path.dentry = parent; + nd->seq = seq; ++ if (unlikely(!path_connected(&nd->path))) ++ goto failed; + break; + } + if (!follow_up_rcu(&nd->path)) +@@ -1242,7 +1262,7 @@ static void follow_mount(struct path *path) + } + } + +-static void follow_dotdot(struct nameidata *nd) ++static int follow_dotdot(struct nameidata *nd) + { + if (!nd->root.mnt) + set_root(nd); +@@ -1258,6 +1278,10 @@ static void follow_dotdot(struct nameidata *nd) + /* rare case of legitimate dget_parent()... */ + nd->path.dentry = dget_parent(nd->path.dentry); + dput(old); ++ if (unlikely(!path_connected(&nd->path))) { ++ path_put(&nd->path); ++ return -ENOENT; ++ } + break; + } + if (!follow_up(&nd->path)) +@@ -1265,6 +1289,7 @@ static void follow_dotdot(struct nameidata *nd) + } + follow_mount(&nd->path); + nd->inode = nd->path.dentry->d_inode; ++ return 0; + } + + /* +@@ -1488,7 +1513,7 @@ static inline int handle_dots(struct nameidata *nd, int type) + if (follow_dotdot_rcu(nd)) + return -ECHILD; + } else +- follow_dotdot(nd); ++ return follow_dotdot(nd); + } + return 0; + } +@@ -2214,7 +2239,7 @@ mountpoint_last(struct nameidata *nd, struct path *path) + if (unlikely(nd->last_type != LAST_NORM)) { + error = handle_dots(nd, nd->last_type); + if (error) +- goto out; ++ return error; + dentry = dget(nd->path.dentry); + goto done; + } +-- +cgit v0.12 + diff --git a/recipes-kernel/linux/linux-yocto_3.14.bbappend b/recipes-kernel/linux/linux-yocto_3.14.bbappend index 4b9e525..fab7fc4 100644 --- a/recipes-kernel/linux/linux-yocto_3.14.bbappend +++ b/recipes-kernel/linux/linux-yocto_3.14.bbappend @@ -1,4 +1,5 @@ FILESEXTRAPATHS_prepend := "${THISDIR}/files:" +FILESEXTRAPATHS_prepend := "${THISDIR}/linux-yocto-3.14:" SRC_URI += "file://HID_CVE_patches/0005-HID-steelseries-validate-output-report-details.patch \ file://splice-CVE-2014-7822-3.14-kernel.patch \ @@ -20,4 +21,6 @@ SRC_URI += "file://HID_CVE_patches/0005-HID-steelseries-validate-output-report-d file://ipv4-CVE-2015-3636.patch \ file://udf-CVE-2015-4167.patch \ file://usb-whiteheat-CVE-2015-5257.patch \ + file://vfs-CVE-2015-2925.patch \ + file://dcache-CVE-2015-2925.patch \ " -- cgit v1.2.3-54-g00ecf