CVE-2015-2925 Don't allow bind mount escape (rhbz 1209367 1209373)
Also fixup some patch stuff from the 4.2 rebase
This commit is contained in:
parent
774ec9b607
commit
a95bfb8427
|
@ -0,0 +1,65 @@
|
|||
From c0ea161a6e7158281f64bc6d41126da43cb08f14 Mon Sep 17 00:00:00 2001
|
||||
From: "Eric W. Biederman" <ebiederm@xmission.com>
|
||||
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 <escaped_path>/<anything> 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" <ebiederm@xmission.com>
|
||||
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
||||
---
|
||||
fs/dcache.c | 7 +++++++
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
diff --git a/fs/dcache.c b/fs/dcache.c
|
||||
index 9b5fe503f6cb..e3b44ca75a1b 100644
|
||||
--- a/fs/dcache.c
|
||||
+++ b/fs/dcache.c
|
||||
@@ -2926,6 +2926,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
|
||||
|
23
kernel.spec
23
kernel.spec
|
@ -610,9 +610,6 @@ Patch509: ideapad-laptop-Add-Lenovo-Yoga-3-14-to-no_hw_rfkill-.patch
|
|||
#rhbz 1253789
|
||||
Patch511: iSCSI-let-session-recovery_tmo-sysfs-writes-persist.patch
|
||||
|
||||
#rhbz 1256281
|
||||
Patch26265: mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch
|
||||
|
||||
#rhbz 1257534
|
||||
Patch515: nv46-Change-mc-subdev-oclass-from-nv44-to-nv4c.patch
|
||||
|
||||
|
@ -632,6 +629,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
|
||||
|
@ -1359,12 +1360,6 @@ ApplyPatch ideapad-laptop-Add-Lenovo-Yoga-3-14-to-no_hw_rfkill-.patch
|
|||
#rhbz 1253789
|
||||
ApplyPatch iSCSI-let-session-recovery_tmo-sysfs-writes-persist.patch
|
||||
|
||||
#rhbz 1250717
|
||||
ApplyPatch ext4-dont-manipulate-recovery-flag-when-freezing.patch
|
||||
|
||||
#rhbz 1256281
|
||||
ApplyPatch mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch
|
||||
|
||||
#rhbz 1257534
|
||||
ApplyPatch nv46-Change-mc-subdev-oclass-from-nv44-to-nv4c.patch
|
||||
|
||||
|
@ -1372,9 +1367,6 @@ ApplyPatch nv46-Change-mc-subdev-oclass-from-nv44-to-nv4c.patch
|
|||
ApplyPatch vmwgfx-Rework-device-initialization.patch
|
||||
ApplyPatch drm-vmwgfx-Allow-dropped-masters-render-node-like-ac.patch
|
||||
|
||||
#rhbz 1259231
|
||||
ApplyPatch make-flush-workqueue-available-to-non-GPL-modules.patch
|
||||
|
||||
#rhbz 1237136
|
||||
ApplyPatch block-blkg_destroy_all-should-clear-q-root_blkg-and-.patch
|
||||
|
||||
|
@ -1389,6 +1381,10 @@ ApplyPatch USB-whiteheat-fix-potential-null-deref-at-probe.patch
|
|||
|
||||
ApplyPatch regulator-axp20x-module-alias.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
|
||||
|
@ -2239,6 +2235,9 @@ fi
|
|||
#
|
||||
#
|
||||
%changelog
|
||||
* Thu Oct 01 2015 Josh Boyer <jwboyer@fedoraproject.org>
|
||||
- CVE-2015-2925 Don't allow bind mount escape (rhbz 1209367 1209373)
|
||||
|
||||
* Tue Sep 29 2015 Justin M. Forbes <jforbes@fedoraproject.org>
|
||||
- - Linux v4.2.2
|
||||
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
From: Haibo Chen <haibo.chen@freescale.com>
|
||||
Date: Tue, 25 Aug 2015 10:02:11 +0800
|
||||
Subject: [PATCH] mmc: sdhci: fix dma memory leak in sdhci_pre_req()
|
||||
|
||||
Currently one mrq->data maybe execute dma_map_sg() twice
|
||||
when mmc subsystem prepare over one new request, and the
|
||||
following log show up:
|
||||
sdhci[sdhci_pre_dma_transfer] invalid cookie: 24, next-cookie 25
|
||||
|
||||
In this condition, mrq->date map a dma-memory(1) in sdhci_pre_req
|
||||
for the first time, and map another dma-memory(2) in sdhci_prepare_data
|
||||
for the second time. But driver only unmap the dma-memory(2), and
|
||||
dma-memory(1) never unmapped, which cause the dma memory leak issue.
|
||||
|
||||
This patch use another method to map the dma memory for the mrq->data
|
||||
which can fix this dma memory leak issue.
|
||||
|
||||
Fixes: commit 348487cb28e66b0 ("mmc: sdhci: use pipeline mmc requests to improve performance")
|
||||
Cc: stable@vger.kernel.org # 4.0+
|
||||
Reported-and-tested-by: Jiri Slaby <jslaby@suse.cz>
|
||||
Signed-off-by: Haibo Chen <haibo.chen@freescale.com>
|
||||
---
|
||||
drivers/mmc/host/sdhci.c | 67 ++++++++++++++++++------------------------------
|
||||
drivers/mmc/host/sdhci.h | 8 +++---
|
||||
2 files changed, 29 insertions(+), 46 deletions(-)
|
||||
|
||||
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
|
||||
index 1dbe932..ea10ebc 100644
|
||||
--- a/drivers/mmc/host/sdhci.c
|
||||
+++ b/drivers/mmc/host/sdhci.c
|
||||
@@ -54,8 +54,7 @@ static void sdhci_finish_command(struct sdhci_host *);
|
||||
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
||||
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
||||
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
||||
- struct mmc_data *data,
|
||||
- struct sdhci_host_next *next);
|
||||
+ struct mmc_data *data);
|
||||
static int sdhci_do_get_cd(struct sdhci_host *host);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@@ -496,7 +495,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
||||
goto fail;
|
||||
BUG_ON(host->align_addr & host->align_mask);
|
||||
|
||||
- host->sg_count = sdhci_pre_dma_transfer(host, data, NULL);
|
||||
+ host->sg_count = sdhci_pre_dma_transfer(host, data);
|
||||
if (host->sg_count < 0)
|
||||
goto unmap_align;
|
||||
|
||||
@@ -635,9 +634,11 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
|
||||
}
|
||||
}
|
||||
|
||||
- if (!data->host_cookie)
|
||||
+ if (data->host_cookie == COOKIE_MAPPED) {
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len, direction);
|
||||
+ data->host_cookie = COOKIE_UNMAPPED;
|
||||
+ }
|
||||
}
|
||||
|
||||
static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
@@ -833,7 +834,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
} else {
|
||||
int sg_cnt;
|
||||
|
||||
- sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
|
||||
+ sg_cnt = sdhci_pre_dma_transfer(host, data);
|
||||
if (sg_cnt <= 0) {
|
||||
/*
|
||||
* This only happens when someone fed
|
||||
@@ -949,11 +950,13 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
||||
if (host->flags & SDHCI_USE_ADMA)
|
||||
sdhci_adma_table_post(host, data);
|
||||
else {
|
||||
- if (!data->host_cookie)
|
||||
+ if (data->host_cookie == COOKIE_MAPPED) {
|
||||
dma_unmap_sg(mmc_dev(host->mmc),
|
||||
data->sg, data->sg_len,
|
||||
(data->flags & MMC_DATA_READ) ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
+ data->host_cookie = COOKIE_UNMAPPED;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2097,49 +2100,36 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||
struct mmc_data *data = mrq->data;
|
||||
|
||||
if (host->flags & SDHCI_REQ_USE_DMA) {
|
||||
- if (data->host_cookie)
|
||||
+ if (data->host_cookie == COOKIE_GIVEN ||
|
||||
+ data->host_cookie == COOKIE_MAPPED)
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
data->flags & MMC_DATA_WRITE ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
- mrq->data->host_cookie = 0;
|
||||
+ data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
}
|
||||
|
||||
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
||||
- struct mmc_data *data,
|
||||
- struct sdhci_host_next *next)
|
||||
+ struct mmc_data *data)
|
||||
{
|
||||
int sg_count;
|
||||
|
||||
- if (!next && data->host_cookie &&
|
||||
- data->host_cookie != host->next_data.cookie) {
|
||||
- pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n",
|
||||
- __func__, data->host_cookie, host->next_data.cookie);
|
||||
- data->host_cookie = 0;
|
||||
+ if (data->host_cookie == COOKIE_MAPPED) {
|
||||
+ data->host_cookie = COOKIE_GIVEN;
|
||||
+ return data->sg_count;
|
||||
}
|
||||
|
||||
- /* Check if next job is already prepared */
|
||||
- if (next ||
|
||||
- (!next && data->host_cookie != host->next_data.cookie)) {
|
||||
- sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
||||
- data->sg_len,
|
||||
- data->flags & MMC_DATA_WRITE ?
|
||||
- DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
-
|
||||
- } else {
|
||||
- sg_count = host->next_data.sg_count;
|
||||
- host->next_data.sg_count = 0;
|
||||
- }
|
||||
+ WARN_ON(data->host_cookie == COOKIE_GIVEN);
|
||||
|
||||
+ sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
+ data->flags & MMC_DATA_WRITE ?
|
||||
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
if (sg_count == 0)
|
||||
- return -EINVAL;
|
||||
+ return -ENOSPC;
|
||||
|
||||
- if (next) {
|
||||
- next->sg_count = sg_count;
|
||||
- data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
|
||||
- } else
|
||||
- host->sg_count = sg_count;
|
||||
+ data->sg_count = sg_count;
|
||||
+ data->host_cookie = COOKIE_MAPPED;
|
||||
|
||||
return sg_count;
|
||||
}
|
||||
@@ -2149,16 +2139,10 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
- if (mrq->data->host_cookie) {
|
||||
- mrq->data->host_cookie = 0;
|
||||
- return;
|
||||
- }
|
||||
+ mrq->data->host_cookie = COOKIE_UNMAPPED;
|
||||
|
||||
if (host->flags & SDHCI_REQ_USE_DMA)
|
||||
- if (sdhci_pre_dma_transfer(host,
|
||||
- mrq->data,
|
||||
- &host->next_data) < 0)
|
||||
- mrq->data->host_cookie = 0;
|
||||
+ sdhci_pre_dma_transfer(host, mrq->data);
|
||||
}
|
||||
|
||||
static void sdhci_card_event(struct mmc_host *mmc)
|
||||
@@ -3030,7 +3014,6 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
host->max_clk = host->ops->get_max_clock(host);
|
||||
}
|
||||
|
||||
- host->next_data.cookie = 1;
|
||||
/*
|
||||
* In case of Host Controller v3.00, find out whether clock
|
||||
* multiplier is supported.
|
||||
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
|
||||
index 5521d29..a9512a4 100644
|
||||
--- a/drivers/mmc/host/sdhci.h
|
||||
+++ b/drivers/mmc/host/sdhci.h
|
||||
@@ -309,9 +309,10 @@ struct sdhci_adma2_64_desc {
|
||||
*/
|
||||
#define SDHCI_MAX_SEGS 128
|
||||
|
||||
-struct sdhci_host_next {
|
||||
- unsigned int sg_count;
|
||||
- s32 cookie;
|
||||
+enum sdhci_cookie {
|
||||
+ COOKIE_UNMAPPED,
|
||||
+ COOKIE_MAPPED,
|
||||
+ COOKIE_GIVEN,
|
||||
};
|
||||
|
||||
struct sdhci_host {
|
||||
@@ -503,7 +504,6 @@ struct sdhci_host {
|
||||
unsigned int tuning_mode; /* Re-tuning mode supported by host */
|
||||
#define SDHCI_TUNING_MODE_1 0
|
||||
|
||||
- struct sdhci_host_next next_data;
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
From 14588dfe2e411056df5ba85ef88ad51730a2fa0a Mon Sep 17 00:00:00 2001
|
||||
From: "Eric W. Biederman" <ebiederm@xmission.com>
|
||||
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" <ebiederm@xmission.com>
|
||||
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
||||
---
|
||||
fs/namei.c | 27 +++++++++++++++++++++++++--
|
||||
1 file changed, 25 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/fs/namei.c b/fs/namei.c
|
||||
index 1c2105ed20c5..29b927938b8c 100644
|
||||
--- a/fs/namei.c
|
||||
+++ b/fs/namei.c
|
||||
@@ -560,6 +560,24 @@ static int __nd_alloc_stack(struct nameidata *nd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * 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);
|
||||
+}
|
||||
+
|
||||
static inline int nd_alloc_stack(struct nameidata *nd)
|
||||
{
|
||||
if (likely(nd->depth != EMBEDDED_LEVELS))
|
||||
@@ -1296,6 +1314,8 @@ static int follow_dotdot_rcu(struct nameidata *nd)
|
||||
return -ECHILD;
|
||||
nd->path.dentry = parent;
|
||||
nd->seq = seq;
|
||||
+ if (unlikely(!path_connected(&nd->path)))
|
||||
+ return -ENOENT;
|
||||
break;
|
||||
} else {
|
||||
struct mount *mnt = real_mount(nd->path.mnt);
|
||||
@@ -1396,7 +1416,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);
|
||||
@@ -1412,6 +1432,8 @@ 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)))
|
||||
+ return -ENOENT;
|
||||
break;
|
||||
}
|
||||
if (!follow_up(&nd->path))
|
||||
@@ -1419,6 +1441,7 @@ static void follow_dotdot(struct nameidata *nd)
|
||||
}
|
||||
follow_mount(&nd->path);
|
||||
nd->inode = nd->path.dentry->d_inode;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1634,7 +1657,7 @@ static inline int handle_dots(struct nameidata *nd, int type)
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
return follow_dotdot_rcu(nd);
|
||||
} else
|
||||
- follow_dotdot(nd);
|
||||
+ return follow_dotdot(nd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
--
|
||||
2.4.3
|
||||
|
Loading…
Reference in New Issue