I've been investigating the race in ptrace(2) ATF tests: dbregs_* for
x86 (i386 and amd64).

Examples:

NetBSD/amd64
http://releng.netbsd.org/b5reports/amd64/2018/2018.03.06.11.21.31/test.html#lib_libc_sys_t_ptrace_wait6_dbregs_dr3_trap_variable_readwrite_write_2bytes

NetBSD/i386
http://releng.netbsd.org/b5reports/i386/2017/2017.12.06.17.54.58/test.html#lib_libc_sys_t_ptrace_wait3_dbregs_dr2_trap_variable_readwrite_read_2bytes



I still don't know what is the root cause for the race and I'm open for
suggestions.



Observations:
1. This bug is reproducible only for the scenario with debug register
trap according to the tests of mine.

2. It's reproducible on slower x86 hardware or in softemu (qemu). I have
not been able to reproduce a single failure on post-Core2Duo CPU.

3. Adding or changing the scenario slightly of tests (like to PT_STEP or
changing SIGSTOP to other signal) makes the test disappear at all.

4. Adding almost any debug code anywhere makes this test either
disappear or make reproducible much less frequently (sometimes once a day).

5. I've detected that the bug is caused by the fact that _lwp_kill(2)
(called via raise(2)) [under a debugger] can trigger two calls of
wait(2) to return for the same signal.

A few days ago, I've prepared this document:

http://netbsd.org/~kamil/kernel/sigstop.txt

6. According to my tests in lwp_userret():

   1549         /*
   1550          * It is safe to do this read unlocked on a MP system..
   1551          */
   1552         while ((l->l_flag & LW_USERRET) != 0) {
   1553                 /*
   1554                  * Process pending signals first, unless the process
   1555                  * is dumping core or exiting, where we will instead
   1556                  * enter the LW_WSUSPEND case below.
   1557                  */
   1558                 if ((l->l_flag & (LW_PENDSIG | LW_WCORE | LW_WEXIT)) ==
   1559                     LW_PENDSIG) {
   1560                         mutex_enter(p->p_lock);
   1561                         while ((sig = issignal(l)) != 0)
   1562                                 postsig(sig);
   1563                         mutex_exit(p->p_lock);
   1564                 }
   1565

 -- src/sys/kern/kern_lwp.c

We always enter the while() loop and call issignal(). Sometimes we
extract a signal and call postsig(). Usually for the call, we see no
misbehavior.

7. I've checked that PT_CONTINUE branch where p_stat != SSTOP is never
taken, at least for this race. We always call:

    828         /* Finally, deliver the requested signal (or none). */
    829         if (t->p_stat == SSTOP) {
    830                 /*
    831                  * Unstop the process.  If it needs to take a
    832                  * signal, make all efforts to ensure that at
    833                  * an LWP runs to see it.
    834                  */
    835                 t->p_xsig = signo;
    836                 if (resume_all)
    837                         proc_unstop(t);
    838                 else
    839                         lwp_unstop(lt);
    840                 return 0;
    841         }

 -- src/sys/kern/sys_ptrace_common.c

8. We checked that we call sigpost() twice for a single _lwp_kill(2) call:
 A. handle_syscall() -> syscall() -> sv_invoke() -> sys__lwp_kill() ->
kpsignal2() -> sigpost()
 B. lwp_userret() -> issignal() -> sigswitch() -> proc_stop() -> sigpost()

9. Adding two consequential wait(2) calls, one after (checking for error
condition for another with WNOHANG) - makes the bug disappear completely.



I'm looking for hints to speedup the research.

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to