Subject: Re: CRITICAL ** Holes in default cron jobs ** CRITICAL
To: None <tech-kern@NetBSD.ORG>
From: Ty Sarna <tsarna@endicor.com>
List: current-users
Date: 12/30/1996 16:01:24
Followups to tech-kern, where this is more appropriate (ment to do that
the first time, sorry).

der Mouse wrote:
> 
> This sounds as though it would require a great redesign of the
> filesystem semantics.

Well, that was the point :-)

> Consider unlink().  This is not an operation on a file; it is an
> operation on file _names_.  unlink() removes a directory entry.  The

So the handle either needs to be on the directory entry, or neds to have
memory of how it was found. The sematics are a bit tough with FFS.
They're a little easier with something like the Amiga FS, where hard
links are distinguishable from the "true file", or with something like
the Be FS, where the filesystem has a database table style
representation and the handle can basically be a row pointer.

> Thus, if you want hunlink() to work, a handle must really be a
> <directory,name> pair.  But if you do that, you are still leaving
> yourself open to racing against changing what that name in that
> directory refers to.  The races will be not as bad as using the full
> path, but the situation will be no better than what you can already get
> by doing a chdir() and then using a slash-free (relative) path.

I've though about this stuff, and it's not all that bad to solve. The
real problem is the battle between "I need to guarantee that these
operations happen as an uniterupted group" and "I need to make sure some
bozo doesn't get a lock and just hold it, denying me access". There are
already solutions for that (especially as I phrased the problem above,
it should be recognizable as a application for transaction processing),
but they're not simple.

> Actually, a file descriptor returned by open() _is_ a handle on the
> file itself; that's how come fchmod() and fchown() exist - they are
> operations on the file, not its contents.  (fchdir() and ftruncate()
> are related, but they are less clearly operations on the file itself as
> opposed to its contents.)

The problem with this is that the fh returned is only used by some
operations, and is not availible for some objects (there's no lopen(),
so how do you get a handle on a symlink? How do I get a handle on
/dev/sd0a when it's already open or mounted and I don't want to do I/O,
just examine the file or move it or something?). Perhaps one solution is
to have some sort of flag that says "open without I/O permission", but
that still leaves the issue of symlinks, and doesn't solve the unlink()
problem. funlink()? (or does that create a link that's neither hard or
soft, but that is fun? :->). funlink() would have the same semantic
difficulties you pointed out above (which directory enrey do I unlink?)

The only total solution is transactions, but that's somewhat... er...
nontrivial to implement. With transactions, you don't need file handles
as I described above, because you can guarantee your view of the
FS will be consistent throughout the transaction). But short of
that, handles seems like a simpler way to go (they're sort of
bargain-basement transactions, in a way), and solve most of the
problems. Still a lot of work, though.

At any rate, I bring this up as food for thought rather than an
immediate solution. Obviously, there are ways to work around this
problem already, and this is somehting that should be fixed soon rather
than waiting for a redesign of filesystem semantics! But I hope this
will get someone thinking about the problem. There are an ever-growing
number of attacks that use races with filesystem operations, and it'd be
nice to come up with a general solution to the whole class of problems
rather than having each programmer trying (and too often failing,
because it's so complex) to deal with all the possibilities himself.