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
10typedef unsigned int             UInt;
11typedef unsigned long            UWord;
12typedef 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)
17extern UWord do_syscall_WRK (UWord syscall_no,
18                             UWord a1, UWord a2, UWord a3,
19                             UWord a4, UWord a5, UWord a6
20                             );
21asm(
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)
46extern UWord do_syscall_WRK (
47          UWord syscall_no,
48          UWord a1, UWord a2, UWord a3,
49          UWord a4, UWord a5, UWord a6
50       );
51asm(
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)
68extern ULong do_syscall_WRK (
69          UWord syscall_no,
70          UWord a1, UWord a2, UWord a3,
71          UWord a4, UWord a5, UWord a6
72       );
73asm(
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)
92extern UWord do_syscall_WRK (
93          UWord a1, UWord a2, UWord a3,
94          UWord a4, UWord a5, UWord a6,
95          UWord syscall_no
96       );
97asm(
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)
112UWord 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)
143extern 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)
167extern 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)
192extern ULong
193do_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);
198asm(
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)
214extern ULong
215do_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);
220asm(
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
241UWord 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
253char **b10;
254char *interior_ptrs[3];
255int mprotect_result = 0;
256static 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?
273static 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
285void 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
364int 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