• Bug#866686: libc6: The stack clash fix is insufficient, because it does

    From Aurelien Jarno@21:1/5 to Mikulas Patocka on Wed Jul 5 13:00:01 2017
    XPost: linux.debian.bugs.dist

    On 2017-06-30 23:56, Mikulas Patocka wrote:
    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.

    The guard size for threads can be changed by calling the pthread_attr_setguardsize function before the thread creation. POSIX
    clearly defines the default thread guard size to be PAGESIZE:

    "The default value of the guardsize attribute is PAGESIZE bytes. The
    actual value of PAGESIZE is implementation-dependent and may not be the
    same on all implementations."

    * 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.


    I am not sure we should violate POSIX there. This probably has to be
    discussed with upstream first.

    --
    Aurelien Jarno GPG: 4096R/1DDD8C9B aurelien@aurel32.net http://www.aurel32.net

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mikulas Patocka@21:1/5 to All on Sat Jul 1 01:10:02 2017
    XPost: linux.debian.bugs.dist

    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)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)