By looking at the source, this is an obvious coding error - kernel first dereferences a pointer, and after that checks whether it's NULL. Where is the compiler bug there?
Posted Jul 17, 2009 14:24 UTC (Fri) by regala (subscriber, #15745)
[Link]
no, you don't understand. this pointer should not be NULL, and having it NULL in the code executed after the check is the security matter. Thus, the exploit has first to cause this ptr to be NULL, and it has to rely on the fact that gcc wrongly moves this check away assuming it is not necessary, being done after dereferencing the pointer, which may be wrong in certain loads, or in SMP, preemptible configs.
Linux 2.6.30 exploit posted
Posted Jul 17, 2009 14:39 UTC (Fri) by trasz (guest, #45786)
[Link]
This code wouldn't be valid in SMP or in preemptible kernel either, unless some locking was added, which would completely change the situation and would probably prevent the problematic optimization from being applied. Other possible way would be to make the variable volatile. Anyway, this is a programming error.
Linux 2.6.30 exploit posted
Posted Jul 17, 2009 14:56 UTC (Fri) by spender (subscriber, #23067)
[Link]
Well, since my videos show me exploiting the bug on multiple SMP and preemptible kernels, that's false. If you would read the exploit source you'd see this.
-Brad
Linux 2.6.30 exploit posted
Posted Jul 17, 2009 15:08 UTC (Fri) by trasz (guest, #45786)
[Link]
Erm, let me restate what I've written above - the kernel code mentioned above wouldn't be valid in any case, including SMP and preemptible kernel, contrary to what Regala said in the comment before.
Linux 2.6.30 exploit posted
Posted Jul 18, 2009 8:51 UTC (Sat) by Ross (subscriber, #4065)
[Link]
There is no point in checking if it is NULL _after_ you have used it. As soon as you have dereferenced a NULL pointer you can't predict what the program will do, and there is no point in blaming the compiler about it. GCC doesn't claim to allow this and it is not required to by any of the C standards.
Linux 2.6.30 exploit posted
Posted Jul 18, 2009 16:59 UTC (Sat) by xilun (subscriber, #50638)
[Link]
Given this source code, gcc has all rights to remove the check, and you obviously don't understand what do and does not imply concurrency, SMP, and preemptible configs.
The real bug is allowing to map 00000000. This can never be right.
Linux 2.6.30 exploit posted
Posted Jul 19, 2009 13:18 UTC (Sun) by PaXTeam (subscriber, #24616)
[Link]
> The real bug is allowing to map 00000000. This can never be right.
how else would you be able to run v8086 mode tasks then?
Linux 2.6.30 exploit posted
Posted Jul 17, 2009 14:28 UTC (Fri) by Trou.fr (subscriber, #26289)
[Link]
because if the check had not been optimized away, you wouldn't have been able to pass it to exploit the vulnerability.
Linux 2.6.30 exploit posted
Posted Jul 17, 2009 20:43 UTC (Fri) by stevenb (guest, #11536)
[Link]
Then the author of the code should have made the pointer volatile, or he/she should protect it with a lock. The optimization that GCC did, appears to be valid (but IANALL).
Linux 2.6.30 exploit posted
Posted Jul 17, 2009 17:55 UTC (Fri) by packet (guest, #5202)
[Link]
There is no compiler bug here. As paragraph 6.5.3.2.4 of the C99 standard
says, "If an invalid value has been assigned to the pointer, the behavior
of the unary * operator is undefined." Undefined here means "behavior,
upon use of a nonportable or erroneous program construct or of erroneous
data, for which this International Standard imposes no requirements"
(paragraph 3.4.3.1). Judging by the standard it seems to be legal to
optimize the null pointer guard away, as it has no side-effects in the
case of a valid pointer value. In the case of this kernel bug
this "undefined behavior" unfortunately makes the bug exploitable which
it presumably would not have been had the guard not been optimized away.
Yes, this is a compiler bug.
Posted Jul 18, 2009 14:31 UTC (Sat) by xoddam (subscriber, #2322)
[Link]
Uh, the zero pointer value is not an 'invalid value' if there's readable memory at address zero. The standard declines to define correct behavior for 'invalid' pointers because there are many ways for a pointer to be 'invalid' in some sense or another and several ways for an implementation to handle them (return garbage, return zero, deliver a signal, halt)
The exploit works because in this case, null is NOT an invalid value, but the compiled code behaves as though it is (and as though the compiler knows how the dereferencing of an invalid value will be handled ... which it doesn't). So yes, it's a compiler bug.
Yes, this is a compiler bug.
Posted Jul 18, 2009 17:20 UTC (Sat) by xilun (subscriber, #50638)
[Link]
So if you move the compiler out of the C standard respecting equation then this is a compiler bug? Makes absolutely no sense. This is a C compiler, not a "I would prefer it do that" compiler. You should better define your own langage where dereferencing a NULL pointer is NOT undefined.
The platform is defined by the compiler and the runtime. The compiler definitely has the right to consider that dereferencing a NULL pointer is something that is always undefined, because, well, the standard precisely says that. There is no story of mapping or not mapping pages at this point : NULL pointer dereferencing an undefined behavior is, and NULL pointer dereferencing an undefined behavior stays. A NULL pointer has been dereferenced, the behavior is undefined (and it's not very surprising that an undefined behavior as per a standard can be an exploitable security hole). This is as simple as that.
The bug also is in allowing to map address 0, which can't be a sane way to serve a sane purpose.
Yes, this is a compiler bug.
Posted Jul 18, 2009 22:53 UTC (Sat) by mstefani (subscriber, #31644)
[Link]
> The bug also is in allowing to map address 0, which can't be a sane way to serve a sane purpose.
If you happen to need to run a DOS program in Wine or sometimes even a Win32 application that still uses some DOS calls then you need access to the memory at 0x00000000. http://wiki.winehq.org/PreloaderPageZeroProblem
Yes, this is a compiler bug.
Posted Jul 19, 2009 0:07 UTC (Sun) by xilun (subscriber, #50638)
[Link]
I could argue that supporting program written for broken systems is not a sane purpose ;)
Yes, this is a compiler bug.
Posted Jul 19, 2009 8:00 UTC (Sun) by packet (guest, #5202)
[Link]
Footnote 84 of the C99 standard clearly says so: "Among the invalid
values for dereferencing a pointer by the unary * operator are a null
pointer, an address inappropriately aligned for the type of object
pointed to, and the address of an object after the end of its lifetime."
Before that sentence, only one exception is mentioned: (&*E == E) is
true, even in the case of a null pointer. IMHO the compiler can legally
do just about anything in the case of dereferencing a null pointer. The
lowest addressable object in C lives at address 1*sizeof(object). If null
was allowed as a valid pointer, there would be no pointer value to check
for invalid pointers. Sadly, this means that at least one byte (in the C
sense: a byte is a char) of the address space cannot be legally addressed
in C. If you consider this a bug, it's a language bug, but not a compiler
bug.
Linux 2.6.30 exploit posted
Posted Jul 24, 2009 15:39 UTC (Fri) by kubarebo (guest, #59791)
[Link]
I think that this exploit is a cascade failure initiated by the ill-conceived gcc patch. If you look at the original gcc patch submitted by Cygnus, you'll read the following:
"If all paths to a null pointer test use the same pointer in a memory load/store, then the pointer must have a non-null value at the test. Otherwise, one of the load/stores leading to the test would have faulted."
The assumption that "load/stores [that use the null pointer] [...] would have faulted" is generally wrong.
On many common architectures, virtual memory location 0 can be mapped and used. Most "small" embedded platforms allow its use. x86 surely does, too.
I think that the optimization was relatively ill conceived. The memory load/store should not be assumed to fail unless the underlying architecture universally guarantees that, or at least the target does. In this case, the target definitely does not guarantee anything of the sort, and the optimization is simply broken. I don't think theres much more to that. As a workaround, all linux kernel builds should disable the particular optimization (if possible).
I deal daily with code where the compiled C code dereferences NULL pointers simply because the underlying architecture supports it, and not letting you do it wastes one byte/word of RAM. On chips with only 256 bytes of RAM you may not want that. On x86 in real mode, the IDT starts at address 0 and some odd tasks like copying the IDT have to start at 0.
Methinks that the NULL pointer concept should be purged from the C standard, it is really up to the developer to ensure that a pointer is valid, and using magic values to indicate invalid pointers is very much environment-dependent. It has no place in a rather platform-agnostic language standard, IMHO.
Linux 2.6.30 exploit posted
Posted Jul 24, 2009 20:44 UTC (Fri) by nix (subscriber, #2304)
[Link]
GCC isn't ever going to compile code for platforms with only 256 bytes of
RAM. It's always been targetted at systems larger than that.
The C Standard disagrees that it's up to the developer to ensure a pointer
is valid: it doesn't just ban null pointers but also things like dividing
pointers by two, XORing pointers, shoving pointers off the ends of arrays
and so on. Attempting to dictate the semantics of pointers in these
situations would be catastrophic for optimization: even simple code motion
would probably have to be banned.
A simple flag (and a target flag) that says 'this compilation
specifically/this target usually has nonfaulting accesses to (void *)0' is
all that's needed, and we have that already.
Linux 2.6.30 exploit posted
Posted Apr 13, 2011 17:23 UTC (Wed) by Blaisorblade (guest, #25465)
[Link]
I agree that the flag is enough.
Anyway, IIRC, by the C standard (6.5.8.5), comparing pointers to different objects gives rise to undefined behavior, while in C++ it gives an unspecified result [1]. Therefore, while it would be valid for GCC (in C) to produce a crashing program, it would be probably of little use.