kevent: Fix races between timer detach and kqtimer_proc_continue()
- When detaching a knote, we need to double check the enqueued flag after acquiring the process lock, as kqtimer_proc_continue() may have toggled it. - kqtimer_proc_continue() could in principle reschedule a stopped callout after filt_timerdetach() drains the callout. So, we need to re-check. Reported by: syzbot+4a4cebb3ec07892cb040@syzkaller.appspotmail.com Reported by: syzbot+a9c04bc76078a3b7dd8d@syzkaller.appspotmail.com Reviewed by: kib MFC after: 1 week Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D31772
This commit is contained in:
parent
d491b42535
commit
c511383de7
@ -859,14 +859,24 @@ filt_timerdetach(struct knote *kn)
|
||||
{
|
||||
struct kq_timer_cb_data *kc;
|
||||
unsigned int old __unused;
|
||||
bool pending;
|
||||
|
||||
kc = kn->kn_ptr.p_v;
|
||||
callout_drain(&kc->c);
|
||||
if ((kc->flags & KQ_TIMER_CB_ENQUEUED) != 0) {
|
||||
do {
|
||||
callout_drain(&kc->c);
|
||||
|
||||
/*
|
||||
* kqtimer_proc_continue() might have rescheduled this callout.
|
||||
* Double-check, using the process mutex as an interlock.
|
||||
*/
|
||||
PROC_LOCK(kc->p);
|
||||
TAILQ_REMOVE(&kc->p->p_kqtim_stop, kc, link);
|
||||
if ((kc->flags & KQ_TIMER_CB_ENQUEUED) != 0) {
|
||||
kc->flags &= ~KQ_TIMER_CB_ENQUEUED;
|
||||
TAILQ_REMOVE(&kc->p->p_kqtim_stop, kc, link);
|
||||
}
|
||||
pending = callout_pending(&kc->c);
|
||||
PROC_UNLOCK(kc->p);
|
||||
}
|
||||
} while (pending);
|
||||
free(kc, M_KQUEUE);
|
||||
old = atomic_fetchadd_int(&kq_ncallouts, -1);
|
||||
KASSERT(old > 0, ("Number of callouts cannot become negative"));
|
||||
|
Loading…
Reference in New Issue
Block a user