By Jake Edge
March 7, 2008
A change to GCC for a recent release coupled with a kernel bug has created
a messy situation, with possible security implications. GCC changed some
assumptions about x86 processor flags, in accordance with the ABI standard,
that can lead to memory corruption for programs built with GCC 4.3.0. No
one has come up with a way to exploit the flaw, at least yet, but it
clearly is a problem that needs to be addressed.
The problem revolves around the x86 direction flag (DF), which governs
whether block memory operations operate forward through memory or
backwards. The main use for the flag is to support overlapping memory
copies, where working backwards through memory may be required so that the data
being copied does not get overwritten as the copy progresses. Debian
hacker Aurélien Jarno reported the problem to
linux-kernel on March 5th, which was found when building Steel Bank
Common Lisp (SBCL) using the new compiler.
GCC's most recent
release, 4.3.0, assumes that the direction flag has been cleared
(i.e. memory operations go in a forward direction) at the entry of each
function, as is specified by the ABI (which is, somewhat amusingly, found at
sco.com [PDF]). Unfortunately, this clashes with
Linux signal handlers, which get called, incorrectly, with the flag in
whatever state it was in when the signal occurred. This has the effect of
leaking one bit of state from the user space process that was running when
the signal occurred to the signal handler, which could be in another process.
That, in itself, is a bug, seemingly with fairly minimal impact. Prior to 4.3, GCC
would emit a cld (clear direction flag) opcode before doing inline
string
or memory operations, so those operations would start from a known state.
In 4.3, GCC relies on the ABI mandate that the direction flag is cleared before
entry to a function, which means that the kernel needs to arrange that
before calling a signal handler. It currently doesn't, but a small patch fixes that.
The window of vulnerability is small, but was observed in SBCL. The
sequence of events that would lead to memory corruption are as follows:
- a user space program does an operation (memmove() for example)
that sets DF
- a signal occurs for some process
- the kernel calls the signal handler
- the signal handler does a memmove() in what it thinks is a
forward direction
- the memory is copied in the reverse direction, leading to corruption
It is hard to see how that could be turned into a security breach, but it
would be a mistake to assume that it can't. Other kernel bugs, like the
one that allowed the recent
vmsplice()
exploit, have looked liked memory corruption, but were found to be more than
that. The DF issue may turn out to be harmless from a security standpoint,
but it should not be assumed.
So, now the question is: what to do about it. It is clear that the kernel
should not leak the DF state to signal handlers, regardless of what GCC
does. It is interesting to note that this behavior is the same (DF is not
cleared on entry to a signal handler) on BSD
kernels, leading some to claim that it is the ABI that is incorrect and
that GCC should revert to its old behavior. Solaris kernels do
clear the DF before calling signal handlers. This problem has existed for
15 years; GCC has always emitted code that worked correctly on kernels
that did not follow the ABI, until now.
Part of the problem is that there are an enormous number of installed
kernels that are vulnerable to this problem, but only if GCC 4.3 is
installed. That version of GCC is not, yet, in widespread use, so the
thinking is that GCC should revert its behavior now, before it gets into
distributions. As kernels with the fix become more widespread, the
"proper" behavior could be restored. The GCC folks don't necessarily see
it that way, so it is unclear what will happen.
While it is true that distributors can control what kernel version and GCC
version they ship, those aren't the only ways that either GCC or
GCC-compiled binaries get installed. It is a bit of ticking time bomb for
random memory corruption at a minimum. Handling those bug reports will be
very difficult and time consuming. While the new behavior of GCC is
correct, and the kernel is broken, it would be very helpful to back out
this change, perhaps providing the new behavior via a command-line argument
for those who are sure their binaries will be running on patched kernels. Some discussion
on the gcc-devel list would indicate that a GCC 4.3.0.1 or 4.3.1 may be
forthcoming.
(
Log in to post comments)