• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
grow_the_stack(void)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];
describe(char * what,void * a)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 
bad_things_below_sp(void)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 
sigsegv_handler(int signr)63 static void sigsegv_handler(int signr)
64 {
65    longjmp(goback, 1);
66 }
67 
bad_things_till_guard_page(void)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 
guess_pagesize(void)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 
describe_many(void)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 
child_fn_0(void * arg)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 
main(int argc,const char ** argv)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