diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2016-03-26 02:37:42 +0100 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2016-03-26 02:37:42 +0100 |
commit | 9aa6573359ffad41ef197b7f83623d7cbdeca068 (patch) | |
tree | 1fd245250d7ddabd7dd138123ccabf17b77cd902 /sys/src/9/port/proc.c | |
parent | 5c95c50c6c470e1b9582998796555e00a6e0d7e5 (diff) |
kernel: fix tsleep()/twakeup()/tsemacquire() race
tsleep() used to cancel the timer with:
if(up->tt != nil)
timerdel(up);
which still can result in twakeup() to fire after tsleep()
returns (because we set Timer.tt to nil *before* we call the tfn).
in most cases, this is not an issue as the Rendez*
usually is just &up->sleep, but when it is dynamically allocated
or on the stack like in tsemacquire(), twakeup() will call
wakeup() on a potentially garbage Rendez structure!
to fix the race, we execute the wakup() with the Timer lock
held, and set p->trend to nil only after we called wakeup().
that way, the timerdel(); which unconditionally locks the Timer;
can act as a proper barrier and use up->trend == nil as the
condition if the timer has already fired.
Diffstat (limited to 'sys/src/9/port/proc.c')
-rw-r--r-- | sys/src/9/port/proc.c | 27 |
1 files changed, 19 insertions, 8 deletions
diff --git a/sys/src/9/port/proc.c b/sys/src/9/port/proc.c index 412a8eef9..3012cbeaa 100644 --- a/sys/src/9/port/proc.c +++ b/sys/src/9/port/proc.c @@ -822,24 +822,37 @@ tfn(void *arg) return up->trend == nil || up->tfn(arg); } -void +static void twakeup(Ureg*, Timer *t) { Proc *p; Rendez *trend; + ilock(t); p = t->ta; trend = p->trend; - p->trend = nil; - if(trend != nil) + if(trend != nil){ wakeup(trend); + p->trend = nil; + } + iunlock(t); +} + +static void +stoptimer(void) +{ + if(up->trend != nil){ + up->trend = nil; + timerdel(up); + } } void tsleep(Rendez *r, int (*fn)(void*), void *arg, ulong ms) { if(up->tt != nil){ - print("tsleep: timer active: mode %d, tf %#p\n", up->tmode, up->tf); + print("%s %lux: tsleep timer active: mode %d, tf %#p, pc %#p\n", + up->text, up->pid, up->tmode, up->tf, getcallerpc(&r)); timerdel(up); } up->tns = MS2NS(ms); @@ -851,13 +864,11 @@ tsleep(Rendez *r, int (*fn)(void*), void *arg, ulong ms) timeradd(up); if(waserror()){ - timerdel(up); + stoptimer(); nexterror(); } sleep(r, tfn, arg); - if(up->tt != nil) - timerdel(up); - up->twhen = 0; + stoptimer(); poperror(); } |