Welcome back. This set of lessons
introduces race conditions and describes how
having two or more seemingly simultaneous sets of
commands can cause
conflicting instructions in your programs.
This affects the program's efficiency and security,
as well as systems security.
We will discuss how you can
prevent race conditions for happening,
and identify them when they do.
Let's get started. So this lesson is on race conditions.
Now, a race condition essentially
is two processes trying to do something,
and the order of the processes in
which the process is trying to do it affects the result.
Now, I'm going to focus in
this lesson on what are known as
time of check to time of
use race conditions and these involve files.
We'll talk about how to spot them,
how to prevent them, and so forth.
Then towards the end, we'll talk about
other types of race conditions that can
occur when you're doing multithreading
or handling asynchronous events.
Now, if you look at the next slide,
two events, a and b are going to occur.
The question is, in what order?
Normally, if order doesn't matter,
then there is no possibility of security problem
unless together they form
a security problem which we'll assume they don't.
But if order matters,
one order may be fine,
the other order may create a security problem.
The reason it's called a race condition
is because the order matters,
so both processes are racing to do something.
The question is, who wins the race?
Who does it first?
Now, the one thing about a race condition is it always
involves two processes or two threads.
A single process or a single thread will
never have a race condition in and of itself.
But if that process is running,
then a second process starts
up and tries to interfere with the first.
You may have a race condition.
By the way, throughout this lesson,
I'm going to use the term process.
This also applies to multithreading.
So if you like,
you should treat processes also a thread.
Basically, any type of concurrent programming or
concurrent processes can cause this issue.
A thread really use is a,
if you like, a lightweight process.
Now, let me give you an example that
will show you how race conditions arise.
This is a very simple example
from a particular version of Sendmail,
which ran sit you idea roots.
So it rain with root privileges.
Sendmail is a very complex program,
and its configuration file is also very complex.
It's designed to be read quickly by computers.
But that means humans often
make errors when setting it up.
So the author of Sendmail,
Erica Baum very kindly provided a dash C option,
capital C option to allow you to test Sendmail.
It would essentially load the configuration file and let
you try various things to see whether or
not you had it configured correctly.
Now initially, you could give it the name of any file.
Since Sendmail ran setuid to root,
you would give it a file that
was not a configuration file.
Because this was intended to debug config files,
the air messages at gave were superb,
including it would print out the line and then
print out a message challenge
you what was wrong with the line.
So if you go to the slide that's Sendmail attack,
when you ran Sendmail with
the configuration file of
some protected file that you normally couldn't read.
Sendmail would open the file since it's running as root,
and read the first line and say, "Well, wait a minute.
That's not a proper configuration line."
So it will simply print the line and then
print an error message like bad format or something.
You can see a couple of examples there.
This is the beginning of the Declaration of Independence.
In this case, when in the course of human events
is not a valid configuration line and so forth.
So what you would do, is ran this on any file you liked
and get the output and then
delete every other line beginning with the second,
and now you have the file.
Now, in a sense this is a,
you're giving too much information on an error.
This is not the race condition.
But the way you fix it can introduce a race condition.
Because the problem here is
that Sendmail is setuid to root,
so it can open any file.
But if the real user ID,
the person who is running it as not root,
Sendmail should only open
those files which that user can open.
So we have conflict here between
the RealUID which is root and the effect of
UID which is the UID of the person running Sendmail.
So somehow you have to figure out how to handle that.
The next slide shows the Obvious Fix.
There is a system called access.
What that says is,
"Give me the config file and
the way you want to open it: read,
write, a patent, whatever execute whatever,
as the second document."
If the user with the effect of UID of
the current process can open that file in that way,
it returns zero, otherwise it returns negative one.
So the access system call is given right up there.
You do the test and if it returns zero,
then you go ahead and open the config file.
Problem is the solution is attractive as it is as flawed.
If you look at the next line,
you'll see an example.
The code there is the typical use of access.
But here is the problem, notice in both lines,
it's referring to the file by name.
So what that means is,
in order to locate the file,
it has to go into the kernel and
resolve that name turning it into an inode,
turn it into a position on disk.
So the access cause issue that does that first with xyz.
But then when fopen is issued,
it does the same thing.
Now, the results may be cached from the first one.
So it may be very quick the second time.
But the point is, both times it's doing that resolution.
So here's the problem.
What happens if I change the reference of xyz?
If it's not cached,
then it'll do the normal search and want
to do the open, I'll get the new one.
If it is cached, well,
then there'll be a mismatch between what's in the cache
and what the actual content of
disk is and so the cash will be ignored.
So in this case,
the problem is initially xyz points
to something that I can read.
But between the access and the fopen,
I delete that link and recreate xyz.
But instead of creating the file,
actually link it to something that only root can read.
So in essence what's happening is,
I'm opening a different file than the one I checked.
So the check becomes completely irrelevant.
That's a race condition,
because you have this window between
access and fopen where I can
change what the check is validating.
So the check validate something correctly.
But I don't care because the
open refers to something completely different.