Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pipe not closed properly #10391

Open
p5pRT opened this issue May 18, 2010 · 5 comments
Open

pipe not closed properly #10391

p5pRT opened this issue May 18, 2010 · 5 comments

Comments

@p5pRT
Copy link

p5pRT commented May 18, 2010

Migrated from rt.perl.org#75172 (status was 'open')

Searchable as RT75172$

@p5pRT
Copy link
Author

p5pRT commented May 18, 2010

From winston@rentec.com

This is a bug report for perl from winston@​rentec.com,
generated with the help of perlbug 1.39 running under perl 5.12.1.

Hello,

I have a possible bug in perl to report. I am on a linux machine running
openSUSE 11.0. The test case for the bug consists of three scripts, go.pl,
send.pl, and recv.pl. Create them in the same directory as follows​:

go.pl​:
#!/home/winston/perl-5.12.1/perl
close STDERR;
close STDOUT;
exec "./send.pl";

send.pl​:
#!/home/winston/perl-5.12.1/perl
open FH, "| ./recv.pl";
close FH;

recv.pl​:
#!/home/winston/perl-5.12.1/perl
my @​message = <STDIN>;

When you run send.pl, the program will exit normally. However, if you run
go.pl, the program will hang. I believe that this is due to a bug in how perl
handles pipes.

When I run send.pl with strace, here is the section that opens and closes the
pipe. The main takeaway is that there are four file descriptors created by
pipe and four calls to close​:

pipe([3, 5]) = 0
pipe([8, 9]) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1e4d313780) = 17130
close(9) = 0
dup2(5, 3) = 3
close(5) = 0
read(8, "", 4) = 0
close(8) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fff5534fb50) = -1 EINVAL (Invalid argument)
lseek(3, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
close(3) = 0

However, when I run go.pl with strace, here is the same section. There are
four file descriptors created by pipe, but only three calls to close​:

pipe([2, 3]) = 0
pipe([5, 8]) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f05e8f79780) = 17141
close(8) = 0
dup2(3, 2) = 2
close(3) = 0
read(5, "", 4) = 0
close(5) = 0
ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7ffff0fb47b0) = -1 EINVAL (Invalid argument)
lseek(2, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
fcntl(2, F_SETFD, 0) = 0

There is no call to close on file descriptor 2. My guess is that there is
some part of the code that wants to treat STDERR as a special case, but it's
not aware that STDERR is no longer bound to file descriptor 2 anymore.

Thanks,
Winston


Flags​:
  category=core
  severity=low


Site configuration information for perl 5.12.1​:

Configured by winston at Tue May 18 12​:22​:15 EDT 2010.

Summary of my perl5 (revision 5 version 12 subversion 1) configuration​:
 
  Platform​:
  osname=linux, osvers=2.6.25.20-0.4-default, archname=x86_64-linux
  uname='linux bentgrass 2.6.25.20-0.4-default #1 smp 2009-06-01 09​:57​:12 +0200 x86_64 x86_64 x86_64 gnulinux '
  config_args='-de -Dnoextensions=ODBM_File'
  hint=recommended, useposix=true, d_sigaction=define
  useithreads=undef, usemultiplicity=undef
  useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
  use64bitint=define, use64bitall=define, uselongdouble=undef
  usemymalloc=n, bincompat5005=undef
  Compiler​:
  cc='cc', ccflags ='-fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
  optimize='-O2',
  cppflags='-fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
  ccversion='', gccversion='4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036]', gccosandvers=''
  intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
  d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
  ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
  alignbytes=8, prototype=define
  Linker and Libraries​:
  ld='cc', ldflags =' -fstack-protector -L/usr/local/lib'
  libpth=/usr/local/lib /lib /usr/lib /lib64 /usr/lib64
  libs=-lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lc -lgdbm_compat
  perllibs=-lnsl -ldl -lm -lcrypt -lutil -lc
  libc=/lib/libc-2.8.so, so=so, useshrplib=false, libperl=libperl.a
  gnulibc_version='2.8'
  Dynamic Linking​:
  dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
  cccdlflags='-fPIC', lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector'

Locally applied patches​:
 


@​INC for perl 5.12.1​:
  /home/winston/perl-5.12.1/lib/
  /usr/local/lib/perl5/site_perl/5.12.1/x86_64-linux
  /usr/local/lib/perl5/site_perl/5.12.1
  /usr/local/lib/perl5/5.12.1/x86_64-linux
  /usr/local/lib/perl5/5.12.1
  .


Environment for perl 5.12.1​:
  HOME=/home/winston
  LANG=en_US.UTF-8
  LANGUAGE (unset)
  LD_LIBRARY_PATH (unset)
  LOGDIR (unset)
  PATH=/opt/kde3/bin​:/home/winston/bin​:/usr/local/bin​:/usr/bin​:/bin​:/usr/bin/X11​:/usr/X11R6/bin​:/usr/games​:/usr/lib/mit/bin​:/usr/lib/mit/sbin​:/usr/lib/qt3/bin
  PERL_BADLANG (unset)
  SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented May 19, 2010

From @Tux

On Tue, 18 May 2010 11​:40​:22 -0700, "winston@​rentec.com (via RT)"
<perlbug-followup@​perl.org> wrote​:

Hello,

I have a possible bug in perl to report. I am on a linux machine running
openSUSE 11.0. The test case for the bug consists of three scripts, go.pl,
send.pl, and recv.pl. Create them in the same directory as follows​:

go.pl​:
#!/home/winston/perl-5.12.1/perl
close STDERR;
close STDOUT;
exec "./send.pl";

send.pl​:
#!/home/winston/perl-5.12.1/perl
open FH, "| ./recv.pl";
close FH;

recv.pl​:
#!/home/winston/perl-5.12.1/perl
my @​message = <STDIN>;

When you run send.pl, the program will exit normally. However, if you run
go.pl, the program will hang.

I just tried exactly above on OpenSUSE 11.2/64 with
This is perl 5, version 12, subversion 1 (v5.12.1) built for x86_64-linux-ld

I believe that this is due to a bug in how perl handles pipes.

When I run send.pl with strace, here is the section that opens and closes the
pipe. The main takeaway is that there are four file descriptors created by
pipe and four calls to close​:

pipe([3, 5]) = 0
pipe([8, 9]) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1e4d313780) = 17130
close(9) = 0
dup2(5, 3) = 3
close(5) = 0
read(8, "", 4) = 0
close(8) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fff5534fb50) = -1 EINVAL (Invalid argument)
lseek(3, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
close(3) = 0

close(3) = 0
pipe([3, 4]) = 0
pipe([5, 6]) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f26fef057c0) = 15717
close(6) = 0
dup2(4, 3) = 3
close(4) = 0
read(5, "\r\0\0\0", 4) = 4
close(5) = 0
--- SIGCHLD (Child exited) @​ 0 (0) ---
close(3) = 0

However, when I run go.pl with strace, here is the same section. There are
four file descriptors created by pipe, but only three calls to close​:

pipe([2, 3]) = 0
pipe([5, 8]) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f05e8f79780) = 17141
close(8) = 0
dup2(3, 2) = 2
close(3) = 0
read(5, "", 4) = 0
close(5) = 0
ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7ffff0fb47b0) = -1 EINVAL (Invalid argument)
lseek(2, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
fcntl(2, F_SETFD, 0) = 0

$ strace -f perl go.pl

pipe([2, 3]) = 0
pipe([4, 5]) = 0
clone(Process 16772 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f0a11e587c0) = 16772
[pid 16771] close(5) = 0
[pid 16771] dup2(3, 2 <unfinished ...>
[pid 16772] close(4 <unfinished ...>
[pid 16771] <... dup2 resumed> ) = 2
[pid 16772] <... close resumed> ) = 0
[pid 16771] close(3) = 0
[pid 16772] fcntl(5, F_SETFD, FD_CLOEXEC <unfinished ...>
[pid 16771] read(4, <unfinished ...>
[pid 16772] <... fcntl resumed> ) = 0
[pid 16772] dup2(2, 0) = 0
[pid 16772] close(2) = 0
[pid 16772] close(3) = 0
[pid 16772] rt_sigaction(SIGFPE, {SIG_DFL, [], SA_RESTORER, 0x7f0a10ea8560}, {0x1, [FPE], SA_RESTORER|SA_RESTART, 0x7f0a10ea8560}, 8) = 0
[pid 16772] execve("./recv.pl", ["./recv.pl"], [/* 83 vars */] <unfinished ...>
[pid 16771] <... read resumed> "", 4) = 0
[pid 16771] close(4) = 0
[pid 16771] ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fffa5f0bc40) = -1 EINVAL (Invalid argument)
[pid 16771] lseek(2, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
[pid 16771] fcntl(2, F_SETFD, 0) = 0

or, without -f, after I break go.pl

pipe([2, 3]) = 0
pipe([4, 5]) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f131ab8c7c0) = 17420
close(5) = 0
dup2(3, 2) = 2
close(3) = 0
read(4, "", 4) = 0
close(4) = 0
ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fff86603a90) = -1 EINVAL (Invalid argument)
lseek(2, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
fcntl(2, F_SETFD, 0) = 0

There is no call to close on file descriptor 2. My guess is that there is
some part of the code that wants to treat STDERR as a special case, but it's
not aware that STDERR is no longer bound to file descriptor 2 anymore.

--
H.Merijn Brand http​://tux.nl Perl Monger http​://amsterdam.pm.org/
using 5.00307 through 5.12 and porting perl5.13.x on HP-UX 10.20, 11.00,
11.11, 11.23, and 11.31, OpenSuSE 10.3, 11.0, and 11.1, AIX 5.2 and 5.3.
http​://mirrors.develooper.com/hpux/ http​://www.test-smoke.org/
http​://qa.perl.org http​://www.goldmark.org/jeff/stupid-disclaimers/

@p5pRT
Copy link
Author

p5pRT commented May 19, 2010

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented May 19, 2010

From @Tux

On Wed, 19 May 2010 17​:51​:09 +0200, "H.Merijn Brand"
<h.m.brand@​xs4all.nl> wrote​:

When you run send.pl, the program will exit normally. However, if you run
go.pl, the program will hang.

I just tried exactly above on OpenSUSE 11.2/64 with
This is perl 5, version 12, subversion 1 (v5.12.1) built for x86_64-linux-ld

FWIW this is not a regression, 5.10.0 acts the same

--
H.Merijn Brand http​://tux.nl Perl Monger http​://amsterdam.pm.org/
using 5.00307 through 5.12 and porting perl5.13.x on HP-UX 10.20, 11.00,
11.11, 11.23, and 11.31, OpenSuSE 10.3, 11.0, and 11.1, AIX 5.2 and 5.3.
http​://mirrors.develooper.com/hpux/ http​://www.test-smoke.org/
http​://qa.perl.org http​://www.goldmark.org/jeff/stupid-disclaimers/

@p5pRT
Copy link
Author

p5pRT commented May 19, 2010

From zefram@fysh.org

winston@​rentec.com wrote​:

close STDERR;
close STDOUT;
exec "./send.pl";

You can't expect this to act sensibly. You're violating the Unix
convention of having file descriptors 0, 1, and 2 open and having
specific semantics. Perl builds that convention into the language,
so its scope to cope with violations is limited.

A possible approach in Perl would be to check whether any newly opened
file descriptor is in the range [0,2], and if so then move it (via dup)
out of that range. So a closed standard error would remain closed despite
later I/O activity. However, this runs into atomicity problems, and we'd
have to make a decision about which I/O operators do this and which don't.
(Perl's open() should, but the ones in POSIX had better not.)

-zefram

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants