diff options
author | kib <kib@FreeBSD.org> | 2012-12-04 20:49:39 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2012-12-04 20:49:39 +0000 |
commit | 54d4ef7790ac3f2922675f0defbaf3b2f5a54d7a (patch) | |
tree | be5ca68461521c9d13f018cf6445647db4335cf1 | |
parent | f366a4aadf28f389a29d65d4ae42b4e84bc63720 (diff) | |
download | FreeBSD-src-54d4ef7790ac3f2922675f0defbaf3b2f5a54d7a.zip FreeBSD-src-54d4ef7790ac3f2922675f0defbaf3b2f5a54d7a.tar.gz |
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
-rw-r--r-- | sys/kern/init_main.c | 2 | ||||
-rw-r--r-- | sys/kern/kern_fork.c | 2 | ||||
-rw-r--r-- | sys/kern/kern_time.c | 3 |
3 files changed, 2 insertions, 5 deletions
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index c30e1a2..00b1c3f 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -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); diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 5dc43ca..b8a4825 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -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 diff --git a/sys/kern/kern_time.c b/sys/kern/kern_time.c index c0e7831..97c288d 100644 --- a/sys/kern/kern_time.c +++ b/sys/kern/kern_time.c @@ -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; } } |