Bug 10417

Summary: /proc/self/fd/<n> semantics differ from Unix /dev/fd/<n>
Product: [Retired] Red Hat Linux Reporter: Bill Rugolsky, Jr. <bill>
Component: kernelAssignee: Arjan van de Ven <arjanv>
Status: CLOSED WONTFIX QA Contact: Brian Brock <bbrock>
Severity: medium Docs Contact:
Priority: medium    
Version: 6.0   
Target Milestone: ---   
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2002-12-16 03:04:58 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Bill Rugolsky, Jr. 2000-03-29 19:13:37 UTC
(The following is taken in part from e-mail to Richard Gooch cc'd to the
 linux-fsdevel mailing list,Message-ID: <20000228161258.A17596@ead37>).

A long time ago ...  Fri, 13 Oct 1995 15:18:02 +0200, Linus Torvalds said:
> I just made Linux-1.3.34 (aka "Not quite the buggiest kernel ever")
> available on the normal ftp-sites.

<snip>

> /proc/<pid>/fd/<x> works again. PLAN9 semantics are gone, as those no
> longer work.

<snip>

Here "PLAN9 semantics" means that the following are equivalent:
    fd = open("/dev/fd/n",mode);
    fd = dup(n);
the point being that permissions are not checked on the open, and
the two descriptors share the same file pointer.

In Linux we have /dev/fd -> /proc/self/fd/, but the semantics are
very different: permissions are checked, and the file pointers are
different.

The permissions are a problem whenever a process changes its uid,
since the file permissions seem not to change with it.  If we attempt to do
something like the following:

$ ssh -x -q localhost gawk "'BEGIN{print \"Hello, World\" >
\"/dev/stderr\"}'"
gawk: cmd. line:1: fatal: can't redirect to `/dev/stderr' (Permission
denied)

  or

# su -l rugolsky -c "gawk 'BEGIN{print \"Hello, World.\" > \"/dev/stderr\"
}'" 2>&1 | tee /tmp/gawk.out
gawk: cmd. line:1: fatal: can't redirect to `/dev/stderr' (Permission
denied)

we run into difficulties.

[ These are contrived examples distilled from actual code.  Note that
although gawk can handle /dev/stderr internally, if there is no such file,
the same is not true for other programs. ]

If we allocate a pty, the permissions are handled by the devpts filesystem:

$ ssh -x -q -t localhost gawk "'BEGIN{print \"Hello, World\" >
\"/dev/stderr\"}'"
Hello, World
$ ssh -x -q -t localhost 'ls -l /proc/$$/fd/2'
lrwx------   1 rugolsky ead            64 Feb 28 14:24 /proc/23428/fd/2 ->
/dev/pts/5

Other unix-like systems, like Solaris, implement /dev/fd/<n> as a
character device:

   crw-rw-rw-   1 root     root     160,  0 Feb 28 15:29 0
   crw-rw-rw-   1 root     root     160,  1 Feb 28 15:29 1

The minor number is the file-descriptor to dup(2).  This implementation
generally limits the largest fd to 255, but is usually more than adequate
for its intended use.

There are two issues here: (1) permission checking, and (2) dup(2)
semantics.  Of these two, the permission checking is probably the most
important, since we are usually interested in streams, which are not
seekable anyway.

I looked at linux/fs/open.c; doing it the Solaris way (with a character
driver) requires restructuring filp_open() so that it can return an
*existing* struct file, instead of a newly created one; it is an intrusive
change in an important code path.  Since Al Viro is restructuring the VFS
code for 2.4, including changes in permission checking required by the
stackable fs code, it is not clear whether his changes will make this
easier.

It is possible to suspend a process, then do a
"chmod a+rw /proc/<pid>/fd/<n>"; in this case the open will succeed,
and all is well.  This begs the question of whether the pipe should be
created with liberal permission bits (currently, linux-2.2.14/fs/pipe.c has
inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR).  Would changing this open
a security hole?  Given that symbolic links in procfs *do* check
permissions, this seems like a workaround *for pipes only*.  Finally, this
could be done in libc, but the cost seems too much to bear for such a
frequently used system call.