Home | History | Annotate | Download | only in tests
      1 #include "../../config.h"
      2 
      3 #define _GNU_SOURCE
      4 #include <inttypes.h>
      5 #include <stdio.h>
      6 #include <pthread.h>
      7 #include <string.h>
      8 #include <stdlib.h>
      9 #include <sys/types.h>
     10 #include <unistd.h>
     11 #include <assert.h>
     12 #include <setjmp.h>
     13 #include <signal.h>
     14 #ifdef HAVE_GETPAGESIZE
     15 #include <unistd.h>
     16 #endif
     17 #include "../../include/valgrind.h"
     18 #include "../memcheck.h"
     19 
     20 typedef  unsigned long   UWord;
     21 typedef  UWord           Addr;
     22 #define VG_ROUNDDN(p, a)   ((Addr)(p) & ~((Addr)(a)-1))
     23 #define VG_ROUNDUP(p, a)   VG_ROUNDDN((p)+(a)-1, (a))
     24 
     25 static pthread_t children;
     26 
     27 // If != 0, will test addr description does not explode with
     28 // wrong stack registration.
     29 static int shake_with_wrong_registration = 0;
     30 
     31 /* Do whatever to have the stack grown enough that
     32    we can access below sp relatively safely */
     33 static void grow_the_stack(void)
     34 {
     35    int i;
     36    char m[5000];
     37    for (i = 0; i < sizeof(m); i++)
     38       m[i] = i;
     39    sprintf(m, "do whatever %d", i);
     40    if (strlen(m) > 1000)
     41       fprintf(stderr, "something went wrong with %s\n", m);
     42 }
     43 
     44 static char s[1000];
     45 static void describe (char* what, void* a)
     46 {
     47    fprintf(stderr, "describing %#" PRIxPTR " %s\n", (uintptr_t) a, what);
     48    sprintf(s, "v.info location %#" PRIxPTR, (uintptr_t) a);
     49    VALGRIND_MONITOR_COMMAND(s);
     50 }
     51 
     52 static void bad_things_below_sp (void)
     53 {
     54    int i;
     55    char *p = (char*)&i;
     56    describe ("1500 bytes below a local var", p-1500);
     57 }
     58 
     59 
     60 static volatile char *lowest_j;
     61 static jmp_buf goback;
     62 
     63 static void sigsegv_handler(int signr)
     64 {
     65    longjmp(goback, 1);
     66 }
     67 
     68 static void bad_things_till_guard_page(void)
     69 {
     70    char j = 0;
     71    char *p = &j;
     72 
     73    for (;;) {
     74       j = j + *p;
     75       p = p - 400;
     76       lowest_j = p;
     77    }
     78 }
     79 
     80 static int guess_pagesize(void)
     81 {
     82 #ifdef HAVE_GETPAGESIZE
     83    const int pagesize = getpagesize();
     84 #else
     85    const int pagesize = 4096; // let's say ?
     86 #endif
     87    return pagesize;
     88 }
     89 
     90 static void describe_many(void)
     91 {
     92    const int pagesize = guess_pagesize();
     93    describe ("discovered address giving SEGV in thread stack",
     94              (void*)lowest_j);
     95    describe ("byte just above highest guardpage byte",
     96              (void*) VG_ROUNDUP(lowest_j, pagesize));
     97    describe ("highest guardpage byte",
     98              (void*) VG_ROUNDUP(lowest_j, pagesize)-1);
     99    describe ("lowest guardpage byte",
    100              (void*) VG_ROUNDDN(lowest_j, pagesize));
    101    /* Cannot test the next byte, as we cannot predict how
    102       this byte will be described. */
    103 }
    104 
    105 static void* child_fn_0 ( void* arg )
    106 {
    107    grow_the_stack();
    108    bad_things_below_sp();
    109 
    110    if (setjmp(goback)) {
    111       describe_many();
    112    } else
    113       bad_things_till_guard_page();
    114 
    115    if (shake_with_wrong_registration) {
    116       // Do whatever stupid things we could imagine
    117       // with stack registration and see no explosion happens
    118       // Note: this is executed only if an arg is given to the program.
    119       //
    120 
    121       const int pgsz = guess_pagesize();
    122       int stackid;
    123 
    124       fprintf(stderr, "\n\nShaking after unregistering stack\n");
    125       // Assuming our first stack was automatically registered as nr 1
    126       VALGRIND_STACK_DEREGISTER(1);
    127       // Test with no stack registered
    128       describe_many();
    129 
    130       fprintf(stderr, "\n\nShaking with small stack\n");
    131       stackid = VALGRIND_STACK_REGISTER((void*) VG_ROUNDDN(&stackid, pgsz),
    132                                         (void*) VG_ROUNDUP(&stackid, pgsz));
    133       describe_many();
    134       VALGRIND_STACK_DEREGISTER(stackid);
    135 
    136       fprintf(stderr, "\n\nShaking with huge stack\n");
    137       stackid = VALGRIND_STACK_REGISTER((void*) 0x0,
    138                                         (void*) VG_ROUNDUP(&stackid, 2<<20));
    139       describe_many();
    140       VALGRIND_STACK_DEREGISTER(stackid);
    141 
    142 
    143    }
    144 
    145    return NULL;
    146 }
    147 
    148 int main(int argc, const char** argv)
    149 {
    150    struct sigaction sa;
    151    int r;
    152 
    153    shake_with_wrong_registration = argc > 1;
    154 
    155    /* We will discover the thread guard page using SEGV.
    156       So, prepare an handler. */
    157    sa.sa_handler = sigsegv_handler;
    158    sigemptyset(&sa.sa_mask);
    159    sa.sa_flags = 0;
    160 
    161    if (sigaction (SIGSEGV, &sa, NULL) != 0)
    162       perror("sigaction");
    163 
    164    grow_the_stack();
    165    bad_things_below_sp();
    166 
    167    pthread_attr_t attrs;
    168    r = pthread_attr_init(&attrs);
    169    assert(!r);
    170 
    171 #  if defined(VGO_solaris)
    172    /* Solaris needs to configure at least two page sizes to have
    173       a visible stack guard page. One page size is deducted for
    174       an implicit mmap red zone. */
    175    r = pthread_attr_setguardsize(&attrs, 2 * guess_pagesize());
    176    assert(!r);
    177 #  endif /* VGO_solaris */
    178 
    179    r = pthread_create(&children, &attrs, child_fn_0, NULL);
    180    assert(!r);
    181 
    182    r = pthread_attr_destroy(&attrs);
    183    assert(!r);
    184 
    185    r = pthread_join(children, NULL);
    186    assert(!r);
    187 
    188 
    189    return 0;
    190 }
    191 
    192