Discussion:
Silent stack-heap collision under GNU/Linux
Vincent Lefevre
2014-07-20 17:49:11 UTC
Permalink
It appears that GCC can generate code that yields silent stack-heap
collision under GNU/Linux. I mean, the program doesn't crash (at least
not immediately), the memory just gets corrupted. At the same time,
this overrides the stack-size limit defined at the kernel level
(getrlimit system call / RLIMIT_STACK) because the kernel has no
chance to detect the collision (no page fault); thus this limit
doesn't protect the user, and the problem seems to be on GCC's side.

Why aren't such collisions detected by default?

How can one tell GCC to detect them?

Here's a test case:

------------------------------------------------------------------------
#include <stdio.h>

static char a = 0;
static unsigned long pa, pb, pc;

#define GETADDR(V) \
do { p##V = (unsigned long) &V; printf ("&" #V " = %016lx\n", p##V); } \
while (0)

void foo (unsigned long s)
{
char c[s];

GETADDR(c);
if (pa >= pc)
c[pa - pc] = 1;
else
printf ("Cannot test.\n");
}

int main (int argc, char **argv)
{
char b;

GETADDR(a);
GETADDR(b);

printf ("a = %d\n", a);
if (pb > pa)
foo (pb - pa);
else
printf ("Cannot test.\n");
printf ("a = %d\n", a);

return 0;
}
------------------------------------------------------------------------

I get something like:

&a = 0000000000600b20
&b = 00007fffbfea7d2f
a = 0
&c = 0000000000600ac0
a = 1

Same problem with the 32-bit ABI (-m32).

With GCC 4.9.1 (Debian/unstable), the program terminates successfully.
With the 4.10.0 snapshot, I also get a segmentation fault at the end,
but that's too late.
--
Vincent Lefèvre <***@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)
Ian Lance Taylor
2014-07-20 19:04:43 UTC
Permalink
On Sun, Jul 20, 2014 at 10:49 AM, Vincent Lefevre
Post by Vincent Lefevre
It appears that GCC can generate code that yields silent stack-heap
collision under GNU/Linux. I mean, the program doesn't crash (at least
not immediately), the memory just gets corrupted. At the same time,
this overrides the stack-size limit defined at the kernel level
(getrlimit system call / RLIMIT_STACK) because the kernel has no
chance to detect the collision (no page fault); thus this limit
doesn't protect the user, and the problem seems to be on GCC's side.
Why aren't such collisions detected by default?
Because it's expensive, and GCC takes the attitude that the C language
comes without safety guards.
Post by Vincent Lefevre
How can one tell GCC to detect them?
Use the -fstack-check option. Or -fsanitize=address. Or if you don't
need variable length arrays at all, you could use -Werror=vla.

Ian
Vincent Lefevre
2014-07-20 20:37:43 UTC
Permalink
Post by Ian Lance Taylor
On Sun, Jul 20, 2014 at 10:49 AM, Vincent Lefevre
Post by Vincent Lefevre
It appears that GCC can generate code that yields silent stack-heap
collision under GNU/Linux. I mean, the program doesn't crash (at least
not immediately), the memory just gets corrupted. At the same time,
this overrides the stack-size limit defined at the kernel level
(getrlimit system call / RLIMIT_STACK) because the kernel has no
chance to detect the collision (no page fault); thus this limit
doesn't protect the user, and the problem seems to be on GCC's side.
Why aren't such collisions detected by default?
Because it's expensive,
[...]

Really? This takes only a few instructions, doesn't it? And AFAIK,
the detection is needed only when the stack pointer decrement is
important (the problem occurs only when unmapped memory after the
stack is skipped). In most cases, VLA's are not used and GCC should
be able to determine an upper bound on the stack pointer decrement,
thus avoiding generating this detection on functions that don't need
it.
Post by Ian Lance Taylor
Post by Vincent Lefevre
How can one tell GCC to detect them?
Use the -fstack-check option.
[...]

Thanks, I hadn't tried this one.
--
Vincent Lefèvre <***@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)
Florian Weimer
2014-07-21 09:44:39 UTC
Permalink
Post by Vincent Lefevre
It appears that GCC can generate code that yields silent stack-heap
collision under GNU/Linux. I mean, the program doesn't crash (at least
not immediately), the memory just gets corrupted. At the same time,
this overrides the stack-size limit defined at the kernel level
(getrlimit system call / RLIMIT_STACK) because the kernel has no
chance to detect the collision (no page fault); thus this limit
doesn't protect the user, and the problem seems to be on GCC's side.
Why aren't such collisions detected by default?
You can try -fstack-check, but it instruments functions unnecessarily.
--
Florian Weimer / Red Hat Product Security
Loading...