From f846a18b030d4bccbb7a2d1fb7359df6c6c69048 Mon Sep 17 00:00:00 2001 From: Sona Sarmadi Date: Wed, 27 Jan 2016 10:45:52 +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 --- .../dcache-CVE-2015-2925.patch | 70 ++++++++++++ .../linux-hierofalcon-3.19/vfs-CVE-2015-2925.patch | 126 +++++++++++++++++++++ .../dcache-CVE-2015-2925.patch | 70 ++++++++++++ .../linux-hierofalcon-4.1/vfs-CVE-2015-2925.patch | 126 +++++++++++++++++++++ recipes-kernel/linux/linux-hierofalcon_3.19.bb | 2 + recipes-kernel/linux/linux-hierofalcon_4.1.bb | 2 + 6 files changed, 396 insertions(+) create mode 100644 recipes-kernel/linux/linux-hierofalcon-3.19/dcache-CVE-2015-2925.patch create mode 100644 recipes-kernel/linux/linux-hierofalcon-3.19/vfs-CVE-2015-2925.patch create mode 100644 recipes-kernel/linux/linux-hierofalcon-4.1/dcache-CVE-2015-2925.patch create mode 100644 recipes-kernel/linux/linux-hierofalcon-4.1/vfs-CVE-2015-2925.patch diff --git a/recipes-kernel/linux/linux-hierofalcon-3.19/dcache-CVE-2015-2925.patch b/recipes-kernel/linux/linux-hierofalcon-3.19/dcache-CVE-2015-2925.patch new file mode 100644 index 0000000..264b27d --- /dev/null +++ b/recipes-kernel/linux/linux-hierofalcon-3.19/dcache-CVE-2015-2925.patch @@ -0,0 +1,70 @@ +From 27f5c615afb5303eb902a1f2535903e0fd1d7517 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 + +[ Upstream commit cde93be45a8a90d8c264c776fab63487b5038a65 ] + +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: Sasha Levin +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 a66d6d8..28ee20a 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -2889,6 +2889,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-hierofalcon-3.19/vfs-CVE-2015-2925.patch b/recipes-kernel/linux/linux-hierofalcon-3.19/vfs-CVE-2015-2925.patch new file mode 100644 index 0000000..ed930b7 --- /dev/null +++ b/recipes-kernel/linux/linux-hierofalcon-3.19/vfs-CVE-2015-2925.patch @@ -0,0 +1,126 @@ +From 7f61fd99a60195578215f46ed870b5c118cfbfc0 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 + +[ Upstream commit 397d425dc26da728396e66d392d5dcb8dac30c37 ] + +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: Sasha Levin +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 d20f061..be3d538 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -487,6 +487,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 +@@ -1164,6 +1182,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)) +@@ -1260,7 +1280,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); +@@ -1276,6 +1296,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)) +@@ -1283,6 +1307,7 @@ static void follow_dotdot(struct nameidata *nd) + } + follow_mount(&nd->path); + nd->inode = nd->path.dentry->d_inode; ++ return 0; + } + + /* +@@ -1503,7 +1528,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; + } +@@ -2239,7 +2264,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-hierofalcon-4.1/dcache-CVE-2015-2925.patch b/recipes-kernel/linux/linux-hierofalcon-4.1/dcache-CVE-2015-2925.patch new file mode 100644 index 0000000..a6a8449 --- /dev/null +++ b/recipes-kernel/linux/linux-hierofalcon-4.1/dcache-CVE-2015-2925.patch @@ -0,0 +1,70 @@ +From 6f4e45e35c02fd23589a62aab0dc84286cc1302c 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 5d03eb0..2e8ddc1 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -2923,6 +2923,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-hierofalcon-4.1/vfs-CVE-2015-2925.patch b/recipes-kernel/linux/linux-hierofalcon-4.1/vfs-CVE-2015-2925.patch new file mode 100644 index 0000000..aa20d7f --- /dev/null +++ b/recipes-kernel/linux/linux-hierofalcon-4.1/vfs-CVE-2015-2925.patch @@ -0,0 +1,126 @@ +From eed13ce27f481cba65352e73403ca15e67bc26a4 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 fe30d3b..acdab61 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -505,6 +505,24 @@ struct nameidata { + char *saved_names[MAX_NESTED_LINKS + 1]; + }; + ++/** ++ * 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 +@@ -1194,6 +1212,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)) +@@ -1290,7 +1310,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); +@@ -1306,6 +1326,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)) +@@ -1313,6 +1337,7 @@ static void follow_dotdot(struct nameidata *nd) + } + follow_mount(&nd->path); + nd->inode = nd->path.dentry->d_inode; ++ return 0; + } + + /* +@@ -1541,7 +1566,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; + } +@@ -2290,7 +2315,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-hierofalcon_3.19.bb b/recipes-kernel/linux/linux-hierofalcon_3.19.bb index 6e77066..7f4720a 100644 --- a/recipes-kernel/linux/linux-hierofalcon_3.19.bb +++ b/recipes-kernel/linux/linux-hierofalcon_3.19.bb @@ -29,6 +29,8 @@ SRC_URI = "git://git.yoctoproject.org/linux-yocto-3.19;branch="standard/qemuarm6 file://ipv4-CVE-2015-3636.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 \ " S = "${WORKDIR}/git" diff --git a/recipes-kernel/linux/linux-hierofalcon_4.1.bb b/recipes-kernel/linux/linux-hierofalcon_4.1.bb index 61b1dae..4fafe34 100644 --- a/recipes-kernel/linux/linux-hierofalcon_4.1.bb +++ b/recipes-kernel/linux/linux-hierofalcon_4.1.bb @@ -28,6 +28,8 @@ SRC_URI = "git://git.yoctoproject.org/linux-yocto-4.1;branch="standard/qemuarm64 file://vhost-CVE-2015-6252.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 \ " S = "${WORKDIR}/git" -- cgit v1.2.3-54-g00ecf