If the local timer interrupt happens just after do_exit() sets PF_EXITING (and before it clears ->it_xxx_expires) run_posix_cpu_timers() will call check_process_timers() with tasklist_lock + ->siglock held and check_process_timers: t = tsk; do { .... do { t = next_thread(t); } while (unlikely(t->flags & PF_EXITING)); } while (t != tsk); the outer loop will never stop. Actually, the window is bigger. Another process can attach the timer after ->it_xxx_expires was cleared (see the next commit) and the 'if (PF_EXITING)' check in arm_timer() is racy (see the one after that).
Unified · Split
| @@ -1173,6 +1173,9 @@ static void check_process_timers(struct task_struct *tsk, · 1172 unmodified lines | ||
| 1173 | 1173 | } |
| 1174 | 1174 | t = tsk; |
| 1175 | 1175 | do { |
| 1176 | + if (unlikely(t->flags & PF_EXITING)) | |
| 1177 | + continue; | |
| 1178 | + | |
| 1176 | 1179 | ticks = cputime_add(cputime_add(t->utime, t->stime), |
| 1177 | 1180 | prof_left); |
| 1178 | 1181 | if (!cputime_eq(prof_expires, cputime_zero) && |
| @@ -1193,11 +1196,7 @@ static void check_process_timers(struct task_struct *tsk, · 14 unmodified lines | ||
| 1193 | 1196 | t->it_sched_expires > sched)) { |
| 1194 | 1197 | t->it_sched_expires = sched; |
| 1195 | 1198 | } |
| 1196 | − | |
| 1197 | − do { | |
| 1198 | − t = next_thread(t); | |
| 1199 | − } while (unlikely(t->flags & PF_EXITING)); | |
| 1200 | − } while (t != tsk); | |
| 1199 | + } while ((t = next_thread(t)) != tsk); | |
| 1201 | 1200 | } |
| 1202 | 1201 | } |
| 1203 | 1202 | |