| From 111dd51700d068df97b7c234ebffd3e398149e6c Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 7 Nov 2024 19:06:48 +0800 |
| Subject: cachefiles: Fix NULL pointer dereference in object->file |
| |
| From: Zizhi Wo <wozizhi@huawei.com> |
| |
| [ Upstream commit 31ad74b20227ce6b40910ff78b1c604e42975cf1 ] |
| |
| At present, the object->file has the NULL pointer dereference problem in |
| ondemand-mode. The root cause is that the allocated fd and object->file |
| lifetime are inconsistent, and the user-space invocation to anon_fd uses |
| object->file. Following is the process that triggers the issue: |
| |
| [write fd] [umount] |
| cachefiles_ondemand_fd_write_iter |
| fscache_cookie_state_machine |
| cachefiles_withdraw_cookie |
| if (!file) return -ENOBUFS |
| cachefiles_clean_up_object |
| cachefiles_unmark_inode_in_use |
| fput(object->file) |
| object->file = NULL |
| // file NULL pointer dereference! |
| __cachefiles_write(..., file, ...) |
| |
| Fix this issue by add an additional reference count to the object->file |
| before write/llseek, and decrement after it finished. |
| |
| Fixes: c8383054506c ("cachefiles: notify the user daemon when looking up cookie") |
| Signed-off-by: Zizhi Wo <wozizhi@huawei.com> |
| Link: https://lore.kernel.org/r/20241107110649.3980193-5-wozizhi@huawei.com |
| Reviewed-by: David Howells <dhowells@redhat.com> |
| Signed-off-by: Christian Brauner <brauner@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/cachefiles/interface.c | 14 ++++++++++---- |
| fs/cachefiles/ondemand.c | 30 ++++++++++++++++++++++++------ |
| 2 files changed, 34 insertions(+), 10 deletions(-) |
| |
| diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c |
| index 35ba2117a6f65..3e63cfe158747 100644 |
| --- a/fs/cachefiles/interface.c |
| +++ b/fs/cachefiles/interface.c |
| @@ -327,6 +327,8 @@ static void cachefiles_commit_object(struct cachefiles_object *object, |
| static void cachefiles_clean_up_object(struct cachefiles_object *object, |
| struct cachefiles_cache *cache) |
| { |
| + struct file *file; |
| + |
| if (test_bit(FSCACHE_COOKIE_RETIRED, &object->cookie->flags)) { |
| if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) { |
| cachefiles_see_object(object, cachefiles_obj_see_clean_delete); |
| @@ -342,10 +344,14 @@ static void cachefiles_clean_up_object(struct cachefiles_object *object, |
| } |
| |
| cachefiles_unmark_inode_in_use(object, object->file); |
| - if (object->file) { |
| - fput(object->file); |
| - object->file = NULL; |
| - } |
| + |
| + spin_lock(&object->lock); |
| + file = object->file; |
| + object->file = NULL; |
| + spin_unlock(&object->lock); |
| + |
| + if (file) |
| + fput(file); |
| } |
| |
| /* |
| diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c |
| index 38ca6dce8ef29..fe3de9ad57bf6 100644 |
| --- a/fs/cachefiles/ondemand.c |
| +++ b/fs/cachefiles/ondemand.c |
| @@ -60,20 +60,26 @@ static ssize_t cachefiles_ondemand_fd_write_iter(struct kiocb *kiocb, |
| { |
| struct cachefiles_object *object = kiocb->ki_filp->private_data; |
| struct cachefiles_cache *cache = object->volume->cache; |
| - struct file *file = object->file; |
| + struct file *file; |
| size_t len = iter->count, aligned_len = len; |
| loff_t pos = kiocb->ki_pos; |
| const struct cred *saved_cred; |
| int ret; |
| |
| - if (!file) |
| + spin_lock(&object->lock); |
| + file = object->file; |
| + if (!file) { |
| + spin_unlock(&object->lock); |
| return -ENOBUFS; |
| + } |
| + get_file(file); |
| + spin_unlock(&object->lock); |
| |
| cachefiles_begin_secure(cache, &saved_cred); |
| ret = __cachefiles_prepare_write(object, file, &pos, &aligned_len, len, true); |
| cachefiles_end_secure(cache, saved_cred); |
| if (ret < 0) |
| - return ret; |
| + goto out; |
| |
| trace_cachefiles_ondemand_fd_write(object, file_inode(file), pos, len); |
| ret = __cachefiles_write(object, file, pos, iter, NULL, NULL); |
| @@ -82,6 +88,8 @@ static ssize_t cachefiles_ondemand_fd_write_iter(struct kiocb *kiocb, |
| kiocb->ki_pos += ret; |
| } |
| |
| +out: |
| + fput(file); |
| return ret; |
| } |
| |
| @@ -89,12 +97,22 @@ static loff_t cachefiles_ondemand_fd_llseek(struct file *filp, loff_t pos, |
| int whence) |
| { |
| struct cachefiles_object *object = filp->private_data; |
| - struct file *file = object->file; |
| + struct file *file; |
| + loff_t ret; |
| |
| - if (!file) |
| + spin_lock(&object->lock); |
| + file = object->file; |
| + if (!file) { |
| + spin_unlock(&object->lock); |
| return -ENOBUFS; |
| + } |
| + get_file(file); |
| + spin_unlock(&object->lock); |
| |
| - return vfs_llseek(file, pos, whence); |
| + ret = vfs_llseek(file, pos, whence); |
| + fput(file); |
| + |
| + return ret; |
| } |
| |
| static long cachefiles_ondemand_fd_ioctl(struct file *filp, unsigned int ioctl, |
| -- |
| 2.43.0 |
| |