diff options
author | Takashi Iwai <tiwai@suse.de> | 2023-05-12 16:18:00 +0100 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@kernel.org> | 2023-05-14 07:33:31 +0100 |
commit | b8c75e4a1b325ea0a9433fa8834be97b5836b946 (patch) | |
tree | 7130e5c018a196a73e9b9b0935163818be2a5609 /drivers/media | |
parent | 627bb528b086b4136315c25d6a447a98ea9448d3 (diff) |
media: dvb-core: Fix kernel WARNING for blocking operation in wait_event*()
Using a semaphore in the wait_event*() condition is no good idea.
It hits a kernel WARN_ON() at prepare_to_wait_event() like:
do not call blocking ops when !TASK_RUNNING; state=1 set at
prepare_to_wait_event+0x6d/0x690
For avoiding the potential deadlock, rewrite to an open-coded loop
instead. Unlike the loop in wait_event*(), this uses wait_woken()
after the condition check, hence the task state stays consistent.
CVE-2023-31084 was assigned to this bug.
Link: https://lore.kernel.org/r/CA+UBctCu7fXn4q41O_3=id1+OdyQ85tZY1x+TkT-6OVBL6KAUw@mail.gmail.com/
Link: https://lore.kernel.org/linux-media/20230512151800.1874-1-tiwai@suse.de
Reported-by: Yu Hao <yhao016@ucr.edu>
Closes: https://nvd.nist.gov/vuln/detail/CVE-2023-31084
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/dvb-core/dvb_frontend.c | 16 |
1 files changed, 12 insertions, 4 deletions
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 947b61959b2b..bc6950a5740f 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -293,14 +293,22 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe, } if (events->eventw == events->eventr) { - int ret; + struct wait_queue_entry wait; + int ret = 0; if (flags & O_NONBLOCK) return -EWOULDBLOCK; - ret = wait_event_interruptible(events->wait_queue, - dvb_frontend_test_event(fepriv, events)); - + init_waitqueue_entry(&wait, current); + add_wait_queue(&events->wait_queue, &wait); + while (!dvb_frontend_test_event(fepriv, events)) { + wait_woken(&wait, TASK_INTERRUPTIBLE, 0); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + remove_wait_queue(&events->wait_queue, &wait); if (ret < 0) return ret; } |