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