NetBSD Problem Report #50318

From paul@pokey.whooppee.com  Fri Oct  9 04:32:51 2015
Return-Path: <paul@pokey.whooppee.com>
Received: from mail.netbsd.org (mail.netbsd.org [149.20.53.66])
	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
	(Client CN "mail.netbsd.org", Issuer "Postmaster NetBSD.org" (verified OK))
	by mollari.NetBSD.org (Postfix) with ESMTPS id F0AC3A64EF
	for <gnats-bugs@gnats.NetBSD.org>; Fri,  9 Oct 2015 04:32:50 +0000 (UTC)
Message-Id: <20151009043200.C283916E7B@pokey.whooppee.com>
Date: Fri,  9 Oct 2015 12:32:00 +0800 (PHT)
From: paul@whooppee.com
Reply-To: paul@whooppee.com
To: gnats-bugs@NetBSD.org
Subject: Process p_nstopchild counter not updated in suspendsched()
X-Send-Pr-Version: 3.95

>Number:         50318
>Category:       kern
>Synopsis:       Process p_nstopchild counter not updated in suspendsched()
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    pgoyette
>State:          closed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Oct 09 04:35:00 +0000 2015
>Closed-Date:    Sat Nov 07 22:17:34 +0000 2015
>Last-Modified:  Sat Nov 07 22:17:34 +0000 2015
>Originator:     paul@whooppee.com
>Release:        NetBSD 7.99.21
>Organization:
+------------------+--------------------------+-------------------------+
| Paul Goyette     | PGP Key fingerprint:     | E-mail addresses:       |
| (Retired)        | FA29 0E3B 35AF E8AE 6651 | paul at whooppee.com    |
| Kernel Developer | 0786 F758 55DE 53BA 7731 | pgoyette at netbsd.org  |
+------------------+--------------------------+-------------------------+
>Environment:


System: NetBSD pokey.whooppee.com 7.99.21 NetBSD 7.99.21 (POKEY 2015-10-08 12:18:09) #0: Fri Oct 9 10:41:35 PHT 2015 paul@pokey.whooppee.com:/build/netbsd-local/obj/amd64/sys/arch/amd64/compile/POKEY amd64
Architecture: x86_64
Machine: amd64
>Description:
	During system shutdown, immediately before syncing the disks,
	we call suspendsched() to clean up the scheduler queues.  For
	each process, we blindly set its p_stat to SSTOP but we do
	not update the parent's count of stopped children.  There is
	thus a small window where a process (usually, init) can call
	waitpid() with an inconsistent counter.

	There doesn't seem to be much real harm done here, however
	it seems to me that we really should keep the data updated
	correctly.

>How-To-Repeat:
	Found by code inspection while investigating a related issue
	(zombie process never getting reaped).

>Fix:

Index: kern_synch.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_synch.c,v
retrieving revision 1.308
diff -u -p -r1.308 kern_synch.c
--- kern_synch.c	28 Feb 2014 10:16:51 -0000	1.308
+++ kern_synch.c	9 Oct 2015 04:25:18 -0000
@@ -985,7 +985,13 @@ suspendsched(void)
 			continue;
 		}

-		p->p_stat = SSTOP;
+/* XXX PRG */
+		if (p->p_stat != SSTOP) {
+			if (p->p_stat != SZOMB && p->p_stat != SDEAD)
+				p->p_pptr->p_nstopchild++;
+			p->p_stat = SSTOP;
+		}
+/* XXX PRG */

 		LIST_FOREACH(l, &p->p_lwps, l_sibling) {
 			if (l == curlwp)

>Release-Note:

>Audit-Trail:
From: Taylor R Campbell <riastradh@NetBSD.org>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: kern/50318: Process p_nstopchild counter not updated in suspendsched()
Date: Sat, 10 Oct 2015 14:24:53 +0000

 The analysis sounds plausible to me, but it also seems as though we
 ought to

 (a) audit all accounting of p_nstopchild, and
 (b) at least identify in comments, if not formally name, every state
 transition that affects it.

 A cursory examination of the grep of p_nstopchild suggests it counts

 - SSTOP children that have not been waited, and
 - SDEAD/SZOMB children.

 So there are a few state transitions that we must worry about:

 1. Child changes p_stat to {SSTOP, SDEAD, SZOMB}.
 2. Child changes p_stat from {SSTOP, SDEAD, SZOMB}.
 3. Child is waited while stopped.
 4. Child is transferred from one parent to another.

 The following updates of p_nstopchild currently exist:

 kern_exec.c:1289:               p->p_pptr->p_nstopchild++;

    Transition 1, implements proc.12345.stopexec to stop on exec (*),
    subject of PR kern/50289 concerning p_waited.

 kern_exit.c:496:        old_parent->p_nstopchild++;

    Transition 1, when process exits and goes to SDEAD before SZOMB.

    XXX Earlier in exit1, there is logic for proc.12345.stopexit to
    stop on exit (*) which transitions p to SSTOP, but it doesn't
    adjust p_pptr->p_nstopchild.

 kern_exit.c:786:                                        parent->p_nstopchil=
 d--;

    Transition 3, implements the part of wait that picks a child.

 kern_exit.c:886:        parent->p_nstopchild--;

    Transition 2, when zombie process is freed and disassociated from
    its parent.

 kern_exit.c:964:                child->p_pptr->p_nstopchild--;
 kern_exit.c:965:                parent->p_nstopchild++;

    Transition 4, in proc_reparent.

 kern_fork.c:564:                p1->p_nstopchild++;

    Transition 1, implements proc.12345.stopfork to stop on fork (*).

 kern_lwp.c:514:         p->p_pptr->p_nstopchild--;

    Transition 2, restarts stopped lwp.  Not clear to me what the
    original p_stat is, but it is set to SACTIVE, so none of {SSTOP,
    SDEAD, SZOMB}.

 kern_sig.c:1373:                                p->p_pptr->p_nstopchild--;

    Transition 2, restarts stopped process on SIGCONT.

 kern_sig.c:1474:        p->p_pptr->p_nstopchild++;

    Transition 1, stops process on SIGSTOP.

 kern_sig.c:2192:                p->p_pptr->p_nstopchild--;

    Transition 2, restarts stopped process using ptrace.

 (*) It looks like there is some duplicated logic to stop in various
 circumstances for proc.12345.stop{exec,exit,fork}.  The differences
 are worth scrutinizing further, but are not likely to be relevant to
 your unreapable zombie problem.

 The following updates of p_stat currently exist:

 kern_exec.c:1296:               p->p_stat =3D SSTOP;

    Transition 1, proc.12345.stopexec (PR kern/50298), does
    p->p_pptr->p_nstopchild++.

 kern_exec.c:2073:               l->l_proc->p_stat =3D SSTOP;
 kern_exec.c:2144:               l->l_proc->p_stat =3D ostat;

    posix_spawn, no obvious update of l->l_proc->p_pptr->p_nstopchild,
    which looks wrong to me, but it is restored shortly afterward.
    This looks like a sketchy bookkeeping kludge, not actually stopping
    a process.

 kern_exec.c:2552:       p2->p_stat =3D SACTIVE;

    posix_spawn, new process (SIDL), can't be transition 2.

 kern_exit.c:232:                p->p_stat =3D SSTOP;

    Transition 1, proc.12345.stopexit, XXX looks like it's missing
    p->p_pptr->p_nstopchild++ (PR kern/50308).

 kern_exit.c:250:        p->p_stat =3D SDYING;
 kern_exit.c:492:        p->p_stat =3D SDEAD;
 kern_exit.c:556:        p->p_stat =3D SZOMB;

    Transition 1, process exit, does p->p_pptr->p_nstopchild++
    alongside the p->p_stat =3D SDEAD transition.

    XXX I assume it must not be stopped beforehand, so that it is not
    relevant whether p was waited, but I have not proven this
    assumption.

    XXX It does not appear that p->p_stat =3D SDEAD happens with
    p->p_lock held, which looks wrong.  What is the lock order for
    processes p_lock of different processes?  Ancestor first or
    descendant first?

 kern_exit.c:884:        p->p_stat =3D SIDL;               /* not even a zom=
 bie any more */

    Transition 2, proc_free, does p->p_pptr->p_nstopchild--.  Since it
    is always a transition from SZOMB, no need to check whether p is
    waited.

 kern_fork.c:562:                p2->p_stat =3D SSTOP;

    Transition 1, proc.12345.stopfork, does p2->p_pptr->p_nstopchild++.
    Since it is always a new process (SIDL), no need to check whether
    p2 is waited.

 kern_fork.c:570:                p2->p_stat =3D SACTIVE;

    In fork, new process (SIDL), can't be transition 2.

 kern_lwp.c:510: p->p_stat =3D SACTIVE;

    Transition 2, restart stopped lwp, does p->p_pptr->p_nstopchild--
    if p is not waited.

 kern_proc.c:739:        p->p_stat =3D SIDL;                       /* protec=
 t against others */

    Newly allocated process, can't be transition 2.

 kern_sig.c:1370:                        p->p_stat =3D SACTIVE;

    Transition 2, SIGCONT/SIGKILL to continue a stopped process, does
    p->p_pptr->p_nstopchild-- if p is not waited.

 kern_sig.c:1472:        p->p_stat =3D SSTOP;

    Transition 1, stops process on SIGSTOP, does
    p->p_pptr->p_nstopchild++.

 kern_sig.c:2187:        p->p_stat =3D SACTIVE;

    Transition 2, restarts stopped process using ptrace, does
    p->p_pptr->p_nstopchild-- if p is not waited.

 kern_synch.c:988:               p->p_stat =3D SSTOP;

    Transition 1, finally the case you noted here.

 From the above analysis, your patch looks correct to me.  But I think
 we nevertheless ought to write down formal names for all these state
 transitions.

Responsible-Changed-From-To: kern-bug-people->pgoyette
Responsible-Changed-By: pgoyette@NetBSD.org
Responsible-Changed-When: Sun, 11 Oct 2015 23:31:54 +0000
Responsible-Changed-Why:
I have the fix.


From: "Paul Goyette" <pgoyette@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/50318 CVS commit: src/sys/kern
Date: Tue, 13 Oct 2015 00:25:52 +0000

 Module Name:	src
 Committed By:	pgoyette
 Date:		Tue Oct 13 00:25:52 UTC 2015

 Modified Files:
 	src/sys/kern: kern_synch.c

 Log Message:
 When clearing out the scheduler queues during system shutdown, we move
 all processes to the SSTOP state.  Make sure we update each process's
 p_waited and the parents' p_nstopchild counters to maintain consistent
 values.  Should not make any real difference this late in the shutdown
 process, but we should still be consistent just in case.

 Fixes PR kern/50318

 Pullups will be requested for:

        NetBSD-7, -6, -6-0, -6-1, -5, -5-0, -5-1, and -5-2


 To generate a diff of this commit:
 cvs rdiff -u -r1.308 -r1.309 src/sys/kern/kern_synch.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: open->pending-pullups
State-Changed-By: pgoyette@NetBSD.org
State-Changed-When: Tue, 13 Oct 2015 01:35:36 +0000
State-Changed-Why:
Committed to head, pending pullups


State-Changed-From-To: pending-pullups->closed
State-Changed-By: pgoyette@NetBSD.org
State-Changed-When: Sat, 07 Nov 2015 22:17:34 +0000
State-Changed-Why:
Fixed, pullups completed.


>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2014 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.