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

POSIX::close() bug? #16966

Open
p5pRT opened this issue Apr 17, 2019 · 8 comments
Open

POSIX::close() bug? #16966

p5pRT opened this issue Apr 17, 2019 · 8 comments

Comments

@p5pRT
Copy link

p5pRT commented Apr 17, 2019

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

Searchable as RT134042$

@p5pRT
Copy link
Author

p5pRT commented Apr 17, 2019

From felipe@cpanel.net

Hello,

  Todd Rinaldo and I were talking today about this​:


perl -MPOSIX= -e'
open my $r3, "<&", \*STDIN; # opens fd 3
open my $r4, "<&", \*STDIN; # opens fd 4
print "r4 is " . fileno($r4) . "\n";
close $r3; # closes fd 3 & Perl $r3
POSIX​::close(fileno($r4)); # closes fd 4, NOT Perl $r4
my($in, $out);
pipe($in, $out); # opens fds 3 & 4
print "out is " . fileno($out) . "\n";

close $out; # XXX​: a no-op, because Perl still has $r4 open?

sysread( $in, my $buf, 512 ); # hangs
'


We’re wondering if the above constitutes something that would reasonably be considered a bug in Perl, and/or something that could be accommodated anyway.

I’m guessing that the fact that close() is a no-op (i.e., it doesn’t close any FDs) is because PerlIO never knew about the POSIX​::close() call against FD 4 and so it tracks both $r4 and $out as referring to FD 4; thus, it won’t do anything when $out is close()d because it still has another filehandle that refers to that FD.

When pipe() gives FD 4, shouldn’t PerlIO set that reference counter to 1? Or should it maybe warn() that there’s a file descriptor conflict?

Thank you!

-Felipe Gasper

@p5pRT
Copy link
Author

p5pRT commented Apr 18, 2019

From @tonycoz

On Wed, 17 Apr 2019 10​:14​:47 -0700, felipe@​cpanel.net wrote​:

Hello,

Todd Rinaldo and I were talking today about this​:

-----
perl -MPOSIX= -e'
open my $r3, "<&", \*STDIN; # opens fd 3
open my $r4, "<&", \*STDIN; # opens fd 4
print "r4 is " . fileno($r4) . "\n";
close $r3; # closes fd 3 & Perl $r3
POSIX​::close(fileno($r4)); # closes fd 4, NOT Perl $r4
my($in, $out);
pipe($in, $out); # opens fds 3 & 4
print "out is " . fileno($out) . "\n";

close $out; # XXX​: a no-op, because Perl
still has $r4 open?

sysread( $in, my $buf, 512 ); # hangs
'
-----

We’re wondering if the above constitutes something that would
reasonably be considered a bug in Perl, and/or something that could be
accommodated anyway.

I’m guessing that the fact that close() is a no-op (i.e., it doesn’t
close any FDs) is because PerlIO never knew about the POSIX​::close()
call against FD 4 and so it tracks both $r4 and $out as referring to
FD 4; thus, it won’t do anything when $out is close()d because it
still has another filehandle that refers to that FD.

When pipe() gives FD 4, shouldn’t PerlIO set that reference counter to
1? Or should it maybe warn() that there’s a file descriptor conflict?

As discussed on IRC, you're pretty much getting expected behaviour - it's consequence of bypassing Perl's I/O abstraction for a Perl file handle.

I don't see a simple solution to forcing the reference count for new fds (as opposed to fdopen()) to 1 for pipe().

From further discussion in IRC it looks like what you were after was a way to close all open file handles so a child that drops privileges after a fork doesn't retain access to any files that may have required elevated privileges to access.

We could modify perl to track all new SV file handles, but XS code could still create them bypassing such tracking (unless it was in sv_upgrade(), I guess, but that would retain closed handles until the SV was destroyed. And it could really only track the handles in the current thread (the SV handles from other threads aren't usable in the current thread.)

I don't see a simple solution.

Tony

@p5pRT
Copy link
Author

p5pRT commented Apr 18, 2019

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

@FGasper
Copy link
Contributor

FGasper commented Jan 31, 2020

perl -MPOSIX= -e'
open my $r3, "<&", \*STDIN; # opens fd 3
open my $r4, "<&", \*STDIN; # opens fd 4
print "r4 is " . fileno($r4) . "\n";
close $r3; # closes fd 3 & Perl $r3
POSIX::close(fileno($r4)); # closes fd 4, NOT Perl $r4
my($in, $out);
pipe($in, $out); # opens fds 3 & 4
print "out is " . fileno($out) . "\n";

close $out; # XXX: a no-op, because Perl still has $r4 open?

sysread( $in, my $buf, 512 ); # hangs
'

@xenu xenu removed the Severity Low label Dec 29, 2021
@khwilliamson
Copy link
Contributor

@tonycoz @Leont The ticket says a warning would be sufficient. And it seems to me that such a warning would be really useful, How hard would that be?

@Leont
Copy link
Contributor

Leont commented Apr 12, 2022

I’m guessing that the fact that close() is a no-op (i.e., it doesn’t close any FDs) is because PerlIO never knew about the POSIX​::close() call against FD 4 and so it tracks both $r4 and $out as referring to FD 4; thus, it won’t do anything when $out is close()d because it still has another filehandle that refers to that FD.

Yeah, POSIX::close() doesn't know about the fd reference counting that PerlIO does (and it wouldn't know what to do with it anyway).

Frankly, you just shouldn't use POSIX::close() on any descriptor owned by perl, why would you do that anyway?.

When pipe() gives FD 4, shouldn’t PerlIO set that reference counter to 1? Or should it maybe warn() that there’s a file descriptor conflict?

That's slightly tricky because there are valid reasons for that counter not to be 1 (in particular, open my $fd, '<&=', $fd), but I think that's possible.

@FGasper
Copy link
Contributor

FGasper commented Apr 12, 2022

Frankly, you just shouldn't use POSIX::close() on any descriptor owned by perl, why would you do that anyway?

It was just for pure-Perl demonstration purposes. The “real-life” circumstance was I think:

  • XS code that closes FDs after a fork. (It’s been too long for me to recall more details than this.)
  • XS code that closes FDs that Perl gave to it. Solvable by giving POSIX::dup() to the XS code rather than Perl’s FD, but that’s knowledge I, at least, lacked for a good while.

@Leont
Copy link
Contributor

Leont commented Apr 12, 2022

XS code that closes FDs after a fork. (It’s been too long for me to recall more details than this.)

Some daemonization code does that, I would highly recommend against doing that in Perl.

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

No branches or pull requests

6 participants