[PATCH 4.17 048/336] ceph: fix use-after-free in ceph_statfs()

From: Greg Kroah-Hartman
Date: Wed Aug 01 2018 - 14:19:05 EST


4.17-stable review patch. If anyone has any objections, please let me know.

------------------

From: Luis Henriques <lhenriques@xxxxxxxx>

[ Upstream commit 73fb0949cf246b212ff63d692a0ec88db954bb35 ]

KASAN found an UAF in ceph_statfs. This was a one-off bug but looking at
the code it looks like the monmap access needs to be protected as it can
be modified while we're accessing it. Fix this by protecting the access
with the monc->mutex.

BUG: KASAN: use-after-free in ceph_statfs+0x21d/0x2c0
Read of size 8 at addr ffff88006844f2e0 by task trinity-c5/304

CPU: 0 PID: 304 Comm: trinity-c5 Not tainted 4.17.0-rc6+ #172
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.0.0-prebuilt.qemu-project.org 04/01/2014
Call Trace:
dump_stack+0xa5/0x11b
? show_regs_print_info+0x5/0x5
? kmsg_dump_rewind+0x118/0x118
? ceph_statfs+0x21d/0x2c0
print_address_description+0x73/0x2b0
? ceph_statfs+0x21d/0x2c0
kasan_report+0x243/0x360
ceph_statfs+0x21d/0x2c0
? ceph_umount_begin+0x80/0x80
? kmem_cache_alloc+0xdf/0x1a0
statfs_by_dentry+0x79/0xb0
vfs_statfs+0x28/0x110
user_statfs+0x8c/0xe0
? vfs_statfs+0x110/0x110
? __fdget_raw+0x10/0x10
__se_sys_statfs+0x5d/0xa0
? user_statfs+0xe0/0xe0
? mutex_unlock+0x1d/0x40
? __x64_sys_statfs+0x20/0x30
do_syscall_64+0xee/0x290
? syscall_return_slowpath+0x1c0/0x1c0
? page_fault+0x1e/0x30
? syscall_return_slowpath+0x13c/0x1c0
? prepare_exit_to_usermode+0xdb/0x140
? syscall_trace_enter+0x330/0x330
? __put_user_4+0x1c/0x30
entry_SYSCALL_64_after_hwframe+0x44/0xa9

Allocated by task 130:
__kmalloc+0x124/0x210
ceph_monmap_decode+0x1c1/0x400
dispatch+0x113/0xd20
ceph_con_workfn+0xa7e/0x44e0
process_one_work+0x5f0/0xa30
worker_thread+0x184/0xa70
kthread+0x1a0/0x1c0
ret_from_fork+0x35/0x40

Freed by task 130:
kfree+0xb8/0x210
dispatch+0x15a/0xd20
ceph_con_workfn+0xa7e/0x44e0
process_one_work+0x5f0/0xa30
worker_thread+0x184/0xa70
kthread+0x1a0/0x1c0
ret_from_fork+0x35/0x40

Signed-off-by: Luis Henriques <lhenriques@xxxxxxxx>
Reviewed-by: "Yan, Zheng" <zyan@xxxxxxxxxx>
Signed-off-by: Ilya Dryomov <idryomov@xxxxxxxxx>
Signed-off-by: Sasha Levin <alexander.levin@xxxxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
fs/ceph/super.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)

--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -45,7 +45,7 @@ static void ceph_put_super(struct super_
static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct ceph_fs_client *fsc = ceph_inode_to_client(d_inode(dentry));
- struct ceph_monmap *monmap = fsc->client->monc.monmap;
+ struct ceph_mon_client *monc = &fsc->client->monc;
struct ceph_statfs st;
u64 fsid;
int err;
@@ -58,7 +58,7 @@ static int ceph_statfs(struct dentry *de
}

dout("statfs\n");
- err = ceph_monc_do_statfs(&fsc->client->monc, data_pool, &st);
+ err = ceph_monc_do_statfs(monc, data_pool, &st);
if (err < 0)
return err;

@@ -94,8 +94,11 @@ static int ceph_statfs(struct dentry *de
buf->f_namelen = NAME_MAX;

/* Must convert the fsid, for consistent values across arches */
- fsid = le64_to_cpu(*(__le64 *)(&monmap->fsid)) ^
- le64_to_cpu(*((__le64 *)&monmap->fsid + 1));
+ mutex_lock(&monc->mutex);
+ fsid = le64_to_cpu(*(__le64 *)(&monc->monmap->fsid)) ^
+ le64_to_cpu(*((__le64 *)&monc->monmap->fsid + 1));
+ mutex_unlock(&monc->mutex);
+
buf->f_fsid.val[0] = fsid & 0xffffffff;
buf->f_fsid.val[1] = fsid >> 32;