Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.91k stars 542 forks source link

signal handler can clobber $! #7235

Closed p5pRT closed 12 years ago

p5pRT commented 20 years ago

Migrated from rt.perl.org#28824 (status was 'resolved')

Searchable as RT28824$

p5pRT commented 20 years ago

From zefram@fysh.org

Created by zefram@fysh.org

If a signal is caught\, and interrupts a system call\, and the Perl signal handler modifies $!\, then this replaces the EINTR from the system call. Simple test case​:

BEGIN t0\<\<\< #!/usr/bin/perl

use warnings; use strict;

$SIG{TSTP} = sub { print "caught SIGTSTP\n"; $! = 0; };

my $a; my $n = sysread(STDIN\, $a\, 1); print "sysread returned "\, defined($n) ? $n : "undef"\,   "; errno = "\, 0+$!\, " ("\, $!\, ")\n"; exit 0;

END t0\<\<\<

The program attempts to read one character\, and just displays what it gets back from sysread. To perform the test\, run the program from a terminal and ^Z while it's waiting for input. I get this behaviour​:

$ ./t0 caught SIGTSTP sysread returned undef; errno = 0 () $

This is pretty surprising behaviour to the code that was calling sysread; returning undef with $! == 0 should be impossible.

The behaviour is perfectly understandable\, and probably someone is going to argue that it's correct. But it's inconsistent with the behaviour one gets in C. Here's the equivalent C program​:

BEGIN t1.c\<\<\< #include \<stdio.h> #include \<stdlib.h> #include \<signal.h> #include \<unistd.h> #include \<errno.h>

void sigtstp(int signum) {   printf("caught SIGTSTP\n");   errno = 0; }

int main(void) {   struct sigaction act;   char c;   int n;   act.sa_handler = sigtstp;   sigemptyset(&act.sa_mask);   act.sa_flags = 0;   if(sigaction(SIGTSTP\, &act\, NULL)) {   perror("sigaction");   exit(1);   }   n = read(0\, &c\, 1);   printf("read returned %d; errno = %d (%s)\n"\, n\,   errno\, strerror(errno));   exit(0); }

END t1.c\<\<\<

$ ./t1 caught SIGTSTP read returned -1; errno = 4 (Interrupted system call) $

Obviously the difference is just a question of where errno/$! is set. In C\, read() sets errno just before it returns\, whereas in Perl sysread() has errno set internally and then does other things\, such as invoking the signal handler\, before returning. I argue that $! should be saved and restored in this case\, so that $! *at the time that sysread() returns* accurately reflects the result of the system call.

Code such as I showed above still needs to save and restore errno/$! manually in the signal handler in order to cover all cases​: being suspended immediately after [sys]read() returns and before errno/$! has been read is a possibility. However\, there are other situations where the order of events can be guaranteed\, and in such cases I think Perl should match the C semantics that it appears to offer.

Perl Info ``` Flags: category=core severity=medium Site configuration information for perl v5.8.3: Configured by Debian Project at Sat Mar 27 17:07:14 EST 2004. Summary of my perl5 (revision 5.0 version 8 subversion 3) configuration: Platform: osname=linux, osvers=2.4.25-ti1211, archname=i386-linux-thread-multi uname='linux kosh 2.4.25-ti1211 #1 thu feb 19 18:20:12 est 2004 i686 gnulinux ' config_args='-Dusethreads -Duselargefiles -Dccflags=-DDEBIAN -Dcccdlflags=-fPIC -Darchname=i386-linux -Dprefix=/usr -Dprivlib=/usr/share/perl/5.8 -Darchlib=/usr/lib/perl/5.8 -Dvendorprefix=/usr -Dvendorlib=/usr/share/perl5 -Dvendorarch=/usr/lib/perl5 -Dsiteprefix=/usr/local -Dsitelib=/usr/local/share/perl/5.8.3 -Dsitearch=/usr/local/lib/perl/5.8.3 -Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/share/man/man3 -Dsiteman1dir=/usr/local/man/man1 -Dsiteman3dir=/usr/local/man/man3 -Dman1ext=1 -Dman3ext=3perl -Dpager=/usr/bin/sensible-pager -Uafs -Ud_csh -Uusesfio -Uusenm -Duseshrplib -Dlibperl=libperl.so.5.8.3 -Dd_dosuid -des' hint=recommended, useposix=true, d_sigaction=define usethreads=define use5005threads=undef useithreads=define usemultiplicity=define useperlio=define d_sfio=undef uselargefiles=define usesocks=undef use64bitint=undef use64bitall=undef uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBIAN -fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64', optimize='-O3', cppflags='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBIAN -fno-strict-aliasing -I/usr/local/include' ccversion='', gccversion='3.3.3 (Debian 20040314)', gccosandvers='' intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8 alignbytes=4, prototype=define Linker and Libraries: ld='cc', ldflags =' -L/usr/local/lib' libpth=/usr/local/lib /lib /usr/lib libs=-lgdbm -lgdbm_compat -ldb -ldl -lm -lpthread -lc -lcrypt perllibs=-ldl -lm -lpthread -lc -lcrypt libc=/lib/libc-2.3.2.so, so=so, useshrplib=true, libperl=libperl.so.5.8.3 gnulibc_version='2.3.2' Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-rdynamic' cccdlflags='-fPIC', lddlflags='-shared -L/usr/local/lib' Locally applied patches: @INC for perl v5.8.3: /etc/perl /usr/local/lib/perl/5.8.3 /usr/local/share/perl/5.8.3 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.8 /usr/share/perl/5.8 /usr/local/lib/site_perl . Environment for perl v5.8.3: HOME=/home/zefram LANG (unset) LANGUAGE (unset) LD_LIBRARY_PATH (unset) LOGDIR (unset) PATH=/home/zefram/pub/i686-pc-linux-gnu/bin:/home/zefram/pub/common/bin:/usr/bin:/usr/X11R6/bin:/bin:/usr/local/bin:/usr/games:/opt/libunsn/bin PERL_BADLANG (unset) SHELL=/usr/bin/zsh ```
p5pRT commented 17 years ago

From @smpeters

On Thu Apr 15 06​:25​:56 2004\, zefram \<!-- x --> at fysh.org wrote​:

This is a bug report for perl from zefram \<!-- x --> at fysh.org\, generated with the help of perlbug 1.34 running under perl v5.8.3.

----------------------------------------------------------------- [Please enter your report here]

If a signal is caught\, and interrupts a system call\, and the Perl signal handler modifies $!\, then this replaces the EINTR from the system call. Simple test case​:

BEGIN t0\<\<\< #!/usr/bin/perl

use warnings; use strict;

$SIG{TSTP} = sub { print "caught SIGTSTP\n"; $! = 0; };

my $a; my $n = sysread(STDIN\, $a\, 1); print "sysread returned "\, defined($n) ? $n : "undef"\, "; errno = "\, 0+$!\, " ("\, $!\, ")\n"; exit 0;

END t0\<\<\<

The program attempts to read one character\, and just displays what it gets back from sysread. To perform the test\, run the program from a terminal and ^Z while it's waiting for input. I get this behaviour​:

$ ./t0 caught SIGTSTP sysread returned undef; errno = 0 () $

This is pretty surprising behaviour to the code that was calling sysread; returning undef with $! == 0 should be impossible.

The behaviour is perfectly understandable\, and probably someone is going to argue that it's correct. But it's inconsistent with the behaviour one gets in C. Here's the equivalent C program​:

The problem is that by the time $! is displayed at the end\, several other operations have occurred. $! has to be checked as soon as possible or you risk having it cleared out. Looking at this\, I'm thinking that this isn't a bug. Any other opinions?

p5pRT commented 17 years ago

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

p5pRT commented 17 years ago

From nospam-abuse@bloodgate.com

-----BEGIN PGP SIGNED MESSAGE----- Hash​: SHA1

Moin\,

On Tuesday 24 July 2007 23​:23​:29 Steve Peters via RT wrote​:

On Thu Apr 15 06​:25​:56 2004\, zefram \<!-- x --> at fysh.org wrote​:

This is a bug report for perl from zefram \<!-- x --> at fysh.org\, generated with the help of perlbug 1.34 running under perl v5.8.3.

----------------------------------------------------------------- [Please enter your report here]

If a signal is caught\, and interrupts a system call\, and the Perl signal handler modifies $!\, then this replaces the EINTR from the system call.

Simple test case​:

BEGIN t0\<\<\<

#!/usr/bin/perl

use warnings; use strict;

$SIG{TSTP} = sub { print "caught SIGTSTP\n"; $! = 0; };

my $a; my $n = sysread(STDIN\, $a\, 1); print "sysread returned "\, defined($n) ? $n : "undef"\, "; errno = "\, 0+$!\, " ("\, $!\, ")\n"; exit 0;

END t0\<\<\<

The program attempts to read one character\, and just displays what it gets back from sysread. To perform the test\, run the program from a terminal and ^Z while it's waiting for input. I get this behaviour​:

$ ./t0 caught SIGTSTP sysread returned undef; errno = 0 () $

This is pretty surprising behaviour to the code that was calling sysread; returning undef with $! == 0 should be impossible.

The behaviour is perfectly understandable\, and probably someone is going to argue that it's correct. But it's inconsistent with the behaviour one gets in C. Here's the equivalent C program​:

The problem is that by the time $! is displayed at the end\, several other operations have occurred.

I am not sure what other operations you mean. This modified program​:

  #!/usr/bin/perl  
  use warnings;   use strict;  
  $SIG{TSTP} = sub { print "caught SIGTSTP\n"; $! = 0; };  
  my $a;   my $n = sysread(STDIN\, $a\, 1); my $b = $! + 0; my $c = "$!";   print "\$!​: " . $b . "$c\n";   print "sysread returned "\, defined($n) ? $n : "undef"\,   "; errno = "\, 0+$!\, " ("\, $!\, ")\n";   exit 0;

prints on Linux\, Perl v5.8.8​:

  linux​:/home/te # perl t.pl   caught SIGTSTP   $!​: 0   sysread returned undef; errno = 0 ()

I can't think of a way to check $! earlier.

All the best\,

Tels

- -- Signed on Tue Jul 24 23​:35​:01 2007 with key 0x93B84C15. View my photo gallery​: http​://bloodgate.com/photos PGP key on http​://bloodgate.com/tels.asc or per email.

"It is true that some lawyers are dishonest\, arrogant\, greedy\, venal\, amoral\, ruthless buckets of slime. On the other hand\, it is unfair to judge the entire profession by a few hundred-thousand bad apples."

  -- The Washington Post -----BEGIN PGP SIGNATURE----- Version​: GnuPG v1.4.2 (GNU/Linux)

iQEVAwUBRqZxJncLPEOTuEwVAQKn8gf6A0I1wQtR6M9lGSyRjsaL7VtO4hDBeOv9 icbS5jBPcyWSFkovlJK6r2T95vKbwAY9Hxd5eqz1yZvplM3TrRUXZ8IVk0oYGvc3 zOEbNs3Walco1hOP+RgqZLuSgv9fXUgfaIzArc2NQ6T39c8vyZIs1mDtx/UkbqBJ ePPk77OtGx/2Xg+s82IH1xd6jxlYTnt19yDgpoA8npbr380X4tWCmiqEKPcQUvus 7zdeMdNI6tm61flh2OAOFWmSiRyobliOvzRKawHJXw2QAbq2TPONkp1dZxEyWajl sV6RyGKafO9jF/JH0ady7YtEN0lxGSw+Tku1Ij6pQTytZ3SkKfkU3Q== =Lkt2 -----END PGP SIGNATURE-----

p5pRT commented 17 years ago

From mark@mark.mielke.cc

Tels wrote​:

-----BEGIN PGP SIGNED MESSAGE----- Hash​: SHA1

Moin\,

On Tuesday 24 July 2007 23​:23​:29 Steve Peters via RT wrote​:

On Thu Apr 15 06​:25​:56 2004\, zefram \<!-- x --> at fysh.org wrote​:

$SIG{TSTP} = sub { print "caught SIGTSTP\n"; $! = 0; }; my $a; my $n = sysread(STDIN\, $a\, 1); print "sysread returned "\, defined($n) ? $n : "undef"\, "; errno = "\, 0+$!\, " ("\, $!\, ")\n"; exit 0;

The problem is that by the time $! is displayed at the end\, several other operations have occurred.

I believe the correct behaviour would be for sysread() to return EINTR. Although it is true that he over-writes errno twice in the signal handler (both the print which turns into a successful write() and the explicit overwrite of $!)\, this occurs in a separate signal call stack on UNIX. The sysread() itself is interrupted when the signal returns. I tested removing the explicit assignment and found​:

$ perl -e '   $SIG{"TSTP"} = sub { print "caught SIGTSTP\n"; };
  my $a;   my $n = sysread(STDIN\, $a\, 1);   print "sysread return "\, defined($n) ? $n : "undef"\,   "; errno = "\, 0+$!\, " ("\, $!\, ")\n"; ' caught SIGTSTP sysread return undef; errno = 4 (Interrupted system call)

This leads me to believe that it is the explicit assignment to the Perl $! that is causing a problem. To be sure​:

$ perl -e '   $SIG{"TSTP"} = sub { $! = 0 };   my $a; my $n = sysread(STDIN\, $a\, 1);   print "sysread() == "\, defined($n) ? $n : "undef"\,   "; errno = "\, 0+$!\, " ("\, $!\, ")\n"; ' sysread() == undef; errno = 0 ()

And to be sure this has to do with signals and assignment to $!​: $ perl -e '   $! = 0; $SIG{"TSTP"} = sub {};   my $a; my $n = sysread(STDIN\, $a\, 1);   print "sysread() == "\, defined($n) ? $n : "undef"\,   "; errno = "\, 0+$!\, " ("\, $!\, ")\n"; ' sysread() == undef; errno = 4 (Interrupted system call)

So - on the surface\, it does look like there is some sort of bug. Perhaps $! has the wrong reference count\, or the errno magic is disabled in some way?

However!

I do not believe C promises you the ability to set the value of errno. The expectation that you can set errno\, or specifically\, set errno in a signal handler\, and be certain that it will have no effect on the caller that may or may not currently be in a system call\, is wrong. What are you trying to do? Why do you expect assignment of errno in a signal handler to be ignored in this case?

Cheers\, mark

-- Mark Mielke \mark@&#8203;mielke\.cc

p5pRT commented 17 years ago

From @gbarr

On Jul 24\, 2007\, at 9​:06 PM\, Mark Mielke wrote​:

Tels wrote​:

-----BEGIN PGP SIGNED MESSAGE----- Hash​: SHA1

Moin\,

On Tuesday 24 July 2007 23​:23​:29 Steve Peters via RT wrote​:

On Thu Apr 15 06​:25​:56 2004\, zefram \<!-- x --> at fysh.org wrote​:

$SIG{TSTP} = sub { print "caught SIGTSTP\n"; $! = 0; }; my $a; my $n = sysread(STDIN\, $a\, 1); print "sysread returned "\, defined($n) ? $n : "undef"\, "; errno = "\, 0+$!\, " ("\, $!\, ")\n"; exit 0;

The problem is that by the time $! is displayed at the end\, several other operations have occurred.

I believe the correct behaviour would be for sysread() to return
EINTR.

Yes

Although it is true that he over-writes errno twice in the signal
handler (both the print which turns into a successful write() and
the explicit overwrite of $!)\, this occurs in a separate signal
call stack on UNIX. The sysread() itself is interrupted when the
signal returns. I tested removing the explicit assignment and found​:

UNIX signal handlers are run on a separate stack and are called
before sysread sets errno. But signal handlers in perl are not called
from the C signal handler. We used to do it that way\, but that causes
all sorts of stability problems because it is run in a separate
stack. So instead the C signal handler remembers it saw a signal and
the perl signal handler is run after the OP has completed (search
archives for "safe signals"). So sysread has returned and errno is
set before the perl handler is run.

To fix this despatch_signals in mg.c would need to save and restore
errno.

I do not believe C promises you the ability to set the value of
errno. The expectation that you can set errno\, or specifically\, set
errno in a signal handler\, and be certain that it will have no
effect on the caller that may or may not currently be in a system
call\, is wrong.

You can always write to errno\, you just cannot be sure when it will
get overwritten. And in the case of a signal handler\, the system call that got interrupted will probably overwrite
it after the handler returns.

Graham.

p5pRT commented 15 years ago

From perlbug@plan9.de

Created by perlbug@plan9.de

Hi!

Today\, I wondered whether I should localise $! in my signal handler in perl.

I looked through 5.10 sources\, and think that perl simply destroys errno in many occasions\, and should probably save and restore errno.

Please note that I have not *verified* anything of this :)

For example\, the Perl_csighandler calls "rsignal" which might destroy errno\, and croak\, which might do the same.

The Perl_sighandler might do I/O (for warn)\, again destroying errno.

While this is probably not a big deal in most cases\, in some cases\, this cna lead to hard to debug problems\, I think. Also\, most perl signal handlers do not seem to care about $!\, so saving/restoring errno (which is standard practise otherwise if you do syscalls) might be a good thing in both Perl_sighandler and Perl_csighandler.

Just my 0.02€

Perl Info ``` Flags: category=core severity=wishlist Site configuration information for perl 5.10.0: Configured by Marc Lehmann at Sat Feb 21 02:30:27 CET 2009. Summary of my perl5 (revision 5 version 10 subversion 0) configuration: Platform: osname=linux, osvers=2.6.24-etchnhalf.1-amd64, archname=amd64-linux uname='linux cerebro 2.6.24-etchnhalf.1-amd64 #1 smp mon jul 21 10:36:02 utc 2008 x86_64 gnulinux ' config_args='-Duselargefiles -Dxxxxuse64bitint -Uuse64bitall -Dusemymalloc=n -Dcc=gcc -Dccflags=-ggdb -gdwarf-2 -g3 -Dcppflags=-DPERL_ARENA_SIZE=16368 -D_GNU_SOURCE -I/opt/include -Doptimize=-O6 -msse2 -funroll-loops -fno-strict-aliasing -Dcccdlflags=-fPIC -Dldflags=-L/opt/perl/lib -L/opt/lib -Dlibs=-ldl -lm -lcrypt -Darchname=amd64-linux -Dprefix=/opt/perl -Dprivlib=/opt/perl/lib/perl5 -Darchlib=/opt/perl/lib/perl5 -Dvendorprefix=/opt/perl -Dvendorlib=/opt/perl/lib/perl5 -Dvendorarch=/opt/perl/lib/perl5 -Dsiteprefix=/opt/perl -Dsitelib=/opt/perl/lib/perl5 -Dsitearch=/opt/perl/lib/perl5 -Dsitebin=/opt/perl/bin -Dman1dir=/opt/perl/man/man1 -Dman3dir=/opt/perl/man/man3 -Dsiteman1dir=/opt/perl/man/man1 -Dsiteman3dir=/opt/perl/man/man3 -Dman1ext=1 -Dman3ext=3 -Dpager=/usr/bin/less -Uafs -Uusesfio -Uusenm -Uuseshrplib -Dd_dosuid -Dusethreads=undef -Duse5005threads=undef -Duseithreads=undef -Dusemultiplicity=undef -Demail=perl-binary@plan9.de -Dcf_email=perl-binary@plan9.de -Dcf_by=Marc Lehmann -Dlocincpth=/opt/perl/include /opt/include -Dmyhostname=localhost -Dmultiarch=undef -Dbin=/opt/perl/bin -Dxxxusedevel -DxxxDEBUGGING -Dxxxuse_debugging_perl -Dxxxuse_debugmalloc -des' hint=recommended, useposix=true, d_sigaction=define useithreads=undef, usemultiplicity=undef useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef use64bitint=define, use64bitall=undef, uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='gcc', ccflags ='-ggdb -gdwarf-2 -g3 -fno-strict-aliasing -pipe -I/opt/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64', optimize='-O6 -msse2 -funroll-loops -fno-strict-aliasing', cppflags='-DPERL_ARENA_SIZE=16368 -D_GNU_SOURCE -I/opt/include -ggdb -gdwarf-2 -g3 -fno-strict-aliasing -pipe -I/opt/include' ccversion='', gccversion='4.3.2', 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='gcc', ldflags ='-L/opt/perl/lib -L/opt/lib -L/usr/local/lib' libpth=/usr/local/lib /lib /usr/lib /lib64 /usr/lib64 libs=-ldl -lm -lcrypt perllibs=-ldl -lm -lcrypt libc=/lib/libc-2.7.so, so=so, useshrplib=false, libperl=libperl.a gnulibc_version='2.7' Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E' cccdlflags='-fPIC', lddlflags='-shared -O6 -msse2 -funroll-loops -fno-strict-aliasing -L/opt/perl/lib -L/opt/lib -L/usr/local/lib' Locally applied patches: http://public.activestate.com/cgi-bin/perlbrowse/p/34209 http://public.activestate.com/cgi-bin/perlbrowse/p/34507 http://www.gossamer-threads.com/lists/perl/porters/232549 embed.fnc:Perl_vcroak NULLOK @INC for perl 5.10.0: /root/src/sex /opt/perl/lib/perl5 /opt/perl/lib/perl5 /opt/perl/lib/perl5 /opt/perl/lib/perl5 /opt/perl/lib/perl5 . Environment for perl 5.10.0: HOME=/root LANG (unset) LANGUAGE (unset) LC_CTYPE=en_US.UTF-8 LD_LIBRARY_PATH (unset) LOGDIR (unset) PATH=/root/s2:/root/s:/opt/bin:/opt/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11/bin:/usr/games:/usr/local/bin:/usr/local/sbin:/root/pserv:. PERL5LIB=/root/src/sex PERL5_CPANPLUS_CONFIG=/root/.cpanplus/config PERLDB_OPTS=ornaments=0 PERL_ANYEVENT_EDNS0=1 PERL_ANYEVENT_NET_TESTS=1 PERL_ANYEVENT_PROTOCOLS=ipv4,ipv6 PERL_ANYEVENT_STRICT=1 PERL_BADLANG (unset) PERL_UNICODE=0 SHELL=/bin/bash ```
p5pRT commented 14 years ago

From lubo.rintel@gooddata.com

This is probably the same issue as this patch deals with​:

http​://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2010-02/msg00060.html

p5pRT commented 14 years ago

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

p5pRT commented 14 years ago

From schmorp@schmorp.de

On Thu\, Feb 04\, 2010 at 04​:58​:19AM -0800\, Lubomir Rintel via RT \perlbug\-followup@&#8203;perl\.org wrote​:

This is probably the same issue as this patch deals with​:

http​://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2010-02/msg00060.html

nope\, but similar. localising $! is not enough\, as the (C) signal handler itself can change errno.

(and the dSAVE_ERRNO should be done outside the loop\, anyways)

it surely helps in general\, of course\, but the issue the patch works around can be fixed from perl (it's a faster local $!)\, while the thing I reported cannot (Perl_csighandler is called asynchronously and does not call perl).

--   The choice of a Deliantra\, the free code+content MORPG   -----==- _GNU_ http​://www.deliantra.net   ----==-- _ generation   ---==---(_)__ __ ____ __ Marc Lehmann   --==---/ / _ \/ // /\ \/ / schmorp@​schmorp.de   -=====/_/_//_/\_\,_/ /_/\_\

p5pRT commented 12 years ago

From @Leont

On Thu Apr 15 06​:25​:56 2004\, zefram@​fysh.org wrote​:

If a signal is caught\, and interrupts a system call\, and the Perl signal handler modifies $!\, then this replaces the EINTR from the system call. Simple test case​:

BEGIN t0\<\<\< #!/usr/bin/perl

use warnings; use strict;

$SIG{TSTP} = sub { print "caught SIGTSTP\n"; $! = 0; };

my $a; my $n = sysread(STDIN\, $a\, 1); print "sysread returned "\, defined($n) ? $n : "undef"\, "; errno = "\, 0+$!\, " ("\, $!\, ")\n"; exit 0;

END t0\<\<\<

The program attempts to read one character\, and just displays what it gets back from sysread. To perform the test\, run the program from a terminal and ^Z while it's waiting for input. I get this behaviour​:

$ ./t0 caught SIGTSTP sysread returned undef; errno = 0 () $

This is pretty surprising behaviour to the code that was calling sysread; returning undef with $! == 0 should be impossible.

The behaviour is perfectly understandable\, and probably someone is going to argue that it's correct. But it's inconsistent with the behaviour one gets in C. Here's the equivalent C program​:

BEGIN t1.c\<\<\< #include \<stdio.h> #include \<stdlib.h> #include \<signal.h> #include \<unistd.h> #include \<errno.h>

void sigtstp(int signum) { printf("caught SIGTSTP\n"); errno = 0; }

int main(void) { struct sigaction act; char c; int n; act.sa_handler = sigtstp; sigemptyset(&act.sa_mask); act.sa_flags = 0; if(sigaction(SIGTSTP\, &act\, NULL)) { perror("sigaction"); exit(1); } n = read(0\, &c\, 1); printf("read returned %d; errno = %d (%s)\n"\, n\, errno\, strerror(errno)); exit(0); }

END t1.c\<\<\<

$ ./t1 caught SIGTSTP read returned -1; errno = 4 (Interrupted system call) $

Obviously the difference is just a question of where errno/$! is set. In C\, read() sets errno just before it returns\, whereas in Perl sysread() has errno set internally and then does other things\, such as invoking the signal handler\, before returning. I argue that $! should be saved and restored in this case\, so that $! *at the time that sysread() returns* accurately reflects the result of the system call.

Code such as I showed above still needs to save and restore errno/$! manually in the signal handler in order to cover all cases​: being suspended immediately after [sys]read() returns and before errno/$! has been read is a possibility. However\, there are other situations where the order of events can be guaranteed\, and in such cases I think Perl should match the C semantics that it appears to offer.

This bug seems to be fixed in blead\, probably by d0166017.

Leon

p5pRT commented 12 years ago

From @Leont

On Wed Apr 01 08​:55​:28 2009\, perlbug@​plan9.de wrote​:

This is a bug report for perl from perlbug@​plan9.de\, generated with the help of perlbug 1.36 running under perl 5.10.0.

----------------------------------------------------------------- [Please enter your report here]

Hi!

Today\, I wondered whether I should localise $! in my signal handler in perl.

I looked through 5.10 sources\, and think that perl simply destroys errno in many occasions\, and should probably save and restore errno.

Please note that I have not *verified* anything of this :)

For example\, the Perl_csighandler calls "rsignal" which might destroy errno\, and croak\, which might do the same.

The Perl_sighandler might do I/O (for warn)\, again destroying errno.

While this is probably not a big deal in most cases\, in some cases\, this cna lead to hard to debug problems\, I think. Also\, most perl signal handlers do not seem to care about $!\, so saving/restoring errno (which is standard practise otherwise if you do syscalls) might be a good thing in both Perl_sighandler and Perl_csighandler.

Just my 0.02€

[Please do not change anything below this line] ----------------------------------------------------------------- --- Flags​: category=core severity=wishlist --- Site configuration information for perl 5.10.0​:

Configured by Marc Lehmann at Sat Feb 21 02​:30​:27 CET 2009.

Summary of my perl5 (revision 5 version 10 subversion 0) configuration​: Platform​: osname=linux\, osvers=2.6.24-etchnhalf.1-amd64\, archname=amd64- linux uname='linux cerebro 2.6.24-etchnhalf.1-amd64 #1 smp mon jul 21 10​:36​:02 utc 2008 x86_64 gnulinux ' config_args='-Duselargefiles -Dxxxxuse64bitint -Uuse64bitall -Dusemymalloc=n -Dcc=gcc -Dccflags=-ggdb -gdwarf-2 -g3 -Dcppflags=-DPERL_ARENA_SIZE=16368 -D_GNU_SOURCE -I/opt/include -Doptimize=-O6 -msse2 -funroll-loops -fno-strict-aliasing -Dcccdlflags=-fPIC -Dldflags=-L/opt/perl/lib -L/opt/lib -Dlibs=-ldl -lm -lcrypt -Darchname=amd64-linux -Dprefix=/opt/perl -Dprivlib=/opt/perl/lib/perl5 -Darchlib=/opt/perl/lib/perl5 -Dvendorprefix=/opt/perl -Dvendorlib=/opt/perl/lib/perl5 -Dvendorarch=/opt/perl/lib/perl5 -Dsiteprefix=/opt/perl -Dsitelib=/opt/perl/lib/perl5 -Dsitearch=/opt/perl/lib/perl5 -Dsitebin=/opt/perl/bin -Dman1dir=/opt/perl/man/man1 -Dman3dir=/opt/perl/man/man3 -Dsiteman1dir=/opt/perl/man/man1 -Dsiteman3dir=/opt/perl/man/man3 -Dman1ext=1 -Dman3ext=3 -Dpager=/usr/bin/less -Uafs -Uusesfio -Uusenm -Uuseshrplib -Dd_dosuid -Dusethreads=undef -Duse5005threads=undef -Duseithreads=undef -Dusemultiplicity=undef -Demail=perl-binary@​plan9.de -Dcf_email=perl-binary@​plan9.de -Dcf_by=Marc Lehmann -Dlocincpth=/opt/perl/include /opt/include -Dmyhostname=localhost -Dmultiarch=undef -Dbin=/opt/perl/bin -Dxxxusedevel -DxxxDEBUGGING -Dxxxuse_debugging_perl -Dxxxuse_debugmalloc -des' hint=recommended\, useposix=true\, d_sigaction=define useithreads=undef\, usemultiplicity=undef useperlio=define\, d_sfio=undef\, uselargefiles=define\, usesocks=undef use64bitint=define\, use64bitall=undef\, uselongdouble=undef usemymalloc=n\, bincompat5005=undef Compiler​: cc='gcc'\, ccflags ='-ggdb -gdwarf-2 -g3 -fno-strict-aliasing -pipe -I/opt/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'\, optimize='-O6 -msse2 -funroll-loops -fno-strict-aliasing'\, cppflags='-DPERL_ARENA_SIZE=16368 -D_GNU_SOURCE -I/opt/include -ggdb -gdwarf-2 -g3 -fno-strict-aliasing -pipe -I/opt/include' ccversion=''\, gccversion='4.3.2'\, 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='gcc'\, ldflags ='-L/opt/perl/lib -L/opt/lib -L/usr/local/lib' libpth=/usr/local/lib /lib /usr/lib /lib64 /usr/lib64 libs=-ldl -lm -lcrypt perllibs=-ldl -lm -lcrypt libc=/lib/libc-2.7.so\, so=so\, useshrplib=false\, libperl=libperl.a gnulibc_version='2.7' Dynamic Linking​: dlsrc=dl_dlopen.xs\, dlext=so\, d_dlsymun=undef\, ccdlflags='-Wl\,-E' cccdlflags='-fPIC'\, lddlflags='-shared -O6 -msse2 -funroll-loops -fno-strict-aliasing -L/opt/perl/lib -L/opt/lib -L/usr/local/lib'

Locally applied patches​: http​://public.activestate.com/cgi-bin/perlbrowse/p/34209 http​://public.activestate.com/cgi-bin/perlbrowse/p/34507 http​://www.gossamer-threads.com/lists/perl/porters/232549 embed.fnc​:Perl_vcroak NULLOK

--- @​INC for perl 5.10.0​: /root/src/sex /opt/perl/lib/perl5 /opt/perl/lib/perl5 /opt/perl/lib/perl5 /opt/perl/lib/perl5 /opt/perl/lib/perl5 .

--- Environment for perl 5.10.0​: HOME=/root LANG (unset) LANGUAGE (unset) LC_CTYPE=en_US.UTF-8 LD_LIBRARY_PATH (unset) LOGDIR (unset)

PATH=/root/s2​:/root/s​:/opt/bin​:/opt/sbin​:/bin​:/sbin​:/usr/bin​:/usr/sbin​:/usr/X11/bin​:/usr/games​:/usr/local/bin​:/usr/local/sbin​:/root/pserv​:. PERL5LIB=/root/src/sex PERL5_CPANPLUS_CONFIG=/root/.cpanplus/config PERLDB_OPTS=ornaments=0 PERL_ANYEVENT_EDNS0=1 PERL_ANYEVENT_NET_TESTS=1 PERL_ANYEVENT_PROTOCOLS=ipv4\,ipv6 PERL_ANYEVENT_STRICT=1 PERL_BADLANG (unset) PERL_UNICODE=0 SHELL=/bin/bash

This bug is a duplicate of #28824. It seems to have been fixed in commit d0166017.

Leon

p5pRT commented 12 years ago

@cpansprout - Status changed from 'open' to 'resolved'

p5pRT commented 12 years ago

@cpansprout - Status changed from 'open' to 'resolved'