Skip to content

Commit 4988e35

Browse files
LiBaokun96brauner
authored andcommitted
cachefiles: never get a new anonymous fd if ondemand_id is valid
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: c838305 ("cachefiles: notify the user daemon when looking up cookie") Signed-off-by: Baokun Li <[email protected]> Link: https://lore.kernel.org/r/[email protected] Acked-by: Jeff Layton <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent 0a79004 commit 4988e35

File tree

1 file changed

+28
-6
lines changed

1 file changed

+28
-6
lines changed

fs/cachefiles/ondemand.c

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,18 @@ static int cachefiles_ondemand_fd_release(struct inode *inode,
1414
struct file *file)
1515
{
1616
struct cachefiles_object *object = file->private_data;
17-
struct cachefiles_cache *cache = object->volume->cache;
18-
struct cachefiles_ondemand_info *info = object->ondemand;
17+
struct cachefiles_cache *cache;
18+
struct cachefiles_ondemand_info *info;
1919
int object_id;
2020
struct cachefiles_req *req;
21-
XA_STATE(xas, &cache->reqs, 0);
21+
XA_STATE(xas, NULL, 0);
22+
23+
if (!object)
24+
return 0;
25+
26+
info = object->ondemand;
27+
cache = object->volume->cache;
28+
xas.xa = &cache->reqs;
2229

2330
xa_lock(&cache->reqs);
2431
spin_lock(&info->lock);
@@ -288,22 +295,39 @@ static int cachefiles_ondemand_get_fd(struct cachefiles_req *req)
288295
goto err_put_fd;
289296
}
290297

298+
spin_lock(&object->ondemand->lock);
299+
if (object->ondemand->ondemand_id > 0) {
300+
spin_unlock(&object->ondemand->lock);
301+
/* Pair with check in cachefiles_ondemand_fd_release(). */
302+
file->private_data = NULL;
303+
ret = -EEXIST;
304+
goto err_put_file;
305+
}
306+
291307
file->f_mode |= FMODE_PWRITE | FMODE_LSEEK;
292308
fd_install(fd, file);
293309

294310
load = (void *)req->msg.data;
295311
load->fd = fd;
296312
object->ondemand->ondemand_id = object_id;
313+
spin_unlock(&object->ondemand->lock);
297314

298315
cachefiles_get_unbind_pincount(cache);
299316
trace_cachefiles_ondemand_open(object, &req->msg, load);
300317
return 0;
301318

319+
err_put_file:
320+
fput(file);
302321
err_put_fd:
303322
put_unused_fd(fd);
304323
err_free_id:
305324
xa_erase(&cache->ondemand_ids, object_id);
306325
err:
326+
spin_lock(&object->ondemand->lock);
327+
/* Avoid marking an opened object as closed. */
328+
if (object->ondemand->ondemand_id <= 0)
329+
cachefiles_ondemand_set_object_close(object);
330+
spin_unlock(&object->ondemand->lock);
307331
cachefiles_put_object(object, cachefiles_obj_put_ondemand_fd);
308332
return ret;
309333
}
@@ -386,10 +410,8 @@ ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
386410

387411
if (msg->opcode == CACHEFILES_OP_OPEN) {
388412
ret = cachefiles_ondemand_get_fd(req);
389-
if (ret) {
390-
cachefiles_ondemand_set_object_close(req->object);
413+
if (ret)
391414
goto out;
392-
}
393415
}
394416

395417
msg->msg_id = xas.xa_index;

0 commit comments

Comments
 (0)