4

This program

use warnings;
use strict;
use feature qw(say);

{
    #use autodie;   # all good when this is uncommented
    no autodie;
}

open my $OLDSTD, '>&', *STDOUT;        #--> line 10 (program fails)
open *STDOUT, '>', 'stdout.out';

say "$$ done";

aborts with

Undefined subroutine called at problem_no_autodie.pl line 10.

To restate the comment in code: if there is a use autodie; statement first then all is well. (All is well with only use autodie; as well.) Curiously, in the same scope with no autodie statement I see no such problems either; only code outside of its scope fails! Kinda anti-scoping, eh?

If this scoped no autodie comes after the use of *STDOUT then it's all good again. Further use of *STDOUT, after (the scoped) no autodie, fails the program.

There is a Gotcha mentioned in docs that involves barewords (which I don't fully understand), and the program indeed fails with STDOUT -- but I have it as *STDOUT.

So it appears that *STDOUT is treated as a user's sub, but I don't understand that nor how the scope of autodie gets defeated. (Scope leakage is mentioned as a bug in some versions but in a seemingly unrelated way.) And there is a practical problem with this.

I don't use autodie in my code. But consider this sub, which I do use

sub isatty {
    no autodie;
    state $isatty = open(my $tty, '+<', '/dev/tty');
    return $isatty;
}

It is legitimate for that open to fail, so we have to disable autodie in that scope in case the user of the sub has it on. Then is the described behavior going to hurt? Under what circumstances?

I am puzzled by this effect of no autodie and by its leakage out of its scope, and by all their strange details. But the real concern is that I am not sure how to protect the code that uses a library like the one above against that behavior, since I don't understand it. Any ideas?

I see this under 5.16.3 (system), 5.26.2 and 5.30.0 (perlbrew) on CentOS 7.8

I do not see this behavior on 5.32.0; no failure there.


The ... or die $! check with open didn't make any difference so it's not shown for simplicity.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • 1
    I don't get any errors with 5.26.1 (system) or 5.32.0 (perlbrew) on an Ubuntu 18.04 system. – Shawn Sep 28 '20 at 07:27
  • 1
    @Shawn Interesting. In particular the fact that two versions where it fails for me bracket the one where it doesn't fail for you. I installed 5.32.0 under perlbrew -- and it doesn't fail for that one! Edited question accordingly. So we agree on somethin' :) Thank you for checking it out. – zdim Sep 28 '20 at 08:47
  • 1
    @Shawn I installed 5.26.1 to test, given ikegami's answer, and the program does fail under it for me! Perhaps your Ubuntu has it patched (or version upgraded) on 5.26.1? – zdim Sep 29 '20 at 18:12

1 Answers1

2

This bug was introduced in autodie 2.24 and fixed in autodie 2.30.

$ ( cd ./autodie-2.23; perl Makefile.PL >&3; make >&3; perl -Mblib ../a.pl && echo ok ) 3>/dev/null
ok

$ ( cd ./autodie-2.24; perl Makefile.PL >&3; make >&3; perl -Mblib ../a.pl && echo ok ) 3>/dev/null
Undefined subroutine called at ../a.pl line 10.

$ ( cd ./autodie-2.29; perl Makefile.PL >&3; make >&3; perl -Mblib ../a.pl && echo ok ) 3>/dev/null
Undefined subroutine called at ../a.pl line 10.

$ ( cd ./autodie-2.30; perl Makefile.PL >&3; make >&3; perl -Mblib ../a.pl && echo ok ) 3>/dev/null
ok

$ ( cd ./autodie-2.31; perl Makefile.PL >&3; make >&3; perl -Mblib ../a.pl && echo ok ) 3>/dev/null
ok

(2.30 is not found on CPAN.)

To protect against this issue, add a dependency on autodie 2.30 or higher.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • 1
    Thank you. I probably can't have a specific module updated on our production systems (policies etc) so then I'll use `eval` instead of `no autodie`. – zdim Sep 29 '20 at 17:00
  • 1
    @zdim You could monkey patch the problematic sub. `*Foo::is_atty = sub { ... };` wrapped in a version check (cause that's so much better than upgrading a module...) – ikegami Sep 29 '20 at 17:25
  • 1
    That would force the calling code to only use the "allowed" version of `autodie`? While that is the _right way_ I think I'd have to pass given how and where the lib may get used. Goes back to mine not understanding _what_ the bug does, so in what circumstances it might bite. But I think that I have that at barely a few places so `eval`-ing instead is easy – zdim Sep 29 '20 at 18:04
  • 1
    ? No, that won't force the code to use a version of autodie that isn't installed; that would provide a means to avoid using autodie at all. – ikegami Sep 29 '20 at 18:07