In most systems, this will work if it's done correctly. It's called file locking. Conceptually, when a process puts a lock on a file no one else can access it. So, if the second process tries to switch files, it blocks. When the first process releases the lock, then the second one can do the switch but by then the first process is done with the file. Now, the whole purpose of locking is to assure indivisibility between the "Check " and the "Use". The idea is you lock the file before the check and then you unlock it after the use. Now, when you do this you have to be careful, because the semantics of different systems differ even now. So, I'm going to talk about two system lockings, which are archetypal Linux locks and FreeBSD locks. With Linux, you have two types of locks, advisory locking and mandatory locking. Advisory locking in this condition is worthless. The reason is advisory locking assumes cooperating processes. So, for example if you have a database file and two processes are running, that will want to update it. The first one grabs an advisory lock. The second one when it wants to update the database, checks to the lock. Gets back a mess. It gets back a code indicating, "Hey, that file is locked." It says, "Okay, I'll wait till the lock's released." The problem with advisory locks when you're dealing with race conditions that are malicious like we're talking about, is that the attacker is not going to honor that lock. The attacker will just go ahead and make the change, and the advisory lock doesn't stop them. But mandatory locking does. When a process grabs a mandatory lock on a file, no other process can access that file until the lock is released and the operating system forces the second and all other processes to honor the lock. So, you don't have an option. That's why it's mandatory. The file is locked, that's it. When the lock is released, then others can access it. The next slide shows the way you do this sum with system calls. F-lock or fcntl, either of them will do it. In fact, they believe fcntl calls f-lock. These are for advisory locks. For mandatory lock, you've got to use fcntl. Also, the file system on Linux has to be mounted with a special option to allow mandatorian locks to be enabled them. Mand is the actual option given to mount. What this does is it turns on the set gid bit and turns off the group execute bit to indicate that the files are available for mandatory locking. The process does the appropriate fcntl to lock it. No one else can access it because of the way the kernel enforces the locking. When it's done, tells fcntl to release the lock. On the most of the Linux systems, at least the ones I looked on, root was forced to honor this as well. So, when you do the mandatory locking you are also locking out root. This may be what you want, on the other hand it may be a very very rude surprise to root. But it turns out the mandatory locks have problems as well. The problem again comes around through synchronicity. Let's say process one reads a file. Process two blocks it. It alters it and then unlocks it. Meanwhile, process one which isn't edited, the contents have been changed. Process one writes out what was originally read. You've overwritten the changes to process two. This is the classic readers writers problem. What would actually have to happen here, is that process one would have to grab the lock first in order to prevent this from happening, or process two would have to grab the lock before process one try to read the file. Now, if root wants access and the file is locked, root can't get access. However, if it kills the process holding the lock, that releases the lock. The kernel smart about this, when the process terminates the lock is gone. Also, you have issues with read and write if the system calls are implemented in a divisible way which is typical of Linux. If someone does a write and starts to write, and then someone else grabs the mandatory lock, the write will complete. So, even though the file seems locked, it's actually going to change. The same thing with read. These are artifacts of the Linux kernel, the way Linux implement system calls and locks and there's not really anything you can do about it. The last note which is implicit throughout this but I wanted to make it explicit, I locked the file. Great. The grandparent directory, in other not the directory containing the file but the one containing the directory containing the file, is world writeable. So, I move the directory containing the locked file to another name. Then I simply create another directory with its name in another file with the name of the file that's locked, and I'm done. So, locking the file does not prevent the race condition. It just makes it harder to exploit. If the locking is done under favorable circumstances, that is you grab it before anyone else is able to open the file, then you in pretty good shape. FreeBSD does things a little differently. It provides something called openat. The idea here is the directory is opened, and given when you open the directory you get a file descriptor. So, you give that file descriptor to openat and then you give it to path. What that says is okay. Starting with the directory whose file descriptor you're given, resolve the path. If it happens that path is just the filename, then you're giving it the directory which is open. Then the only thing the attacker can do is switch the binding of the file, can't do anything with any answers to directories because those are controlled by a file descriptor. Just be sure the one that you open is the one that's right that's far down the path as you can trust. That makes life a lot easier