Patch "cachefiles: never get a new anonymous fd if ondemand_id is valid" has been added to the 6.6-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    cachefiles: never get a new anonymous fd if ondemand_id is valid

to the 6.6-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     cachefiles-never-get-a-new-anonymous-fd-if-ondemand_.patch
and it can be found in the queue-6.6 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 6da64f507940363f282ec190a807edde15abb9ac
Author: Baokun Li <libaokun1@xxxxxxxxxx>
Date:   Wed May 22 19:43:04 2024 +0800

    cachefiles: never get a new anonymous fd if ondemand_id is valid
    
    [ Upstream commit 4988e35e95fc938bdde0e15880fe72042fc86acf ]
    
    Now every time the daemon reads an open request, it gets a new anonymous fd
    and ondemand_id. With the introduction of "restore", it is possible to read
    the same open request more than once, and therefore an object can have more
    than one anonymous fd.
    
    If the anonymous fd is not unique, the following concurrencies will result
    in an fd leak:
    
         t1     |         t2         |          t3
    ------------------------------------------------------------
     cachefiles_ondemand_init_object
      cachefiles_ondemand_send_req
       REQ_A = kzalloc(sizeof(*req) + data_len)
       wait_for_completion(&REQ_A->done)
                cachefiles_daemon_read
                 cachefiles_ondemand_daemon_read
                  REQ_A = cachefiles_ondemand_select_req
                  cachefiles_ondemand_get_fd
                    load->fd = fd0
                    ondemand_id = object_id0
                                      ------ restore ------
                                      cachefiles_ondemand_restore
                                       // restore REQ_A
                                      cachefiles_daemon_read
                                       cachefiles_ondemand_daemon_read
                                        REQ_A = cachefiles_ondemand_select_req
                                          cachefiles_ondemand_get_fd
                                            load->fd = fd1
                                            ondemand_id = object_id1
                 process_open_req(REQ_A)
                 write(devfd, ("copen %u,%llu", msg->msg_id, size))
                 cachefiles_ondemand_copen
                  xa_erase(&cache->reqs, id)
                  complete(&REQ_A->done)
       kfree(REQ_A)
                                      process_open_req(REQ_A)
                                      // copen fails due to no req
                                      // daemon close(fd1)
                                      cachefiles_ondemand_fd_release
                                       // set object closed
     -- umount --
     cachefiles_withdraw_cookie
      cachefiles_ondemand_clean_object
       cachefiles_ondemand_init_close_req
        if (!cachefiles_ondemand_object_is_open(object))
          return -ENOENT;
        // The fd0 is not closed until the daemon exits.
    
    However, the anonymous fd holds the reference count of the object and the
    object holds the reference count of the cookie. So even though the cookie
    has been relinquished, it will not be unhashed and freed until the daemon
    exits.
    
    In fscache_hash_cookie(), when the same cookie is found in the hash list,
    if the cookie is set with the FSCACHE_COOKIE_RELINQUISHED bit, then the new
    cookie waits for the old cookie to be unhashed, while the old cookie is
    waiting for the leaked fd to be closed, if the daemon does not exit in time
    it will trigger a hung task.
    
    To avoid this, allocate a new anonymous fd only if no anonymous fd has
    been allocated (ondemand_id == 0) or if the previously allocated anonymous
    fd has been closed (ondemand_id == -1). Moreover, returns an error if
    ondemand_id is valid, letting the daemon know that the current userland
    restore logic is abnormal and needs to be checked.
    
    Fixes: c8383054506c ("cachefiles: notify the user daemon when looking up cookie")
    Signed-off-by: Baokun Li <libaokun1@xxxxxxxxxx>
    Link: https://lore.kernel.org/r/20240522114308.2402121-9-libaokun@xxxxxxxxxxxxxxx
    Acked-by: Jeff Layton <jlayton@xxxxxxxxxx>
    Signed-off-by: Christian Brauner <brauner@xxxxxxxxxx>
    Stable-dep-of: 4b4391e77a6b ("cachefiles: defer exposing anon_fd until after copy_to_user() succeeds")
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/fs/cachefiles/ondemand.c b/fs/cachefiles/ondemand.c
index 99b4bffad4a4f..773c3b407a33b 100644
--- a/fs/cachefiles/ondemand.c
+++ b/fs/cachefiles/ondemand.c
@@ -14,11 +14,18 @@ static int cachefiles_ondemand_fd_release(struct inode *inode,
 					  struct file *file)
 {
 	struct cachefiles_object *object = file->private_data;
-	struct cachefiles_cache *cache = object->volume->cache;
-	struct cachefiles_ondemand_info *info = object->ondemand;
+	struct cachefiles_cache *cache;
+	struct cachefiles_ondemand_info *info;
 	int object_id;
 	struct cachefiles_req *req;
-	XA_STATE(xas, &cache->reqs, 0);
+	XA_STATE(xas, NULL, 0);
+
+	if (!object)
+		return 0;
+
+	info = object->ondemand;
+	cache = object->volume->cache;
+	xas.xa = &cache->reqs;
 
 	xa_lock(&cache->reqs);
 	spin_lock(&info->lock);
@@ -275,22 +282,39 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req)
 		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;
+		ret = -EEXIST;
+		goto err_put_file;
+	}
+
 	file->f_mode |= FMODE_PWRITE | FMODE_LSEEK;
 	fd_install(fd, file);
 
 	load = (void *)req->msg.data;
 	load->fd = fd;
 	object->ondemand->ondemand_id = object_id;
+	spin_unlock(&object->ondemand->lock);
 
 	cachefiles_get_unbind_pincount(cache);
 	trace_cachefiles_ondemand_open(object, &req->msg, load);
 	return 0;
 
+err_put_file:
+	fput(file);
 err_put_fd:
 	put_unused_fd(fd);
 err_free_id:
 	xa_erase(&cache->ondemand_ids, object_id);
 err:
+	spin_lock(&object->ondemand->lock);
+	/* Avoid marking an opened object as closed. */
+	if (object->ondemand->ondemand_id <= 0)
+		cachefiles_ondemand_set_object_close(object);
+	spin_unlock(&object->ondemand->lock);
 	cachefiles_put_object(object, cachefiles_obj_put_ondemand_fd);
 	return ret;
 }
@@ -373,10 +397,8 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
 
 	if (msg->opcode == CACHEFILES_OP_OPEN) {
 		ret = cachefiles_ondemand_get_fd(req);
-		if (ret) {
-			cachefiles_ondemand_set_object_close(req->object);
+		if (ret)
 			goto out;
-		}
 	}
 
 	msg->msg_id = xas.xa_index;




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux