diff options
-rw-r--r-- | src/pkg/runtime/chan.c | 29 |
1 files changed, 20 insertions, 9 deletions
diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c index 69b90bda56..fba36a4c34 100644 --- a/src/pkg/runtime/chan.c +++ b/src/pkg/runtime/chan.c @@ -809,16 +809,27 @@ sellock(Select *sel) static void selunlock(Select *sel) { - uint32 i; - Hchan *c, *c0; + int32 i, n, r; + Hchan *c; - c = nil; - for(i=sel->ncase; i-->0;) { - c0 = sel->lockorder[i]; - if(c0 && c0 != c) { - c = c0; - runtime·unlock(c); - } + // We must be very careful here to not touch sel after we have unlocked + // the last lock, because sel can be freed right after the last unlock. + // Consider the following situation. + // First M calls runtime·park() in runtime·selectgo() passing the sel. + // Once runtime·park() has unlocked the last lock, another M makes + // the G that calls select runnable again and schedules it for execution. + // When the G runs on another M, it locks all the locks and frees sel. + // Now if the first M touches sel, it will access freed memory. + n = (int32)sel->ncase; + r = 0; + // skip the default case + if(n>0 && sel->lockorder[0] == nil) + r = 1; + for(i = n-1; i >= r; i--) { + c = sel->lockorder[i]; + if(i>0 && sel->lockorder[i-1] == c) + continue; // will unlock it on the next iteration + runtime·unlock(c); } } |