Bug#866686: libc6: The stack clash fix is insufficient, because it doesn't increase the stack guard gap for thread stacks
Package: libc6
Severity: important
Tags: upstream
Dear Maintainer,
*** Reporter, please consider answering these questions, where appropriate ***
* What led up to the situation?
The stack clash patch modifies the kernel so that there is 1MB gap below the
stack. If a single function allocates 1MB or less space on its stack frame, it
would hit the gap and it couldn't overwrite heap.
However, the stack gap for thread stacks is still 4k, meaning that the whole
stack clash exploit could be still used against multithreaded programs.
* What exactly did you do (or not do) that was effective (or
ineffective)?
* What was the outcome of this action?
Run pmap on any multithreaded process and you can see that there is only 4k gap
below the thread stack, for example:
00007f931d83d000 4K ----- [ anon ]
00007f931d83e000 8192K rw--- [ anon ]
00007f931e03e000 4K ----- [ anon ]
00007f931e03f000 8192K rw--- [ anon ]
00007f931e83f000 4K ----- [ anon ]
00007f931e840000 8192K rw--- [ anon ]
00007f931f040000 4K ----- [ anon ]
00007f931f041000 8192K rw--- [ anon ]
00007f931f841000 4K ----- [ anon ]
00007f931f842000 8192K rw--- [ anon ]
* What outcome did you expect instead?
The libc should be modified so that there is 1MB guard area below the thread
stack.
This program can show the stack clash problem with multithreaded programs - it
creates 10 threads, it finds the first stack gap (a memory area with protection
"-----") and it maps a page just below the stack gap.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <pthread.h>
void *thread(void *p)
{
pause();
return NULL;
}
int main(void)
{
int i;
char command[256];
FILE *p;
unsigned long addr;
void *ptr;
for (i = 0; i < 10; i++) {
pthread_t t;
if (pthread_create(&t, NULL, thread, NULL))
fprintf(stderr, "pthread_create failed\n"), exit(1);
}
sleep(1);
snprintf(command, sizeof command, "pmap %d|grep -- -----", getpid());
p = popen(command, "r");
if (!p) perror("popen"), exit(1);
if (fscanf(p, "%lx", &addr) != 1)
fprintf(stderr, "fscanf failed\n"), exit(1);
addr -= 4096;
ptr = mmap((void *)addr, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE |
MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED) perror("mmap"), exit(1);
snprintf(command, sizeof command, "pmap %d", getpid());
system(command);
printf("wanted address = %lx, mapped address = %p\n", addr, ptr);
return 0;
}
When you run the program, you get an output like this, which indicates that it
is possible to map anonymous memory just 4k below the bottom of the thread
stack. Therefore, the code that executes in the thread is prone to the stack
clash bug.
11937: ./a.out
0000000000400000 4K r-x-- a.out
0000000000600000 4K r---- a.out
0000000000601000 4K rw--- a.out
0000000000d3d000 132K rw--- [ anon ]
00007f59f3ae8000 4K rw--- [ anon ] <---- anonymously mapped memory
00007f59f3ae9000 4K ----- [ anon ] <---- guard page
00007f59f3aea000 8192K rw--- [ anon ] <---- thread stack
00007f59f42ea000 4K ----- [ anon ]
00007f59f42eb000 8192K rw--- [ anon ]
00007f59f4aeb000 4K ----- [ anon ]
00007f59f4aec000 8192K rw--- [ anon ]
00007f59f52ec000 4K ----- [ anon ]
00007f59f52ed000 8192K rw--- [ anon ]
00007f59f5aed000 4K ----- [ anon ]
00007f59f5aee000 8192K rw--- [ anon ]
00007f59f62ee000 4K ----- [ anon ]
00007f59f62ef000 8192K rw--- [ anon ]
00007f59f6aef000 4K ----- [ anon ]
00007f59f6af0000 8192K rw--- [ anon ]
00007f59f72f0000 4K ----- [ anon ]
00007f59f72f1000 8192K rw--- [ anon ]
00007f59f7af1000 4K ----- [ anon ]
00007f59f7af2000 8192K rw--- [ anon ]
00007f59f82f2000 4K ----- [ anon ]
00007f59f82f3000 8192K rw--- [ anon ]
00007f59f8af3000 1620K r-x-- libc-2.24.so
00007f59f8c88000 2048K ----- libc-2.24.so
00007f59f8e88000 16K r---- libc-2.24.so
00007f59f8e8c000 8K rw--- libc-2.24.so
00007f59f8e8e000 16K rw--- [ anon ]
00007f59f8e92000 96K r-x-- libpthread-2.24.so
00007f59f8eaa000 2044K ----- libpthread-2.24.so
00007f59f90a9000 4K r---- libpthread-2.24.so
00007f59f90aa000 4K rw--- libpthread-2.24.so
00007f59f90ab000 16K rw--- [ anon ]
00007f59f90af000 140K r-x-- ld-2.24.so
00007f59f92a8000 8K rw--- [ anon ]
00007f59f92cf000 12K rw--- [ anon ]
00007f59f92d2000 4K r---- ld-2.24.so
00007f59f92d3000 4K rw--- ld-2.24.so
00007f59f92d4000 4K rw--- [ anon ]
00007ffdc4ae6000 132K rw--- [ stack ]
00007ffdc4b85000 8K r---- [ anon ]
00007ffdc4b87000 8K r-x-- [ anon ]
total 88300K
wanted address = 7f59f3ae8000, mapped address = 0x7f59f3ae8000
-- System Information:
Debian Release: 9.0
APT prefers stable
APT policy: (500, 'stable')
Architecture: amd64 (x86_64)
Foreign Architectures: i386, arm64, mips, mips64el, ppc64el, s390x
Kernel: Linux 4.4.74 (SMP w/12 CPU cores; PREEMPT)
Locale: LANG=cs_CZ.utf8, LC_CTYPE=cs_CZ.utf8 (charmap=UTF-8), LANGUAGE=cs_CZ.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: sysvinit (via /sbin/init)
Reply to: