Dear Linux kernel developers and maintainers,
We are writing to report 7 issues discovered in the dvb subsystem with
our generated syzkaller specifications. Below are the details of these
issues:
(1) KASAN: slab-use-after-free Write in dvb_device_open (reproducer: c, syz)
(2) KASAN: slab-use-after-free Read in dvb_frontend_release (reproducer: syz)
(3) possible deadlock in dvb_dvr_release (reproducer: c, syz)
(4) KASAN: slab-use-after-free Read in dvb_frontend_thread
(5) KFENCE: use-after-free read in dvb_frontend_release
(6) WARNING: still has locks held in _dmxdev_lock
(7) WARNING: bad unlock balance in _dmxdev_unlock
Here is our analysis:
## (1) KASAN: slab-use-after-free Write in dvb_device_open
The root cause is double put. In dvb_device_open(), the refcount is
decreased (via put) when the open operation fails [1]. However, in
dvb_frontend_open(), put will also be called in dvb_generic_release()
if dvb_frontend_start() failed. Such process can lead to double put
and use-after-free.
[1]
https://elixir.bootlin.com/linux/v6.19-rc4/source/drivers/media/dvb-core/dvbdev.c#L113
## (2) KASAN: slab-use-after-free Read in dvb_frontend_release
The root cause is that in concurrent scenarios, the kernel attempts to
access dvbdev->users after dvbdev has already been freed [2].
[2]
https://elixir.bootlin.com/linux/v6.19-rc4/source/drivers/media/dvb-core/dvb_frontend.c#L2916
## (3) possible deadlock in dvb_dvr_release
The root cause is lock imbalance. Call chain is as follows:
dvb_vb2_dqbuf() ->
vb2_core_dqbuf() ->
__vb2_get_done_vb() ->
__vb2_wait_for_done_vb() ->
_dmxdev_lock() (call_void_qop(q, wait_finish, q)) ->
mutex_lock()
ctx->mutex is locked during this process, but if the return value of
vb2_core_dqbuf() is not zero, ctx->mutex will not be unlocked [3].
When the task exits and calls dvb_dvr_release(), the function attempts
to acquire dmxdev->lock, lockdep detects that ctx->mutex is still
held, violating the locking order and reporting possible deadlock.
[3]
https://elixir.bootlin.com/linux/v6.19-rc4/source/drivers/media/dvb-core/dvb_vb2.c#L415-L419
A potentail solution is to protect vb2_core_dqbuf() with mutex, e.g.
```
int dvb_vb2_dqbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
{
unsigned long flags;
int ret;
+ mutex_lock(&ctx->mutex);
ret = vb2_core_dqbuf(&ctx->vb_q, &b->index, b, ctx->nonblocking);
+ mutex_unlock(&ctx->mutex);
if (ret) ...
}
```
If this solution is acceptable, we would be happy to submit a patch :)
## Other issues
Unfortunately we do not have reproducers for other issues currently.
We have attached kernel console output to help with analysis and offer
our preliminary findings:
(4) KASAN: slab-use-after-free Read in dvb_frontend_thread: in
concurrent scenarios, kernel accesses fepriv->wait_queue after dvbdev
is freed [4]. Would adding a refcount for dvbdec to fix this?
(5) KFENCE: use-after-free read in dvb_frontend_release: similar to (2).
(6) WARNING: still has locks held in _dmxdev_lock: it seems a branch
in dvb_dvr_do_ioctl() might be acquiring a lock but failing to release
it?
(7) WARNING: bad unlock balance in _dmxdev_unlock: in call chain like
dvb_dvr_do_ioctl() -> dvb_vb2_dqbuf(), kernel misjudge lock state and
attempt to unlock an already unlocked lock?
[4]
https://elixir.bootlin.com/linux/v6.19-rc4/source/drivers/media/dvb-core/dvb_frontend.c#L682-L685
You can refer to [5] to reproduce issues using syz programs. We also
provide our generated specs to help with running of syz program.
[5]
https://github.com/google/syzkaller/blob/master/docs/reproducing_crashes.md#using-a-syz-reproducer
Please let me know if any further information is required.
Best Regards,
Jiaming Zhang