This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
'$! = EACCESS; require ...' could fail
authorDavid Mitchell <davem@iabyn.com>
Mon, 28 Oct 2013 16:39:35 +0000 (16:39 +0000)
committerDavid Mitchell <davem@iabyn.com>
Tue, 29 Oct 2013 12:46:41 +0000 (12:46 +0000)
When require is checking against a particular entry in @INC, it stats the
potential pathname, and does various checks like skipping if it's a
directory. In this case the stat succeeds, so $! is left unchanged.
Later however, pp_require() checks for $! being EACCES. So if $! was
already set before the require, this could influence require's behaviour.

pp_ctl.c
t/comp/require.t

index c3b66bb..1653c17 100644 (file)
--- a/pp_ctl.c
+++ b/pp_ctl.c
@@ -3625,6 +3625,11 @@ S_check_type_and_open(pTHX_ SV *name)
     if (!IS_SAFE_PATHNAME(p, len, "require"))
         return NULL;
 
+    /* we use the value of errno later to see how stat() or open() failed.
+     * We don't want it set if the stat succeeded but we still failed,
+     * such as if the name exists, but is a directory */
+    errno = 0;
+
     st_rc = PerlLIO_stat(p, &st);
 
     if (st_rc < 0 || S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode)) {
index 475388f..4eafce4 100644 (file)
@@ -34,7 +34,7 @@ if (grep -e, @files_to_delete) {
 
 my $Is_EBCDIC = (ord('A') == 193) ? 1 : 0;
 my $Is_UTF8   = (${^OPEN} || "") =~ /:utf8/;
-my $total_tests = 55;
+my $total_tests = 56;
 if ($Is_EBCDIC || $Is_UTF8) { $total_tests -= 3; }
 print "1..$total_tests\n";
 
@@ -285,6 +285,21 @@ EOT
     }
 }
 
+
+{
+    # if we 'require "op"', since we're in the t/ directory and '.' is the
+    # first thing in @INC, it will try to load t/op/; it should fail and
+    # move onto the next path; however, the previous value of $! was
+    # leaking into implementation if it was EACCES and we're accessing a
+    # directory.
+
+    $! = eval 'use Errno qw(EACCES); EACCES' || 0;
+    eval q{require 'op'};
+    $i++;
+    print "not " if $@ =~ /Permission denied/;
+    print "ok $i - require op\n";
+}
+
 # Test "require func()" with abs path when there is no .pmc file.
 ++$::i;
 if (defined &DynaLoader::boot_DynaLoader) {