Fix a race between kern_setitimer() and realitexpire(), where the

callout is started before kern_setitimer() acquires process mutex, but
looses a race and kern_setitimer() gets the process mutex before the
callout.  Then, assuming that new specified struct itimerval has
it_interval zero, but it_value non-zero, the callout, after it starts
executing again, clears p->p_realtimer.it_value, but kern_setitimer()
already rescheduled the callout.

As the result of the race, both p_realtimer is zero, and the callout
is rescheduled. Then, in the exit1(), the exit code sees that it_value
is zero and does not even try to stop the callout. This allows the
struct proc to be reused and eventually the armed callout is
re-initialized.  The consequence is the corrupted callwheel tailq.

Use process mutex to interlock the callout start, which fixes the race.

Reported and tested by:	pho
Reviewed by:	jhb
MFC after:	2 weeks
This commit is contained in:
Konstantin Belousov 2012-12-04 20:49:39 +00:00
parent 9bdf6ccab3
commit f7e50ea722
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=243869
3 changed files with 2 additions and 5 deletions

View File

@ -498,7 +498,7 @@ proc0_init(void *dummy __unused)
strncpy(p->p_comm, "kernel", sizeof (p->p_comm));
strncpy(td->td_name, "swapper", sizeof (td->td_name));
callout_init(&p->p_itcallout, CALLOUT_MPSAFE);
callout_init_mtx(&p->p_itcallout, &p->p_mtx, 0);
callout_init_mtx(&p->p_limco, &p->p_mtx, 0);
callout_init(&td->td_slpcallout, CALLOUT_MPSAFE);

View File

@ -591,7 +591,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2,
LIST_INIT(&p2->p_children);
LIST_INIT(&p2->p_orphans);
callout_init(&p2->p_itcallout, CALLOUT_MPSAFE);
callout_init_mtx(&p2->p_itcallout, &p2->p_mtx, 0);
/*
* If PF_FORK is set, the child process inherits the

View File

@ -788,13 +788,11 @@ realitexpire(void *arg)
struct timeval ctv, ntv;
p = (struct proc *)arg;
PROC_LOCK(p);
kern_psignal(p, SIGALRM);
if (!timevalisset(&p->p_realtimer.it_interval)) {
timevalclear(&p->p_realtimer.it_value);
if (p->p_flag & P_WEXIT)
wakeup(&p->p_itcallout);
PROC_UNLOCK(p);
return;
}
for (;;) {
@ -806,7 +804,6 @@ realitexpire(void *arg)
timevalsub(&ntv, &ctv);
callout_reset(&p->p_itcallout, tvtohz(&ntv) - 1,
realitexpire, p);
PROC_UNLOCK(p);
return;
}
}