Skip to content

Commit 19f4f39

Browse files
LiBaokun96brauner
authored andcommitted
cachefiles: cyclic allocation of msg_id to avoid reuse
Reusing the msg_id after a maliciously completed reopen request may cause a read request to remain unprocessed and result in a hung, as shown below: t1 | t2 | t3 ------------------------------------------------- cachefiles_ondemand_select_req cachefiles_ondemand_object_is_close(A) cachefiles_ondemand_set_object_reopening(A) queue_work(fscache_object_wq, &info->work) ondemand_object_worker cachefiles_ondemand_init_object(A) cachefiles_ondemand_send_req(OPEN) // get msg_id 6 wait_for_completion(&req_A->done) cachefiles_ondemand_daemon_read // read msg_id 6 req_A cachefiles_ondemand_get_fd copy_to_user // Malicious completion msg_id 6 copen 6,-1 cachefiles_ondemand_copen complete(&req_A->done) // will not set the object to close // because ondemand_id && fd is valid. // ondemand_object_worker() is done // but the object is still reopening. // new open req_B cachefiles_ondemand_init_object(B) cachefiles_ondemand_send_req(OPEN) // reuse msg_id 6 process_open_req copen 6,A.size // The expected failed copen was executed successfully Expect copen to fail, and when it does, it closes fd, which sets the object to close, and then close triggers reopen again. However, due to msg_id reuse resulting in a successful copen, the anonymous fd is not closed until the daemon exits. Therefore read requests waiting for reopen to complete may trigger hung task. To avoid this issue, allocate the msg_id cyclically to avoid reusing the msg_id for a very short duration of time. 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]> Reviewed-by: Gao Xiang <[email protected]> Reviewed-by: Jia Zhu <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent 12e009d commit 19f4f39

File tree

2 files changed

+17
-4
lines changed

2 files changed

+17
-4
lines changed

fs/cachefiles/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ struct cachefiles_cache {
128128
unsigned long req_id_next;
129129
struct xarray ondemand_ids; /* xarray for ondemand_id allocation */
130130
u32 ondemand_id_next;
131+
u32 msg_id_next;
131132
};
132133

133134
static inline bool cachefiles_in_ondemand_mode(struct cachefiles_cache *cache)

fs/cachefiles/ondemand.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,20 +433,32 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
433433
smp_mb();
434434

435435
if (opcode == CACHEFILES_OP_CLOSE &&
436-
!cachefiles_ondemand_object_is_open(object)) {
436+
!cachefiles_ondemand_object_is_open(object)) {
437437
WARN_ON_ONCE(object->ondemand->ondemand_id == 0);
438438
xas_unlock(&xas);
439439
ret = -EIO;
440440
goto out;
441441
}
442442

443-
xas.xa_index = 0;
443+
/*
444+
* Cyclically find a free xas to avoid msg_id reuse that would
445+
* cause the daemon to successfully copen a stale msg_id.
446+
*/
447+
xas.xa_index = cache->msg_id_next;
444448
xas_find_marked(&xas, UINT_MAX, XA_FREE_MARK);
449+
if (xas.xa_node == XAS_RESTART) {
450+
xas.xa_index = 0;
451+
xas_find_marked(&xas, cache->msg_id_next - 1, XA_FREE_MARK);
452+
}
445453
if (xas.xa_node == XAS_RESTART)
446454
xas_set_err(&xas, -EBUSY);
455+
447456
xas_store(&xas, req);
448-
xas_clear_mark(&xas, XA_FREE_MARK);
449-
xas_set_mark(&xas, CACHEFILES_REQ_NEW);
457+
if (xas_valid(&xas)) {
458+
cache->msg_id_next = xas.xa_index + 1;
459+
xas_clear_mark(&xas, XA_FREE_MARK);
460+
xas_set_mark(&xas, CACHEFILES_REQ_NEW);
461+
}
450462
xas_unlock(&xas);
451463
} while (xas_nomem(&xas, GFP_KERNEL));
452464

0 commit comments

Comments
 (0)