[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: