Description of problem: Jake Edge has reported the following gcc kernel related potential security issue on LWN: <cite> 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 </cite> Link to the post: http://lwn.net/Articles/272048/#Comments Link to upstream fix: http://git.kernel.org/?p=linux/kernel/git/x86/linux-2.6-x86.git;a=commitdiff;h=52c841e1012b8e73cc04b53f92fb933db580fb42
Additional comment from BZ#436131: The debian folks reported that kernel doesn't ensure direction flag is cleared upon entry to signal handler, which violates both i?86 and x86_64 ABIs. Old GCCs conservatively used cld anyway before using any instructions that use that flag, but GCC 4.3 no longer does that, it relies on the ABI guarantees that on entry to a function the direction flag must be cleared. See http://gcc.gnu.org/ml/gcc-patches/2006-12/msg00276.html Anything that uses std instruction must cld again before calling another function or before returning from function. Unfortunately, if async signal is sent while a thread has std flag set, kernel will start a signal handler with DF flag set. The fix is addition of regs->eflags &= ~X86_EFLAGS_DF; or similar in setup_frame/setup_rt_frame (i386, x86_64, x86_64 32-bit support). While only code compiled with GCC 4.3 and later will be affected by this bug, RHEL5 kernels are often used with later Fedora userland (e.g. koji buildboxes), so IMHO if at all possible to fix should be backported to RHEL5.2 and perhaps even RHEL4.7 kernels.
Created attachment 297943 [details] Testcase for finding out the value of the DF flag.
Comments from Jason Baron and reply from Jakub Jelinek describing the current behaviour and the nature of the bug: <cite jbaron> Whenever i run the test case (sending SIGUSR1 to the test process), i get: 'DF = 1'. indicating that the the df flag is set...I expected it to be unset b/c from reading this gcc clears the df flags...i've tried older distros and compat-gcc and still i get DF = 1...what am i missing? thanks. </cite> <cite jakub> If you get DF = 1, then that means the kernel is buggy. The psABI says that the DF flag must be clear upon entry to a function and also on exit from function. GCC <= 4.2.x would add cld just in case, whenever it used some string instruction (movs*/stos*/loads*/cmps* etc.), GCC 4.3.0 relies on the ABI guarantee. As GCC itself never issues std insn, it is just inline or out-of-line assembly which has to reset cld after it did std (AFAIK all such assembly I saw does that), the kernel has to start a process with cleared DF flag (also done) and the kernel signal handler needs to clear it for the signal handler (this is the bug). </cite>
Valid link to upstream kernel commit: http://marc.info/?l=git-commits-head&m=120492000901739&w=2 And the following one: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=e40cd10ccff3d9fbffd57b93780bee4b7b9bff51
Hi Jan, Can you post the rhel3 committed patch for clarification? I see some usage of the DF flag but it's unlike the upstream version.
This was addressed via: Red Hat Enterprise Linux version 3 (RHSA-2008:0211) Red Hat Enterprise Linux version 5 (RHSA-2008:0233) Red Hat Enterprise Linux version 4 (RHSA-2008:0508)