[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Plash] fchmod not working?
Re: [Plash] fchmod not working?
Tue, 29 Apr 2008 19:16:52 +0100 (BST)
Thomas Leonard <address@hidden> wrote:
> Running strace on unzip without pola-run shows:
> unlink("HelloWorld/main") = 0
> open("HelloWorld/main", O_RDWR|O_CREAT|O_TRUNC, 0666) = 12
> write(12, "#!/bin/sh\necho Hello World\n", 27) = 27
> fchmod(12, 0100755) = 0
> close(12) = 0
> utime("HelloWorld/main", [2008/02/07-19:49:37, 2005/09/17-14:19:23]) = 0
> Any ideas?
This is a tricky one. The cause is simple: fchmod() is one of those
operations for which file descriptors do not behave like capabilities.
fchmod() only works if the process is running under the UID that owns
the inode. So fchmod() has never worked under Plash.
I am surprised that this has not caused a problem before. Perhaps the
behaviour of unzip or cpio has changed in Ubuntu hardy. Some programs
have been switching to FD-based operations to avoid race conditions
such as symlink races. (That tends to involve using openat() or using
fchdir() + open() on single-component pathnames, although unzip is
clearly not doing that here.) It might have been possible to set the
file mode in the open() call, except that open()'s mode argument is
restricted by umask, whereas chmod()'s mode is not.
It's a bit odd that unzip is passing a mode of 0100755. 0100000 is
S_IFREG (meaning "this is a regular file") as returned by stat(). I
don't see anything in the man pages to say chmod() accepts this flag,
but I guess it does accept it under Linux.
It's annoying that we have to deal with permissions bits at all.
Plash does not normally interpret them. Under Plash, a file does not
need the executable bit set to be invoked as an executable via
execve(). However, outside of Plash, Unix does check the executable
bit, even though this bit does not usually enforce anything. So it is
useful if Plash-sandboxed processes can set the executable bit, as a
way to interact with the outside world. Zero-Install's use of
pola-run to wrap tar/unzip is a prime example.
I can think of two ways to support fchmod().
1. The server process could provide a generic fchmod() operation that
takes an FD and a mode and invokes fchmod() under its own UID. This
would be very coarse-grained. This limits our ability to restrict
chmod() operations on files that we have granted to sandboxed
processes. fchmod() does not normally look at the FD's open flags;
you can use it on read-only FDs. We would probably have to check the
open flags using fcntl() (F_GETFL).
2. In glibc, make each file FD be backed by a file object with a
chmod() method (let's call it a ChmodFacet).
A similar trick is already applied to directory FDs, but these are not
used very often.
It is quite distasteful to have to do this for file FDs. There would
be a performance impact:
* open(): not too bad, since interprocess communication is already
required at this point.
* close(): now requires an RPC.
* in between: storage cost in server process.
However, we wouldn't have to pay these costs if we don't need the
fchmod() feature. A file object such as a read-only proxy can opt not
to return a ChmodFacet from its file_open() method.
Currently these backing objects are not propagated across execve(),
fork(), dup()/dup2() or sendmsg(). Fixing dup() is easy; fork() a
little harder; execve() harder still but doable; however, sendmsg() is
I was planning on doing a release of Plash soon. Would you object if
I made a release before trying to fix this? It is not a trivial thing
to fix. It looks like tar still works: it uses open() to set the file
mode instead of fchmod().