Skip to content

Commit

Permalink
cachefiles: defer exposing anon_fd until after copy_to_user() succeeds
Browse files Browse the repository at this point in the history
After installing the anonymous fd, we can now see it in userland and close
it. However, at this point we may not have gotten the reference count of
the cache, but we will put it during colse fd, so this may cause a cache
UAF.

So grab the cache reference count before fd_install(). In addition, by
kernel convention, fd is taken over by the user land after fd_install(),
and the kernel should not call close_fd() after that, i.e., it should call
fd_install() after everything is ready, thus fd_install() is called after
copy_to_user() succeeds.

Fixes: c838305 ("cachefiles: notify the user daemon when looking up cookie")
Suggested-by: Hou Tao <houtao1@huawei.com>
Signed-off-by: Baokun Li <libaokun1@huawei.com>
Link: https://lore.kernel.org/r/20240522114308.2402121-10-libaokun@huaweicloud.com
Acked-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
  • Loading branch information
LiBaokun96 authored and brauner committed May 29, 2024
1 parent 4988e35 commit 4b4391e
Showing 1 changed file with 33 additions and 20 deletions.
53 changes: 33 additions & 20 deletions fs/cachefiles/ondemand.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
#include <linux/uio.h>
#include "internal.h"

struct ondemand_anon_file {
struct file *file;
int fd;
};

static inline void cachefiles_req_put(struct cachefiles_req *req)
{
if (refcount_dec_and_test(&req->ref))
Expand Down Expand Up @@ -263,14 +268,14 @@ int cachefiles_ondemand_restore(struct cachefiles_cache *cache, char *args)
return 0;
}

static int cachefiles_ondemand_get_fd(struct cachefiles_req *req)
static int cachefiles_ondemand_get_fd(struct cachefiles_req *req,
struct ondemand_anon_file *anon_file)
{
struct cachefiles_object *object;
struct cachefiles_cache *cache;
struct cachefiles_open *load;
struct file *file;
u32 object_id;
int ret, fd;
int ret;

object = cachefiles_grab_object(req->object,
cachefiles_obj_get_ondemand_fd);
Expand All @@ -282,33 +287,32 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req)
if (ret < 0)
goto err;

fd = get_unused_fd_flags(O_WRONLY);
if (fd < 0) {
ret = fd;
anon_file->fd = get_unused_fd_flags(O_WRONLY);
if (anon_file->fd < 0) {
ret = anon_file->fd;
goto err_free_id;
}

file = anon_inode_getfile("[cachefiles]", &cachefiles_ondemand_fd_fops,
object, O_WRONLY);
if (IS_ERR(file)) {
ret = PTR_ERR(file);
anon_file->file = anon_inode_getfile("[cachefiles]",
&cachefiles_ondemand_fd_fops, object, O_WRONLY);
if (IS_ERR(anon_file->file)) {
ret = PTR_ERR(anon_file->file);
goto err_put_fd;
}

spin_lock(&object->ondemand->lock);
if (object->ondemand->ondemand_id > 0) {
spin_unlock(&object->ondemand->lock);
/* Pair with check in cachefiles_ondemand_fd_release(). */
file->private_data = NULL;
anon_file->file->private_data = NULL;
ret = -EEXIST;
goto err_put_file;
}

file->f_mode |= FMODE_PWRITE | FMODE_LSEEK;
fd_install(fd, file);
anon_file->file->f_mode |= FMODE_PWRITE | FMODE_LSEEK;

load = (void *)req->msg.data;
load->fd = fd;
load->fd = anon_file->fd;
object->ondemand->ondemand_id = object_id;
spin_unlock(&object->ondemand->lock);

Expand All @@ -317,9 +321,11 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req)
return 0;

err_put_file:
fput(file);
fput(anon_file->file);
anon_file->file = NULL;
err_put_fd:
put_unused_fd(fd);
put_unused_fd(anon_file->fd);
anon_file->fd = ret;
err_free_id:
xa_erase(&cache->ondemand_ids, object_id);
err:
Expand Down Expand Up @@ -376,6 +382,7 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
struct cachefiles_msg *msg;
size_t n;
int ret = 0;
struct ondemand_anon_file anon_file;
XA_STATE(xas, &cache->reqs, cache->req_id_next);

xa_lock(&cache->reqs);
Expand Down Expand Up @@ -409,18 +416,24 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
xa_unlock(&cache->reqs);

if (msg->opcode == CACHEFILES_OP_OPEN) {
ret = cachefiles_ondemand_get_fd(req);
ret = cachefiles_ondemand_get_fd(req, &anon_file);
if (ret)
goto out;
}

msg->msg_id = xas.xa_index;
msg->object_id = req->object->ondemand->ondemand_id;

if (copy_to_user(_buffer, msg, n) != 0) {
if (copy_to_user(_buffer, msg, n) != 0)
ret = -EFAULT;
if (msg->opcode == CACHEFILES_OP_OPEN)
close_fd(((struct cachefiles_open *)msg->data)->fd);

if (msg->opcode == CACHEFILES_OP_OPEN) {
if (ret < 0) {
fput(anon_file.file);
put_unused_fd(anon_file.fd);
goto out;
}
fd_install(anon_file.fd, anon_file.file);
}
out:
cachefiles_put_object(req->object, cachefiles_obj_put_read_req);
Expand Down

0 comments on commit 4b4391e

Please sign in to comment.