fuse: fix use after free in fuse_read_interrupt()
There is a potential race between fuse_read_interrupt() and fuse_request_end(). TASK1 in fuse_read_interrupt(): delete req->intr_entry (while holding fiq->lock) TASK2 in fuse_request_end(): req->intr_entry is empty -> skip fiq->lock wake up TASK3 TASK3 request is freed TASK1 in fuse_read_interrupt(): dereference req->in.h.unique ***BAM*** Fix by always grabbing fiq->lock if the request was ever interrupted (FR_INTERRUPTED set) thereby serializing with concurrent fuse_read_interrupt() calls. FR_INTERRUPTED is set before the request is queued on fiq->interrupts. Dequeing the request is done with list_del_init() but FR_INTERRUPTED is not cleared in this case. Reported-by: lijiazi <lijiazi@xiaomi.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
parent
e73f0f0ee7
commit
e1e71c1688
|
@ -288,10 +288,10 @@ void fuse_request_end(struct fuse_req *req)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* test_and_set_bit() implies smp_mb() between bit
|
* test_and_set_bit() implies smp_mb() between bit
|
||||||
* changing and below intr_entry check. Pairs with
|
* changing and below FR_INTERRUPTED check. Pairs with
|
||||||
* smp_mb() from queue_interrupt().
|
* smp_mb() from queue_interrupt().
|
||||||
*/
|
*/
|
||||||
if (!list_empty(&req->intr_entry)) {
|
if (test_bit(FR_INTERRUPTED, &req->flags)) {
|
||||||
spin_lock(&fiq->lock);
|
spin_lock(&fiq->lock);
|
||||||
list_del_init(&req->intr_entry);
|
list_del_init(&req->intr_entry);
|
||||||
spin_unlock(&fiq->lock);
|
spin_unlock(&fiq->lock);
|
||||||
|
|
Loading…
Reference in New Issue