diff --git a/dcache-Handle-escaped-paths-in-prepend_path.patch b/dcache-Handle-escaped-paths-in-prepend_path.patch new file mode 100644 index 0000000..e09e9e4 --- /dev/null +++ b/dcache-Handle-escaped-paths-in-prepend_path.patch @@ -0,0 +1,65 @@ +From 0e9ff3b71d0b2866f8f4ce408043f3f06792aad3 Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Sat, 15 Aug 2015 13:36:12 -0500 +Subject: [PATCH 1/2] 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. + +Signed-off-by: "Eric W. Biederman" +Signed-off-by: Al Viro +--- + fs/dcache.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/fs/dcache.c b/fs/dcache.c +index 5d03eb0ec0ac..2e8ddc1d09e9 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); +-- +2.4.3 + diff --git a/kernel.spec b/kernel.spec index f65bef4..6d0a085 100644 --- a/kernel.spec +++ b/kernel.spec @@ -653,6 +653,10 @@ Patch526: 0001-x86-cpu-cacheinfo-Fix-teardown-path.patch #CVE-2015-5257 rhbz 1265607 1265612 Patch527: USB-whiteheat-fix-potential-null-deref-at-probe.patch +#CVE-2015-2925 rhbz 1209367 1209373 +Patch528: dcache-Handle-escaped-paths-in-prepend_path.patch +Patch529: vfs-Test-for-and-handle-paths-that-are-unreachable-f.patch + # END OF PATCH DEFINITIONS %endif @@ -1424,6 +1428,10 @@ ApplyPatch 0001-x86-cpu-cacheinfo-Fix-teardown-path.patch #CVE-2015-5257 rhbz 1265607 1265612 ApplyPatch USB-whiteheat-fix-potential-null-deref-at-probe.patch +#CVE-2015-2925 rhbz 1209367 1209373 +ApplyPatch dcache-Handle-escaped-paths-in-prepend_path.patch +ApplyPatch vfs-Test-for-and-handle-paths-that-are-unreachable-f.patch + # END OF PATCH APPLICATIONS %endif @@ -2274,6 +2282,9 @@ fi # # %changelog +* Thu Oct 01 2015 Josh Boyer +- CVE-2015-2925 Don't allow bind mount escape (rhbz 1209367 1209373) + * Tue Sep 29 2015 Josh Boyer - 4.1.9-200 - Linux v4.1.9 diff --git a/vfs-Test-for-and-handle-paths-that-are-unreachable-f.patch b/vfs-Test-for-and-handle-paths-that-are-unreachable-f.patch new file mode 100644 index 0000000..e302963 --- /dev/null +++ b/vfs-Test-for-and-handle-paths-that-are-unreachable-f.patch @@ -0,0 +1,121 @@ +From 74038ebc44da6e3f9c918f2525f9111e10c8efc2 Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Sat, 15 Aug 2015 20:27:13 -0500 +Subject: [PATCH 2/2] 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. + +Signed-off-by: "Eric W. Biederman" +Signed-off-by: Al Viro +--- + fs/namei.c | 31 ++++++++++++++++++++++++++++--- + 1 file changed, 28 insertions(+), 3 deletions(-) + +diff --git a/fs/namei.c b/fs/namei.c +index fe30d3be43a8..acdab610521b 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; + } +-- +2.4.3 +