Proposal for a new I/O library design

Ben Rudiak-Gould benrg@dark.darkweb.com
Tue, 29 Jul 2003 23:52:41 -0700 (PDT)


On Tue, 29 Jul 2003, Sven Panne wrote:

> Ben Rudiak-Gould wrote:
> > I don't think this is fatal. The important part of File identity is that
> > two File values which compare equal necessarily denote the same file, not
> > the converse. [...]
>
> Huh? I thought it was the other way round. What is this your identity good
> for?

What I wrote above doesn't make much sense. Here's what I was trying to
say.

The keystone of this whole model is that File values are not file handles:
they are files. It's essential that the library implementation maintain
the link from the File value to the file by something more reliable than a
pathname. For example, in the following code:

        do f1 <- lookupFileByPathname "/mydir/myfile"
           fRead f1 ...
           f2 <- lookupFileByPathname "/mydir/myfile"
	   putStrLn (if f1 == f2 then "equal" else "inequal")
	   fRead f2 ...

f1 and f2 may compare equal or they may compare inequal (the latter being
the case if someone has renamed the original file and put a new one in its
place, for example), but if they compare equal then it *must* be the case
that they refer to the same actual file. This would not hold true if File
values were implemented internally as pathnames.

Why does this matter? Because people use the File abstraction in their
code whether the system provides it or not, and if the system doesn't
provide it, the way they implement it is as, you guessed it, pathnames.
This opens them up to various security exploits that involve renaming
files or directories in between successive opens of the same pathname.

In Haskell, I thought we could avoid this by implementing the File
abstraction correctly once and for all. People would have to use the
system-supplied abstraction rather than their own, but that would just be
among the things you'd learn when learning Haskell programming, along with
strong typing, side-effect isolation, pattern matching, and so on. Like
those features, the File abstraction not only prevents a certain class of
bugs, it's also conceptually more elegant than the alternatives.

But the upshot of the discussion here is that there's no way to implement
a well-behaved File abstraction on Win32 or Posix, so people are just
going to have to continue writing insecure programs on a less-elegant API.
Stop me if I start to sound bitter. :-)


-- Ben

(All of this also applies to the Directory abstraction, of course. Another
example from the proposal: dCreateFileEntry must fail if an entry by that
name already exists, and it must return IO File and not IO (). Anything
else is exploitable. Even this can't be implemented on some systems, I
think.)