1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include "../memcheck.h"
6 #include "leak.h"
7 #include <sys/mman.h>
8 #include <sys/syscall.h>
9
10 typedef unsigned int UInt;
11 typedef unsigned long UWord;
12 typedef unsigned long long int ULong;
13
14 // Below code is copied from m_syscall.c
15 // Refer to this file for syscall convention.
16 #if defined(VGP_x86_linux)
17 extern UWord do_syscall_WRK (UWord syscall_no,
18 UWord a1, UWord a2, UWord a3,
19 UWord a4, UWord a5, UWord a6
20 );
21 asm(
22 ".text\n"
23 ".globl do_syscall_WRK\n"
24 "do_syscall_WRK:\n"
25 " push %esi\n"
26 " push %edi\n"
27 " push %ebx\n"
28 " push %ebp\n"
29 " movl 16+ 4(%esp),%eax\n"
30 " movl 16+ 8(%esp),%ebx\n"
31 " movl 16+12(%esp),%ecx\n"
32 " movl 16+16(%esp),%edx\n"
33 " movl 16+20(%esp),%esi\n"
34 " movl 16+24(%esp),%edi\n"
35 " movl 16+28(%esp),%ebp\n"
36 " int $0x80\n"
37 " popl %ebp\n"
38 " popl %ebx\n"
39 " popl %edi\n"
40 " popl %esi\n"
41 " ret\n"
42 ".previous\n"
43 );
44
45 #elif defined(VGP_amd64_linux)
46 extern UWord do_syscall_WRK (
47 UWord syscall_no,
48 UWord a1, UWord a2, UWord a3,
49 UWord a4, UWord a5, UWord a6
50 );
51 asm(
52 ".text\n"
53 ".globl do_syscall_WRK\n"
54 "do_syscall_WRK:\n"
55 " movq %rdi, %rax\n"
56 " movq %rsi, %rdi\n"
57 " movq %rdx, %rsi\n"
58 " movq %rcx, %rdx\n"
59 " movq %r8, %r10\n"
60 " movq %r9, %r8\n"
61 " movq 8(%rsp), %r9\n" /* last arg from stack */
62 " syscall\n"
63 " ret\n"
64 ".previous\n"
65 );
66
67 #elif defined(VGP_ppc32_linux)
68 extern ULong do_syscall_WRK (
69 UWord syscall_no,
70 UWord a1, UWord a2, UWord a3,
71 UWord a4, UWord a5, UWord a6
72 );
73 asm(
74 ".text\n"
75 ".globl do_syscall_WRK\n"
76 "do_syscall_WRK:\n"
77 " mr 0,3\n"
78 " mr 3,4\n"
79 " mr 4,5\n"
80 " mr 5,6\n"
81 " mr 6,7\n"
82 " mr 7,8\n"
83 " mr 8,9\n"
84 " sc\n" /* syscall: sets %cr0.so on error */
85 " mfcr 4\n" /* %cr -> low word of return var */
86 " rlwinm 4,4,4,31,31\n" /* rotate flag bit so to lsb, and mask it */
87 " blr\n" /* and return */
88 ".previous\n"
89 );
90
91 #elif defined(VGP_arm_linux)
92 extern UWord do_syscall_WRK (
93 UWord a1, UWord a2, UWord a3,
94 UWord a4, UWord a5, UWord a6,
95 UWord syscall_no
96 );
97 asm(
98 ".text\n"
99 ".globl do_syscall_WRK\n"
100 "do_syscall_WRK:\n"
101 " push {r4, r5, r7}\n"
102 " ldr r4, [sp, #12]\n"
103 " ldr r5, [sp, #16]\n"
104 " ldr r7, [sp, #20]\n"
105 " svc 0x0\n"
106 " pop {r4, r5, r7}\n"
107 " bx lr\n"
108 ".previous\n"
109 );
110
111 #elif defined(VGP_s390x_linux)
do_syscall_WRK(UWord syscall_no,UWord arg1,UWord arg2,UWord arg3,UWord arg4,UWord arg5,UWord arg6)112 UWord do_syscall_WRK (
113 UWord syscall_no,
114 UWord arg1, UWord arg2, UWord arg3,
115 UWord arg4, UWord arg5, UWord arg6
116 )
117 {
118 register UWord __arg1 asm("2") = arg1;
119 register UWord __arg2 asm("3") = arg2;
120 register UWord __arg3 asm("4") = arg3;
121 register UWord __arg4 asm("5") = arg4;
122 register UWord __arg5 asm("6") = arg5;
123 register UWord __arg6 asm("7") = arg6;
124 register ULong __svcres asm("2");
125
126 __asm__ __volatile__ (
127 "lgr %%r1,%1\n\t"
128 "svc 0\n\t"
129 : "=d" (__svcres)
130 : "a" (syscall_no),
131 "0" (__arg1),
132 "d" (__arg2),
133 "d" (__arg3),
134 "d" (__arg4),
135 "d" (__arg5),
136 "d" (__arg6)
137 : "1", "cc", "memory");
138
139 return (UWord) (__svcres);
140 }
141
142 #elif defined(VGP_mips64_linux)
do_syscall_WRK(UWord syscall_no,UWord a1,UWord a2,UWord a3,UWord a4,UWord a5,UWord a6)143 extern UWord do_syscall_WRK (
144 UWord syscall_no,
145 UWord a1, UWord a2, UWord a3,
146 UWord a4, UWord a5, UWord a6
147 )
148 {
149 UWord out;
150 __asm__ __volatile__ (
151 "move $v0, %1\n\t"
152 "move $a0, %2\n\t"
153 "move $a1, %3\n\t"
154 "move $a2, %4\n\t"
155 "move $a3, %5\n\t"
156 "move $8, %6\n\t" /* We use numbers because some compilers */
157 "move $9, %7\n\t" /* don't recognize $a4 and $a5 */
158 "syscall\n"
159 "move %0, $v0\n\t"
160 : /*out*/ "=r" (out)
161 : "r"(syscall_no), "r"(a1), "r"(a2), "r"(a3),
162 "r"(a4), "r"(a5), "r"(a6)
163 : "v0", "v1", "a0", "a1", "a2", "a3", "$8", "$9");
164 return out;
165 }
166 #elif defined(VGP_tilegx_linux)
do_syscall_WRK(UWord syscall_no,UWord a1,UWord a2,UWord a3,UWord a4,UWord a5,UWord a6)167 extern UWord do_syscall_WRK (
168 UWord syscall_no,
169 UWord a1, UWord a2, UWord a3,
170 UWord a4, UWord a5, UWord a6
171 )
172 {
173 UWord out;
174 __asm__ __volatile__ (
175 "move r10, %1\n\t"
176 "move r0, %2\n\t"
177 "move r1, %3\n\t"
178 "move r2, %4\n\t"
179 "move r3, %5\n\t"
180 "move r4, %6\n\t"
181 "move r5, %7\n\t"
182 "swint1 \n\t"
183 "move %0, r0\n\t"
184 : /*out*/ "=r" (out)
185 : "r"(syscall_no), "r"(a1), "r"(a2), "r"(a3),
186 "r"(a4), "r"(a5), "r"(a6)
187 : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r10");
188 return out;
189 }
190
191 #elif defined(VGP_x86_solaris)
192 extern ULong
193 do_syscall_WRK(UWord a1, UWord a2, UWord a3,
194 UWord a4, UWord a5, UWord a6,
195 UWord a7, UWord a8,
196 UWord syscall_no,
197 UInt *errflag);
198 asm(
199 ".text\n"
200 ".globl do_syscall_WRK\n"
201 "do_syscall_WRK:\n"
202 " movl 40(%esp), %ecx\n" /* assume syscall success */
203 " movl $0, (%ecx)\n"
204 " movl 36(%esp), %eax\n"
205 " int $0x91\n"
206 " jnc 1f\n" /* jump if success */
207 " movl 40(%esp), %ecx\n" /* syscall failed - set *errflag */
208 " movl $1, (%ecx)\n"
209 "1: ret\n"
210 ".previous\n"
211 );
212
213 #elif defined(VGP_amd64_solaris)
214 extern ULong
215 do_syscall_WRK(UWord a1, UWord a2, UWord a3,
216 UWord a4, UWord a5, UWord a6,
217 UWord a7, UWord a8,
218 UWord syscall_no,
219 UInt *errflag);
220 asm(
221 ".text\n"
222 ".globl do_syscall_WRK\n"
223 "do_syscall_WRK:\n"
224 " movq %rcx, %r10\n" /* pass rcx in r10 instead */
225 " movq 32(%rsp), %rcx\n" /* assume syscall success */
226 " movl $0, (%rcx)\n"
227 " movq 24(%rsp), %rax\n"
228 " syscall\n"
229 " jnc 1f\n" /* jump if success */
230 " movq 32(%rsp), %rcx\n" /* syscall failed - set *errflag */
231 " movl $1, (%rcx)\n"
232 "1: ret\n"
233 ".previous\n"
234 );
235
236 #else
237 // Ensure the file compiles even if the syscall nr is not defined.
238 #ifndef __NR_mprotect
239 #define __NR_mprotect 0
240 #endif
do_syscall_WRK(UWord syscall_no,UWord a1,UWord a2,UWord a3,UWord a4,UWord a5,UWord a6)241 UWord do_syscall_WRK (UWord syscall_no,
242 UWord a1, UWord a2, UWord a3,
243 UWord a4, UWord a5, UWord a6
244 )
245 {
246 // not implemented. vgtest prereq should avoid this to be called.
247 return -1;
248 }
249 #endif
250
251
252
253 char **b10;
254 char *interior_ptrs[3];
255 int mprotect_result = 0;
non_simd_mprotect(long tid,void * addr,long len)256 static void non_simd_mprotect (long tid, void* addr, long len)
257 {
258 #if defined(VGP_x86_solaris) || defined(VGP_amd64_solaris)
259 UInt err = 0;
260 mprotect_result = do_syscall_WRK((UWord) addr, len, PROT_NONE,
261 0, 0, 0, 0, 0, SYS_mprotect,
262 &err);
263 if (err)
264 mprotect_result = -1;
265 #else
266 mprotect_result = do_syscall_WRK(__NR_mprotect,
267 (UWord) addr, len, PROT_NONE,
268 0, 0, 0);
269 #endif
270 }
271
272 // can this work without global variable for return value?
my_mprotect_none(void * addr,long len)273 static void my_mprotect_none(void* addr, long len)
274 {
275 if (RUNNING_ON_VALGRIND)
276 (void) VALGRIND_NON_SIMD_CALL2(non_simd_mprotect,
277 addr,
278 len);
279 else
280 mprotect_result = mprotect(addr,
281 len,
282 PROT_NONE);
283 }
284
f(void)285 void f(void)
286 {
287 long pagesize;
288 #define RNDPAGEDOWN(a) ((long)a & ~(pagesize-1))
289 int i;
290 const int nr_ptr = (10000 * 4)/sizeof(char*);
291
292 b10 = calloc (nr_ptr * sizeof(char*), 1);
293 for (i = 0; i < nr_ptr; i++)
294 b10[i] = (char*)b10;
295 b10[4000] = malloc (1000);
296
297 fprintf(stderr, "expecting no leaks\n");
298 fflush(stderr);
299 VALGRIND_DO_LEAK_CHECK;
300
301 // make b10[4000] undefined. This should create a leak.
302 (void) VALGRIND_MAKE_MEM_UNDEFINED (&b10[4000], sizeof(char*));
303 fprintf(stderr, "expecting a leak\n");
304 fflush(stderr);
305 VALGRIND_DO_LEAK_CHECK;
306
307 // make b10[4000] defined again.
308 (void) VALGRIND_MAKE_MEM_DEFINED (&b10[4000], sizeof(char*));
309
310 // now make some bricolage to have some pages around b10[4000]
311 // unreadable. The leak check should recover from that
312 // thanks to a SEGV handler and a setjmp/longjmp.
313 // This setjmp/longjmp is useful if there is a desync between
314 // the aspacemgr and the real pages mapping.
315 // To have such a discrepancy, we resort on a non SIMD call
316 // to mprotect the pages : as this syscall will not be seen
317 // by Valgrind core, the aspacemgr will not get a chance
318 // to stay synchronised.
319 pagesize = sysconf(_SC_PAGE_SIZE);
320 if (pagesize == -1)
321 perror ("sysconf failed");
322
323 my_mprotect_none((void*) RNDPAGEDOWN(&b10[4000]), 2 * pagesize);
324 fprintf(stderr, "mprotect result %d\n", mprotect_result);
325
326 fprintf(stderr, "expecting a leak again\n");
327 fflush(stderr);
328 VALGRIND_DO_LEAK_CHECK;
329
330 my_mprotect_none((void*) RNDPAGEDOWN(&b10[0]),
331 RNDPAGEDOWN(&(b10[nr_ptr-1]))
332 - RNDPAGEDOWN(&(b10[0])));
333 fprintf(stderr, "full mprotect result %d\n", mprotect_result);
334
335 fprintf(stderr, "expecting a leak again after full mprotect\n");
336 fflush(stderr);
337 VALGRIND_DO_LEAK_CHECK;
338
339 // allocate memory but keep only interior pointers to trigger various
340 // heuristics
341 // Allocate some memory:
342 interior_ptrs[0] = calloc (nr_ptr * sizeof(char*), 1);
343
344 // Inner pointer after 3 sizeT: triggers the stdstring heuristic:
345 interior_ptrs[2] = interior_ptrs[0] + 3 * sizeof(size_t);
346
347 // Inner pointer after 1 ULong: triggers the length64 heuristic:
348 interior_ptrs[1] = interior_ptrs[0] + sizeof(unsigned long);
349
350 // Inner pointer after a size: triggers the newarray heuristics.
351 interior_ptrs[0] += sizeof(size_t);
352
353 my_mprotect_none( (void*) RNDPAGEDOWN((interior_ptrs[0] - sizeof(size_t))),
354 RNDPAGEDOWN(nr_ptr * sizeof(char*)));
355 fprintf(stderr, "mprotect result %d\n", mprotect_result);
356
357 fprintf(stderr, "expecting heuristic not to crash after full mprotect\n");
358 fflush(stderr);
359 VALGRIND_DO_LEAK_CHECK;
360
361 fprintf(stderr, "finished\n");
362 }
363
main(void)364 int main(void)
365 {
366 DECLARE_LEAK_COUNTERS;
367
368 GET_INITIAL_LEAK_COUNTS;
369
370 f(); // see leak-cases.c
371
372
373 GET_FINAL_LEAK_COUNTS;
374
375 PRINT_LEAK_COUNTS(stderr);
376
377 return 0;
378 }
379