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.
This commit is contained in:
parent
5c95c50c6c
commit
9aa6573359
1 changed files with 19 additions and 8 deletions
|
@ -822,24 +822,37 @@ tfn(void *arg)
|
||||||
return up->trend == nil || up->tfn(arg);
|
return up->trend == nil || up->tfn(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
twakeup(Ureg*, Timer *t)
|
twakeup(Ureg*, Timer *t)
|
||||||
{
|
{
|
||||||
Proc *p;
|
Proc *p;
|
||||||
Rendez *trend;
|
Rendez *trend;
|
||||||
|
|
||||||
|
ilock(t);
|
||||||
p = t->ta;
|
p = t->ta;
|
||||||
trend = p->trend;
|
trend = p->trend;
|
||||||
p->trend = nil;
|
if(trend != nil){
|
||||||
if(trend != nil)
|
|
||||||
wakeup(trend);
|
wakeup(trend);
|
||||||
|
p->trend = nil;
|
||||||
|
}
|
||||||
|
iunlock(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stoptimer(void)
|
||||||
|
{
|
||||||
|
if(up->trend != nil){
|
||||||
|
up->trend = nil;
|
||||||
|
timerdel(up);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tsleep(Rendez *r, int (*fn)(void*), void *arg, ulong ms)
|
tsleep(Rendez *r, int (*fn)(void*), void *arg, ulong ms)
|
||||||
{
|
{
|
||||||
if(up->tt != nil){
|
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);
|
timerdel(up);
|
||||||
}
|
}
|
||||||
up->tns = MS2NS(ms);
|
up->tns = MS2NS(ms);
|
||||||
|
@ -851,13 +864,11 @@ tsleep(Rendez *r, int (*fn)(void*), void *arg, ulong ms)
|
||||||
timeradd(up);
|
timeradd(up);
|
||||||
|
|
||||||
if(waserror()){
|
if(waserror()){
|
||||||
timerdel(up);
|
stoptimer();
|
||||||
nexterror();
|
nexterror();
|
||||||
}
|
}
|
||||||
sleep(r, tfn, arg);
|
sleep(r, tfn, arg);
|
||||||
if(up->tt != nil)
|
stoptimer();
|
||||||
timerdel(up);
|
|
||||||
up->twhen = 0;
|
|
||||||
poperror();
|
poperror();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue