Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Leaking a file descriptor into a child to use with /proc/self/fd/3

by ewheeler (Novice)
on Feb 25, 2020 at 00:42 UTC ( [id://11113376]=perlquestion: print w/replies, xml ) Need Help??

ewheeler has asked for the wisdom of the Perl Monks concerning the following question:

We are trying to open a file descriptor and access it from a forked + exec child. It works in bash as follows:

# exec 3< /etc/passwd # grep root /proc/self/fd/3 root:x:0:0:root:/root:/bin/bash

When we test with perl using the code below, grep complains about fd/3 not existing.

open(my $in, '/etc/passwd'); if (!fork()) { exec("grep", "root", "/proc/self/fd/" . fileno($in)); } close($in);

What am I missing, is fork or exec closing the leaked file descriptor?

-Eric

Replies are listed 'Best First'.
Re: Leaking a file descriptor into a child to use with /proc/self/fd/3
by hippo (Bishop) on Feb 25, 2020 at 09:51 UTC
    We are trying to open a file descriptor and access it from a forked + exec child.

    Here's a working SSCCE counterexample. It uses the PPID to retrieve the correct FD.

    #!/usr/bin/env perl use strict; use warnings; use autodie; $| = 1; open my $in, '<', '/etc/passwd'; my $ppid = $$; unless (fork) { print "CHILD: "; exec ("grep", "root", "/proc/$ppid/fd/" . fileno($in)); } else { print "PARENT: still going\n"; wait; } close $in;

    Here's the simpler one for not shelling out:

    #!/usr/bin/env perl use strict; use warnings; use autodie; $| = 1; open my $in, '<', '/etc/passwd'; unless (fork) { print "CHILD: $_", for grep { /root/ } <$in>; print "CHILD: we're done here\n"; exit; } else { print "PARENT: still going\n"; wait; } close $in;

      Neat idea, we noticed that PPID still referenced it as well--however if the parent exits before the child uses the FD then it may not be consistent. $^F or fcntl for CLOEXEC is the solution here.

Re: Leaking a file descriptor into a child to use with /proc/self/fd/3
by Fletch (Bishop) on Feb 25, 2020 at 14:26 UTC

    Might also want to look at $^F (see perlvar) and/or explicitly clearing CLOEXEC on the descriptor with fcntl.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      Perfect, that fixed the problem. I did not realize Perl closed FDs on exec (or maybe that is an OS thing).

        The Perl process essentially goes away by replacing itself when you exec. So, it might feel surprising but it seems sensible/intuitive that any FDs opened in Perl would close.

        This worked:

        open(my $in, '/etc/passwd'); my $flags = fcntl($in, F_GETFD, 0); fcntl($in, F_SETFD, $flags & (~FD_CLOEXEC)) or die "Can't set flags: $ +!\n"; if (!fork()) { exec("grep", "root", "/proc/self/fd/" . fileno($in)); } close($in);
Re: Leaking a file descriptor into a child to use with /proc/self/fd/3
by bliako (Monsignor) on Feb 25, 2020 at 04:18 UTC

    And what do you expect to happen when you do:

    exec 4< /etc/passwd; exec 5< /etc/passwd

    I don't think a redirection is a 2-way thing ...

    What about this?

    open(my $in1, '</etc/passwd'); open(my $in2, '</etc/passwd');

    Do you expect the OS to give you the same fd when (re-)opening a specific file?

    btw,

    if (!fork()) { system('ls -al /proc/self/fd/');}

    shows that fd #3 is "linked" to /etc/passwd even for the child of the child of bash.

    bw, bliako

Re: Leaking a file descriptor into a child to use with /proc/self/fd/3
by 1nickt (Canon) on Feb 25, 2020 at 01:32 UTC

    Hi, sorry, it's really unclear what you are trying to do, because of the shelling out and all that. Also note that you do not check to see that your open call succeeded.

    Maybe this will help?

    Unix::Passwd::File

    $ perl -MUnix::Passwd::File -MData::Dumper -Mstrict -wE 'say Dumper Un +ix::Passwd::File::get_user(user => "root")'
    $VAR1 = [ 200, 'OK', { 'gecos' => 'System Administrator', 'home' => '/var/root', 'uid' => '0', 'pass' => '*', 'gid' => '0', 'user' => 'root', 'shell' => '/bin/sh' } ];

    ... or maybe:

    Path::Tiny

    $ perl -MPath::Tiny -Mstrict -wE 'print for grep {/root/} path("/etc/p +asswd")->lines'
    root:*:0:0:System Administrator:/var/root:/bin/sh daemon:*:1:1:System Services:/var/root:/usr/bin/false _cvmsroot:*:212:212:CVMS Root:/var/empty:/usr/bin/false

    Hope this helps!


    The way forward always starts with a minimal test.

      Actually grep was intended as a trivial example of something someone might run in exec after forking with reference to a FD that the parent opened. We don't actually need to grep the passwd file, I just wanted a simple example to show what was not working for us. $^F was the solution.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11113376]
Approved by GrandFather
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (6)
As of 2024-04-25 15:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found