Subject: bin/25321: sh(1) background job hangs opening a named pipe for stdin
To: None <gnats-bugs@gnats.NetBSD.org>
From: David Young <dyoung@cuw.ojctech.com>
List: netbsd-bugs
Date: 04/25/2004 14:13:48
>Number:         25321
>Category:       bin
>Synopsis:       sh(1) background job hangs opening a named pipe for stdin
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Apr 25 19:14:00 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator:     David Young
>Release:        NetBSD 1.6ZK
>Organization:
OJC Technologies, Urbana, IL
>Environment:
System: NetBSD cuw.ojctech.com 1.6ZK NetBSD 1.6ZK (GENERIC.pcibios) #0: Fri Mar 12 17:59:03 CST 2004 dyoung@cuw.ojctech.com:/u1/dyoung/nbsd/O/sys/arch/i386/compile/GENERIC.pcibios i386
Architecture: i386
Machine: i386
/bin/sh:
     $NetBSD: crt0.c,v 1.13 2003/07/26 19:24:27 salo Exp $
     $NetBSD: alias.c,v 1.12 2003/08/07 09:05:29 agc Exp $
     $NetBSD: cd.c,v 1.34 2003/11/14 20:00:28 dsl Exp $
     $NetBSD: error.c,v 1.31 2003/08/07 09:05:30 agc Exp $
     $NetBSD: eval.c,v 1.75 2003/11/14 10:27:10 dsl Exp $
     $NetBSD: exec.c,v 1.37 2003/08/07 09:05:31 agc Exp $
     $NetBSD: expand.c,v 1.60 2003/12/21 08:32:39 jdolecek Exp $
     $NetBSD: histedit.c,v 1.34 2003/10/27 06:19:29 lukem Exp $
     $NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc Exp $
     $NetBSD: jobs.c,v 1.62 2003/12/18 00:56:05 christos Exp $
     $NetBSD: mail.c,v 1.16 2003/08/07 09:05:33 agc Exp $
     $NetBSD: main.c,v 1.48 2003/09/14 12:09:29 jmmv Exp $
     $NetBSD: memalloc.c,v 1.28 2003/08/07 09:05:34 agc Exp $
     $NetBSD: miscbltin.c,v 1.32 2003/08/07 09:05:35 agc Exp $
     $NetBSD: mystring.c,v 1.16 2003/08/07 09:05:35 agc Exp $
     $NetBSD: options.c,v 1.36 2004/01/05 23:23:32 jmmv Exp $
     $NetBSD: parser.c,v 1.55 2003/08/07 09:05:37 agc Exp $
     $NetBSD: redir.c,v 1.28 2003/08/07 09:05:37 agc Exp $
     $NetBSD: show.c,v 1.26 2003/11/14 10:46:13 dsl Exp $
     $NetBSD: trap.c,v 1.30 2003/08/26 18:13:25 jmmv Exp $
     $NetBSD: output.c,v 1.28 2003/08/07 09:05:36 agc Exp $
     $NetBSD: var.c,v 1.34 2003/08/26 18:14:24 jmmv Exp $
     $NetBSD: test.c,v 1.25 2002/05/25 23:12:16 wiz Exp $
     $NetBSD: printf.c,v 1.29 2003/08/07 11:15:33 agc Exp $
     $NetBSD: kill.c,v 1.23 2003/08/07 09:05:13 agc Exp $
     $NetBSD: skeleton.c,v 1.25 2003/08/07 11:17:54 agc Exp $
     $NetBSD: arith.y,v 1.17 2003/09/17 17:33:36 jmmv Exp $
     $NetBSD: arith_lex.l,v 1.12 2003/08/07 09:05:30 agc Exp $
>Description:
If 'fifo' is a named pipe, a background command like

	program < fifo &

will hang sh(1).  This is new, broken behavior.  It started with
the use of vfork(2) in sh(1).  open(2) blocks when we open a named
pipe that some other process has not already opened.  Used to be
that open(2) was called on the named pipe in a fully-formed child
process created by fork(2).  Now it calls open(2) on the named pipe
in a child process half-formed by vfork(2); the child does not
become independent of its parent until the exec(2).  Since it is
blocked in open(2), the child does not ever get to exec(2).  Since
the parent and child are running in the same process context, they
are both stuck.  (Thanks to Thor and others on icb for interpreting
this mysterious bug.)
>How-To-Repeat:
#!/bin/sh

fifo=$(mktemp -d /var/tmp/$(basename $0).XXXXXX)/fifo
if [ $? -ne 0 -o $fifo = /fifo ]; then
	echo "$(basename $0): could not create tempdir" 1>&2
fi

if ! mkfifo $fifo; then
	echo "$(basename $0): could not create fifo" 1>&2
fi

#
# The script hangs on the following line.  It has just vfork(2)'d.
# It is blocked open(2)'ing $fifo.
#
# cuw:~> ps aux | grep fifotest
# dyoung   21921  0.0  0.5  140   628 pc D+    1:43PM 0:00.00 sh ./fifotest 
# dyoung   28964  0.0  0.5  140   628 pc IV+   1:43PM 0:00.00 sh ./fifotest 
# cuw:~> 
#

sed 's/PacMan/Missile Command/g' < $fifo &

echo "This text does not appear because we're blocked." 1>&2

echo "PacMan is cooler than Donkey Kong." > $fifo

>Fix:
On icb, a few solutions were tossed around.  One is that a child
should tentatively open(2) with O_NONBLOCK.  If the open(2) fails,
then the child should send an indication to the parent and vunfork(2).
(Caveat: we do not have a vunfork(2), yet.)  The parent will retry
with fork(2).  However, if the open(2) succeeds, then the child
should fcntl(, F_SETFL, ~O_NONBLOCK) the descriptor.
>Release-Note:
>Audit-Trail:
>Unformatted: