Skip to content

Commit eeb1798

Browse files
committed
Merge patch series "cachefiles: random bugfixes"
[email protected] <[email protected]> says: This is the third version of this patch series, in which another patch set is subsumed into this one to avoid confusing the two patch sets. (https://patchwork.kernel.org/project/linux-fsdevel/list/?series=854914) We've been testing ondemand mode for cachefiles since January, and we're almost done. We hit a lot of issues during the testing period, and this patch series fixes some of the issues. The patches have passed internal testing without regression. The following is a brief overview of the patches, see the patches for more details. Patch 1-2: Add fscache_try_get_volume() helper function to avoid fscache_volume use-after-free on cache withdrawal. Patch 3: Fix cachefiles_lookup_cookie() and cachefiles_withdraw_cache() concurrency causing cachefiles_volume use-after-free. Patch 4: Propagate error codes returned by vfs_getxattr() to avoid endless loops. Patch 5-7: A read request waiting for reopen could be closed maliciously before the reopen worker is executing or waiting to be scheduled. So ondemand_object_worker() may be called after the info and object and even the cache have been freed and trigger use-after-free. So use cancel_work_sync() in cachefiles_ondemand_clean_object() to cancel the reopen worker or wait for it to finish. Since it makes no sense to wait for the daemon to complete the reopen request, to avoid this pointless operation blocking cancel_work_sync(), Patch 1 avoids request generation by the DROPPING state when the request has not been sent, and Patch 2 flushes the requests of the current object before cancel_work_sync(). Patch 8: Cyclic allocation of msg_id to avoid msg_id reuse misleading the daemon to cause hung. Patch 9: Hold xas_lock during polling to avoid dereferencing reqs causing use-after-free. This issue was triggered frequently in our tests, and we found that anolis 5.10 had fixed it. So to avoid failing the test, this patch is pushed upstream as well. Baokun Li (7): netfs, fscache: export fscache_put_volume() and add fscache_try_get_volume() cachefiles: fix slab-use-after-free in fscache_withdraw_volume() cachefiles: fix slab-use-after-free in cachefiles_withdraw_cookie() cachefiles: propagate errors from vfs_getxattr() to avoid infinite loop cachefiles: stop sending new request when dropping object cachefiles: cancel all requests for the object that is being dropped cachefiles: cyclic allocation of msg_id to avoid reuse Hou Tao (1): cachefiles: wait for ondemand_object_worker to finish when dropping object Jingbo Xu (1): cachefiles: add missing lock protection when polling fs/cachefiles/cache.c | 45 ++++++++++++++++++++++++++++- fs/cachefiles/daemon.c | 4 +-- fs/cachefiles/internal.h | 3 ++ fs/cachefiles/ondemand.c | 52 ++++++++++++++++++++++++++++++---- fs/cachefiles/volume.c | 1 - fs/cachefiles/xattr.c | 5 +++- fs/netfs/fscache_volume.c | 14 +++++++++ fs/netfs/internal.h | 2 -- include/linux/fscache-cache.h | 6 ++++ include/trace/events/fscache.h | 4 +++ 10 files changed, 123 insertions(+), 13 deletions(-) Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
2 parents 22a40d1 + cf5bb09 commit eeb1798

23 files changed

+201
-126
lines changed

fs/cachefiles/cache.c

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/slab.h>
99
#include <linux/statfs.h>
1010
#include <linux/namei.h>
11+
#include <trace/events/fscache.h>
1112
#include "internal.h"
1213

1314
/*
@@ -312,26 +313,67 @@ static void cachefiles_withdraw_objects(struct cachefiles_cache *cache)
312313
}
313314

314315
/*
315-
* Withdraw volumes.
316+
* Withdraw fscache volumes.
317+
*/
318+
static void cachefiles_withdraw_fscache_volumes(struct cachefiles_cache *cache)
319+
{
320+
struct list_head *cur;
321+
struct cachefiles_volume *volume;
322+
struct fscache_volume *vcookie;
323+
324+
_enter("");
325+
retry:
326+
spin_lock(&cache->object_list_lock);
327+
list_for_each(cur, &cache->volumes) {
328+
volume = list_entry(cur, struct cachefiles_volume, cache_link);
329+
330+
if (atomic_read(&volume->vcookie->n_accesses) == 0)
331+
continue;
332+
333+
vcookie = fscache_try_get_volume(volume->vcookie,
334+
fscache_volume_get_withdraw);
335+
if (vcookie) {
336+
spin_unlock(&cache->object_list_lock);
337+
fscache_withdraw_volume(vcookie);
338+
fscache_put_volume(vcookie, fscache_volume_put_withdraw);
339+
goto retry;
340+
}
341+
}
342+
spin_unlock(&cache->object_list_lock);
343+
344+
_leave("");
345+
}
346+
347+
/*
348+
* Withdraw cachefiles volumes.
316349
*/
317350
static void cachefiles_withdraw_volumes(struct cachefiles_cache *cache)
318351
{
319352
_enter("");
320353

321354
for (;;) {
355+
struct fscache_volume *vcookie = NULL;
322356
struct cachefiles_volume *volume = NULL;
323357

324358
spin_lock(&cache->object_list_lock);
325359
if (!list_empty(&cache->volumes)) {
326360
volume = list_first_entry(&cache->volumes,
327361
struct cachefiles_volume, cache_link);
362+
vcookie = fscache_try_get_volume(volume->vcookie,
363+
fscache_volume_get_withdraw);
364+
if (!vcookie) {
365+
spin_unlock(&cache->object_list_lock);
366+
cpu_relax();
367+
continue;
368+
}
328369
list_del_init(&volume->cache_link);
329370
}
330371
spin_unlock(&cache->object_list_lock);
331372
if (!volume)
332373
break;
333374

334375
cachefiles_withdraw_volume(volume);
376+
fscache_put_volume(vcookie, fscache_volume_put_withdraw);
335377
}
336378

337379
_leave("");
@@ -371,6 +413,7 @@ void cachefiles_withdraw_cache(struct cachefiles_cache *cache)
371413
pr_info("File cache on %s unregistering\n", fscache->name);
372414

373415
fscache_withdraw_cache(fscache);
416+
cachefiles_withdraw_fscache_volumes(cache);
374417

375418
/* we now have to destroy all the active objects pertaining to this
376419
* cache - which we do by passing them off to thread pool to be

fs/cachefiles/daemon.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,14 +366,14 @@ static __poll_t cachefiles_daemon_poll(struct file *file,
366366

367367
if (cachefiles_in_ondemand_mode(cache)) {
368368
if (!xa_empty(&cache->reqs)) {
369-
rcu_read_lock();
369+
xas_lock(&xas);
370370
xas_for_each_marked(&xas, req, ULONG_MAX, CACHEFILES_REQ_NEW) {
371371
if (!cachefiles_ondemand_is_reopening_read(req)) {
372372
mask |= EPOLLIN;
373373
break;
374374
}
375375
}
376-
rcu_read_unlock();
376+
xas_unlock(&xas);
377377
}
378378
} else {
379379
if (test_bit(CACHEFILES_STATE_CHANGED, &cache->flags))

fs/cachefiles/internal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ enum cachefiles_object_state {
4848
CACHEFILES_ONDEMAND_OBJSTATE_CLOSE, /* Anonymous fd closed by daemon or initial state */
4949
CACHEFILES_ONDEMAND_OBJSTATE_OPEN, /* Anonymous fd associated with object is available */
5050
CACHEFILES_ONDEMAND_OBJSTATE_REOPENING, /* Object that was closed and is being reopened. */
51+
CACHEFILES_ONDEMAND_OBJSTATE_DROPPING, /* Object is being dropped. */
5152
};
5253

5354
struct cachefiles_ondemand_info {
@@ -128,6 +129,7 @@ struct cachefiles_cache {
128129
unsigned long req_id_next;
129130
struct xarray ondemand_ids; /* xarray for ondemand_id allocation */
130131
u32 ondemand_id_next;
132+
u32 msg_id_next;
131133
};
132134

133135
static inline bool cachefiles_in_ondemand_mode(struct cachefiles_cache *cache)
@@ -335,6 +337,7 @@ cachefiles_ondemand_set_object_##_state(struct cachefiles_object *object) \
335337
CACHEFILES_OBJECT_STATE_FUNCS(open, OPEN);
336338
CACHEFILES_OBJECT_STATE_FUNCS(close, CLOSE);
337339
CACHEFILES_OBJECT_STATE_FUNCS(reopening, REOPENING);
340+
CACHEFILES_OBJECT_STATE_FUNCS(dropping, DROPPING);
338341

339342
static inline bool cachefiles_ondemand_is_reopening_read(struct cachefiles_req *req)
340343
{

fs/cachefiles/ondemand.c

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,8 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
517517
*/
518518
xas_lock(&xas);
519519

520-
if (test_bit(CACHEFILES_DEAD, &cache->flags)) {
520+
if (test_bit(CACHEFILES_DEAD, &cache->flags) ||
521+
cachefiles_ondemand_object_is_dropping(object)) {
521522
xas_unlock(&xas);
522523
ret = -EIO;
523524
goto out;
@@ -527,20 +528,32 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
527528
smp_mb();
528529

529530
if (opcode == CACHEFILES_OP_CLOSE &&
530-
!cachefiles_ondemand_object_is_open(object)) {
531+
!cachefiles_ondemand_object_is_open(object)) {
531532
WARN_ON_ONCE(object->ondemand->ondemand_id == 0);
532533
xas_unlock(&xas);
533534
ret = -EIO;
534535
goto out;
535536
}
536537

537-
xas.xa_index = 0;
538+
/*
539+
* Cyclically find a free xas to avoid msg_id reuse that would
540+
* cause the daemon to successfully copen a stale msg_id.
541+
*/
542+
xas.xa_index = cache->msg_id_next;
538543
xas_find_marked(&xas, UINT_MAX, XA_FREE_MARK);
544+
if (xas.xa_node == XAS_RESTART) {
545+
xas.xa_index = 0;
546+
xas_find_marked(&xas, cache->msg_id_next - 1, XA_FREE_MARK);
547+
}
539548
if (xas.xa_node == XAS_RESTART)
540549
xas_set_err(&xas, -EBUSY);
550+
541551
xas_store(&xas, req);
542-
xas_clear_mark(&xas, XA_FREE_MARK);
543-
xas_set_mark(&xas, CACHEFILES_REQ_NEW);
552+
if (xas_valid(&xas)) {
553+
cache->msg_id_next = xas.xa_index + 1;
554+
xas_clear_mark(&xas, XA_FREE_MARK);
555+
xas_set_mark(&xas, CACHEFILES_REQ_NEW);
556+
}
544557
xas_unlock(&xas);
545558
} while (xas_nomem(&xas, GFP_KERNEL));
546559

@@ -568,7 +581,8 @@ static int cachefiles_ondemand_send_req(struct cachefiles_object *object,
568581
* If error occurs after creating the anonymous fd,
569582
* cachefiles_ondemand_fd_release() will set object to close.
570583
*/
571-
if (opcode == CACHEFILES_OP_OPEN)
584+
if (opcode == CACHEFILES_OP_OPEN &&
585+
!cachefiles_ondemand_object_is_dropping(object))
572586
cachefiles_ondemand_set_object_close(object);
573587
kfree(req);
574588
return ret;
@@ -667,8 +681,34 @@ int cachefiles_ondemand_init_object(struct cachefiles_object *object)
667681

668682
void cachefiles_ondemand_clean_object(struct cachefiles_object *object)
669683
{
684+
unsigned long index;
685+
struct cachefiles_req *req;
686+
struct cachefiles_cache *cache;
687+
688+
if (!object->ondemand)
689+
return;
690+
670691
cachefiles_ondemand_send_req(object, CACHEFILES_OP_CLOSE, 0,
671692
cachefiles_ondemand_init_close_req, NULL);
693+
694+
if (!object->ondemand->ondemand_id)
695+
return;
696+
697+
/* Cancel all requests for the object that is being dropped. */
698+
cache = object->volume->cache;
699+
xa_lock(&cache->reqs);
700+
cachefiles_ondemand_set_object_dropping(object);
701+
xa_for_each(&cache->reqs, index, req) {
702+
if (req->object == object) {
703+
req->error = -EIO;
704+
complete(&req->done);
705+
__xa_erase(&cache->reqs, index);
706+
}
707+
}
708+
xa_unlock(&cache->reqs);
709+
710+
/* Wait for ondemand_object_worker() to finish to avoid UAF. */
711+
cancel_work_sync(&object->ondemand->ondemand_work);
672712
}
673713

674714
int cachefiles_ondemand_init_obj_info(struct cachefiles_object *object,

fs/cachefiles/volume.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ void cachefiles_free_volume(struct fscache_volume *vcookie)
133133

134134
void cachefiles_withdraw_volume(struct cachefiles_volume *volume)
135135
{
136-
fscache_withdraw_volume(volume->vcookie);
137136
cachefiles_set_volume_xattr(volume);
138137
__cachefiles_free_volume(volume);
139138
}

fs/cachefiles/xattr.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,11 @@ int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file
110110
if (xlen == 0)
111111
xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, tlen);
112112
if (xlen != tlen) {
113-
if (xlen < 0)
113+
if (xlen < 0) {
114+
ret = xlen;
114115
trace_cachefiles_vfs_error(object, file_inode(file), xlen,
115116
cachefiles_trace_getxattr_error);
117+
}
116118
if (xlen == -EIO)
117119
cachefiles_io_error_obj(
118120
object,
@@ -252,6 +254,7 @@ int cachefiles_check_volume_xattr(struct cachefiles_volume *volume)
252254
xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, len);
253255
if (xlen != len) {
254256
if (xlen < 0) {
257+
ret = xlen;
255258
trace_cachefiles_vfs_error(NULL, d_inode(dentry), xlen,
256259
cachefiles_trace_getxattr_error);
257260
if (xlen == -EIO)

fs/netfs/buffered_read.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ void netfs_rreq_unlock_folios(struct netfs_io_request *rreq)
117117
if (!test_bit(NETFS_RREQ_DONT_UNLOCK_FOLIOS, &rreq->flags)) {
118118
if (folio->index == rreq->no_unlock_folio &&
119119
test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags))
120-
_debug("no unlock");
120+
kdebug("no unlock");
121121
else
122122
folio_unlock(folio);
123123
}
@@ -204,7 +204,7 @@ void netfs_readahead(struct readahead_control *ractl)
204204
struct netfs_inode *ctx = netfs_inode(ractl->mapping->host);
205205
int ret;
206206

207-
_enter("%lx,%x", readahead_index(ractl), readahead_count(ractl));
207+
kenter("%lx,%x", readahead_index(ractl), readahead_count(ractl));
208208

209209
if (readahead_count(ractl) == 0)
210210
return;
@@ -268,7 +268,7 @@ int netfs_read_folio(struct file *file, struct folio *folio)
268268
struct folio *sink = NULL;
269269
int ret;
270270

271-
_enter("%lx", folio->index);
271+
kenter("%lx", folio->index);
272272

273273
rreq = netfs_alloc_request(mapping, file,
274274
folio_file_pos(folio), folio_size(folio),
@@ -508,7 +508,7 @@ int netfs_write_begin(struct netfs_inode *ctx,
508508

509509
have_folio:
510510
*_folio = folio;
511-
_leave(" = 0");
511+
kleave(" = 0");
512512
return 0;
513513

514514
error_put:
@@ -518,7 +518,7 @@ int netfs_write_begin(struct netfs_inode *ctx,
518518
folio_unlock(folio);
519519
folio_put(folio);
520520
}
521-
_leave(" = %d", ret);
521+
kleave(" = %d", ret);
522522
return ret;
523523
}
524524
EXPORT_SYMBOL(netfs_write_begin);
@@ -536,7 +536,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio,
536536
size_t flen = folio_size(folio);
537537
int ret;
538538

539-
_enter("%zx @%llx", flen, start);
539+
kenter("%zx @%llx", flen, start);
540540

541541
ret = -ENOMEM;
542542

@@ -567,7 +567,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio,
567567
error_put:
568568
netfs_put_request(rreq, false, netfs_rreq_trace_put_discard);
569569
error:
570-
_leave(" = %d", ret);
570+
kleave(" = %d", ret);
571571
return ret;
572572
}
573573

fs/netfs/buffered_write.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ static enum netfs_how_to_modify netfs_how_to_modify(struct netfs_inode *ctx,
5656
struct netfs_group *group = netfs_folio_group(folio);
5757
loff_t pos = folio_file_pos(folio);
5858

59-
_enter("");
59+
kenter("");
6060

6161
if (group != netfs_group && group != NETFS_FOLIO_COPY_TO_CACHE)
6262
return NETFS_FLUSH_CONTENT;
@@ -272,12 +272,12 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
272272
*/
273273
howto = netfs_how_to_modify(ctx, file, folio, netfs_group,
274274
flen, offset, part, maybe_trouble);
275-
_debug("howto %u", howto);
275+
kdebug("howto %u", howto);
276276
switch (howto) {
277277
case NETFS_JUST_PREFETCH:
278278
ret = netfs_prefetch_for_write(file, folio, offset, part);
279279
if (ret < 0) {
280-
_debug("prefetch = %zd", ret);
280+
kdebug("prefetch = %zd", ret);
281281
goto error_folio_unlock;
282282
}
283283
break;
@@ -418,7 +418,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter,
418418
}
419419

420420
iocb->ki_pos += written;
421-
_leave(" = %zd [%zd]", written, ret);
421+
kleave(" = %zd [%zd]", written, ret);
422422
return written ? written : ret;
423423

424424
error_folio_unlock:
@@ -491,7 +491,7 @@ ssize_t netfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
491491
struct netfs_inode *ictx = netfs_inode(inode);
492492
ssize_t ret;
493493

494-
_enter("%llx,%zx,%llx", iocb->ki_pos, iov_iter_count(from), i_size_read(inode));
494+
kenter("%llx,%zx,%llx", iocb->ki_pos, iov_iter_count(from), i_size_read(inode));
495495

496496
if (!iov_iter_count(from))
497497
return 0;
@@ -528,7 +528,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr
528528
vm_fault_t ret = VM_FAULT_RETRY;
529529
int err;
530530

531-
_enter("%lx", folio->index);
531+
kenter("%lx", folio->index);
532532

533533
sb_start_pagefault(inode->i_sb);
534534

fs/netfs/direct_read.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ ssize_t netfs_unbuffered_read_iter_locked(struct kiocb *iocb, struct iov_iter *i
3333
size_t orig_count = iov_iter_count(iter);
3434
bool async = !is_sync_kiocb(iocb);
3535

36-
_enter("");
36+
kenter("");
3737

3838
if (!orig_count)
3939
return 0; /* Don't update atime */

0 commit comments

Comments
 (0)