(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.