Skip to content

Commit 5d8f805

Browse files
LiBaokun96brauner
authored andcommitted
cachefiles: fix slab-use-after-free in cachefiles_withdraw_cookie()
We got the following issue in our fault injection stress test: ================================================================== BUG: KASAN: slab-use-after-free in cachefiles_withdraw_cookie+0x4d9/0x600 Read of size 8 at addr ffff888118efc000 by task kworker/u78:0/109 CPU: 13 PID: 109 Comm: kworker/u78:0 Not tainted 6.8.0-dirty #566 Call Trace: <TASK> kasan_report+0x93/0xc0 cachefiles_withdraw_cookie+0x4d9/0x600 fscache_cookie_state_machine+0x5c8/0x1230 fscache_cookie_worker+0x91/0x1c0 process_one_work+0x7fa/0x1800 [...] Allocated by task 117: kmalloc_trace+0x1b3/0x3c0 cachefiles_acquire_volume+0xf3/0x9c0 fscache_create_volume_work+0x97/0x150 process_one_work+0x7fa/0x1800 [...] Freed by task 120301: kfree+0xf1/0x2c0 cachefiles_withdraw_cache+0x3fa/0x920 cachefiles_put_unbind_pincount+0x1f6/0x250 cachefiles_daemon_release+0x13b/0x290 __fput+0x204/0xa00 task_work_run+0x139/0x230 do_exit+0x87a/0x29b0 [...] ================================================================== Following is the process that triggers the issue: p1 | p2 ------------------------------------------------------------ fscache_begin_lookup fscache_begin_volume_access fscache_cache_is_live(fscache_cache) cachefiles_daemon_release cachefiles_put_unbind_pincount cachefiles_daemon_unbind cachefiles_withdraw_cache fscache_withdraw_cache fscache_set_cache_state(cache, FSCACHE_CACHE_IS_WITHDRAWN); cachefiles_withdraw_objects(cache) fscache_wait_for_objects(fscache) atomic_read(&fscache_cache->object_count) == 0 fscache_perform_lookup cachefiles_lookup_cookie cachefiles_alloc_object refcount_set(&object->ref, 1); object->volume = volume fscache_count_object(vcookie->cache); atomic_inc(&fscache_cache->object_count) cachefiles_withdraw_volumes cachefiles_withdraw_volume fscache_withdraw_volume __cachefiles_free_volume kfree(cachefiles_volume) fscache_cookie_state_machine cachefiles_withdraw_cookie cache = object->volume->cache; // cachefiles_volume UAF !!! After setting FSCACHE_CACHE_IS_WITHDRAWN, wait for all the cookie lookups to complete first, and then wait for fscache_cache->object_count == 0 to avoid the cookie exiting after the volume has been freed and triggering the above issue. Therefore call fscache_withdraw_volume() before calling cachefiles_withdraw_objects(). This way, after setting FSCACHE_CACHE_IS_WITHDRAWN, only the following two cases will occur: 1) fscache_begin_lookup fails in fscache_begin_volume_access(). 2) fscache_withdraw_volume() will ensure that fscache_count_object() has been executed before calling fscache_wait_for_objects(). Fixes: fe2140e ("cachefiles: Implement volume support") Suggested-by: Hou Tao <[email protected]> Signed-off-by: Baokun Li <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent 522018a commit 5d8f805

File tree

2 files changed

+34
-2
lines changed

2 files changed

+34
-2
lines changed

fs/cachefiles/cache.c

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,39 @@ static void cachefiles_withdraw_objects(struct cachefiles_cache *cache)
313313
}
314314

315315
/*
316-
* 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.
317349
*/
318350
static void cachefiles_withdraw_volumes(struct cachefiles_cache *cache)
319351
{
@@ -381,6 +413,7 @@ void cachefiles_withdraw_cache(struct cachefiles_cache *cache)
381413
pr_info("File cache on %s unregistering\n", fscache->name);
382414

383415
fscache_withdraw_cache(fscache);
416+
cachefiles_withdraw_fscache_volumes(cache);
384417

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

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
}

0 commit comments

Comments
 (0)