]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
cachefiles: Fix NULL pointer dereference in object->file
authorZizhi Wo <wozizhi@huawei.com>
Thu, 7 Nov 2024 11:06:48 +0000 (19:06 +0800)
committerChristian Brauner <brauner@kernel.org>
Mon, 11 Nov 2024 13:39:38 +0000 (14:39 +0100)
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>
fs/cachefiles/interface.c
fs/cachefiles/ondemand.c

index 35ba2117a6f652c8903e32db08b10824bf537660..3e63cfe15874728653c5592cba288b3c1d661da4 100644 (file)
@@ -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);
 }
 
 /*
index 38ca6dce8ef291e8345c57c92bb6183813a90467..fe3de9ad57bf6d5bb0115baa9a9c2cb61822b17a 100644 (file)
@@ -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,