Bug#793471: Perl system and $SIG{__WARN__} when exec fails
Ian Jackson
ijackson at chiark.greenend.org.uk
Fri Jul 24 10:50:36 UTC 2015
Package: perl
Version: 5.20.2-6
See transcripts below.
Perl's system builtin needs to fork and exec. If the exec fails it
needs to print a message to stderr and exit. It appears that this is
implemented in Perl and that it does the latter with sysexit, but the
former with warn.
This has a number of strange effects, which are visible if a
$SIG{__WARN__} handler is installed. Ones which can be observed
below include:
* If $SIG{__WARN__} = sub { die $_[0]; } (or something else that
raises an exception) then the control flow escapes from the confines
of system, and avoids the sysexit.
The program's END blocks might be executed, for example.
* Worse, if the program has its own exception handler surrounding
system, it will now start executing the main program both in the
parent and in the child.
* During the $SIG{__WARN__} handler, the Perl interpreter is not
properly set up in all versions of Perl. For example in
5.14.2-21+deb7u2, $$ is a lie - it still refers to the parent
process.
* Something else weird seems to be going on. I find these two
messages in the transcript below hard to explain:
24633 GOT UNDEF EVAL at ./t.pl line 15.
24633 BARE at ./t.pl line 21.
It appears that something is discarding or squashing an error.
The latter is particularly puzzling because $@ is left as ""
(indicating that the eval did not catch an exception) but $y is not
set to 42 (indicating that the eval did not run to completion).
I suggest the following remedies, in combination:
* system() should reset $SIG{__WARN__} in the child. There should be
a way to set $SIG{__WARN__} within system's child, but it should be
separate. $SIG{__WARN_CHILD__} or something maybe.
* The behaviour should be documented.
* The fact that $$ was wrong should be written down somewhere in the
manual so that users use the correct workaround (see below).
* And ideally someone familiar with the implementation should satisfy
themselves that they understand the currently observed behaviour, so
that we are confident that there aren't other lurking oddities.
As a workaround, people using unfixed versions of Perl can write:
my $mainprogram = $$;
$SIG{__WARN__} = sub { die $_[0] unless getppid == $mainprogram; };
Note that `die $_[0] if $$ == $mainprogram' is ineffective because $$
is wrong.
Thanks,
Ian.
(build)ian at zealot:~/junk$ cat t.pl
#!/usr/bin/perl -w
use strict;
use POSIX;
$SIG{__WARN__} = sub { die "DYING DUE TO WARNING $_[0]"; };
END { print STDERR "wheee!\n" if getppid == $$; }
END { print STDERR "getppid= ", getppid, "\n"; }
END { print STDERR "\$\$= $$\n"; }
my @bad = qw(/dev/enoent nothing);
print STDERR "---------- 1 ".getppid." $$ ----------\n";
my $y = eval { system @bad and die "EVAL $!"; 42; };
$y //= 'UNDEF';
print STDERR getppid." GOT $y $@\n";
print STDERR "---------- 2 ".getppid." ----------\n";
system @bad and die getppid." BARE $!";
print STDERR "NOTREACHED\n";
(build)ian at zealot:~/junk$ ./t.pl
---------- 1 24633 24948 ----------
24948 GOT UNDEF DYING DUE TO WARNING Can't exec "/dev/enoent": No such file or directory at ./t.pl line 15.
---------- 2 24948 ----------
DYING DUE TO WARNING Can't exec "/dev/enoent": No such file or directory at ./t.pl line 21.
$$= 24950
getppid= 24949
24948 BARE No such file or directory at ./t.pl line 21.
$$= 24949
getppid= 24948
24633 GOT UNDEF EVAL at ./t.pl line 15.
---------- 2 24633 ----------
DYING DUE TO WARNING Can't exec "/dev/enoent": No such file or directory at ./t.pl line 21.
$$= 24951
getppid= 24948
24633 BARE at ./t.pl line 21.
$$= 24948
getppid= 24633
(build)ian at zealot:~/junk$
zealot:~/junk> ./t.pl
---------- 1 23655 24976 ----------
24976 GOT UNDEF DYING DUE TO WARNING Can't exec "/dev/enoent": No such file or directory at ./t.pl line 15.
---------- 2 24976 ----------
DYING DUE TO WARNING Can't exec "/dev/enoent": No such file or directory at ./t.pl line 21.
$$= 24976
getppid= 24978
24976 BARE No such file or directory at ./t.pl line 21.
$$= 24976
getppid= 24976
wheee!
23655 GOT UNDEF EVAL at ./t.pl line 15.
---------- 2 23655 ----------
DYING DUE TO WARNING Can't exec "/dev/enoent": No such file or directory at ./t.pl line 21.
$$= 24976
getppid= 24976
wheee!
23655 BARE at ./t.pl line 21.
$$= 24976
getppid= 23655
zealot:~/junk>
More information about the Perl-maintainers
mailing list