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) 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) 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 167 #elif defined(VGP_x86_solaris) 168 extern ULong 169 do_syscall_WRK(UWord a1, UWord a2, UWord a3, 170 UWord a4, UWord a5, UWord a6, 171 UWord a7, UWord a8, 172 UWord syscall_no, 173 UInt *errflag); 174 asm( 175 ".text\n" 176 ".globl do_syscall_WRK\n" 177 "do_syscall_WRK:\n" 178 " movl 40(%esp), %ecx\n" /* assume syscall success */ 179 " movl $0, (%ecx)\n" 180 " movl 36(%esp), %eax\n" 181 " int $0x91\n" 182 " jnc 1f\n" /* jump if success */ 183 " movl 40(%esp), %ecx\n" /* syscall failed - set *errflag */ 184 " movl $1, (%ecx)\n" 185 "1: ret\n" 186 ".previous\n" 187 ); 188 189 #elif defined(VGP_amd64_solaris) 190 extern ULong 191 do_syscall_WRK(UWord a1, UWord a2, UWord a3, 192 UWord a4, UWord a5, UWord a6, 193 UWord a7, UWord a8, 194 UWord syscall_no, 195 UInt *errflag); 196 asm( 197 ".text\n" 198 ".globl do_syscall_WRK\n" 199 "do_syscall_WRK:\n" 200 " movq %rcx, %r10\n" /* pass rcx in r10 instead */ 201 " movq 32(%rsp), %rcx\n" /* assume syscall success */ 202 " movl $0, (%rcx)\n" 203 " movq 24(%rsp), %rax\n" 204 " syscall\n" 205 " jnc 1f\n" /* jump if success */ 206 " movq 32(%rsp), %rcx\n" /* syscall failed - set *errflag */ 207 " movl $1, (%rcx)\n" 208 "1: ret\n" 209 ".previous\n" 210 ); 211 212 #else 213 // Ensure the file compiles even if the syscall nr is not defined. 214 #ifndef __NR_mprotect 215 #define __NR_mprotect 0 216 #endif 217 UWord do_syscall_WRK (UWord syscall_no, 218 UWord a1, UWord a2, UWord a3, 219 UWord a4, UWord a5, UWord a6 220 ) 221 { 222 // not implemented. vgtest prereq should avoid this to be called. 223 return -1; 224 } 225 #endif 226 227 228 229 char **b10; 230 char *interior_ptrs[3]; 231 int mprotect_result = 0; 232 static void non_simd_mprotect (long tid, void* addr, long len) 233 { 234 #if defined(VGP_x86_solaris) || defined(VGP_amd64_solaris) 235 UInt err = 0; 236 mprotect_result = do_syscall_WRK((UWord) addr, len, PROT_NONE, 237 0, 0, 0, 0, 0, SYS_mprotect, 238 &err); 239 if (err) 240 mprotect_result = -1; 241 #else 242 mprotect_result = do_syscall_WRK(__NR_mprotect, 243 (UWord) addr, len, PROT_NONE, 244 0, 0, 0); 245 #endif 246 } 247 248 // can this work without global variable for return value? 249 static void my_mprotect_none(void* addr, long len) 250 { 251 if (RUNNING_ON_VALGRIND) 252 (void) VALGRIND_NON_SIMD_CALL2(non_simd_mprotect, 253 addr, 254 len); 255 else 256 mprotect_result = mprotect(addr, 257 len, 258 PROT_NONE); 259 } 260 261 void f(void) 262 { 263 long pagesize; 264 #define RNDPAGEDOWN(a) ((long)a & ~(pagesize-1)) 265 int i; 266 const int nr_ptr = (10000 * 20)/sizeof(char*); 267 268 b10 = calloc (nr_ptr * sizeof(char*), 1); 269 for (i = 0; i < nr_ptr; i++) 270 b10[i] = (char*)b10; 271 b10[4000] = malloc (1000); 272 273 fprintf(stderr, "expecting no leaks\n"); 274 fflush(stderr); 275 VALGRIND_DO_LEAK_CHECK; 276 277 // make b10[4000] undefined. This should create a leak. 278 (void) VALGRIND_MAKE_MEM_UNDEFINED (&b10[4000], sizeof(char*)); 279 fprintf(stderr, "expecting a leak\n"); 280 fflush(stderr); 281 VALGRIND_DO_LEAK_CHECK; 282 283 // make b10[4000] defined again. 284 (void) VALGRIND_MAKE_MEM_DEFINED (&b10[4000], sizeof(char*)); 285 286 // now make some bricolage to have some pages around b10[4000] 287 // unreadable. The leak check should recover from that 288 // thanks to a SEGV handler and a setjmp/longjmp. 289 // This setjmp/longjmp is useful if there is a desync between 290 // the aspacemgr and the real pages mapping. 291 // To have such a discrepancy, we resort on a non SIMD call 292 // to mprotect the pages : as this syscall will not be seen 293 // by Valgrind core, the aspacemgr will not get a chance 294 // to stay synchronised. 295 pagesize = sysconf(_SC_PAGE_SIZE); 296 if (pagesize == -1) 297 perror ("sysconf failed"); 298 299 my_mprotect_none((void*) RNDPAGEDOWN(&b10[4000]), 2 * pagesize); 300 fprintf(stderr, "mprotect result %d\n", mprotect_result); 301 302 fprintf(stderr, "expecting a leak again\n"); 303 fflush(stderr); 304 VALGRIND_DO_LEAK_CHECK; 305 306 my_mprotect_none((void*) RNDPAGEDOWN(&b10[0]), 307 RNDPAGEDOWN(&(b10[nr_ptr-1])) 308 - RNDPAGEDOWN(&(b10[0]))); 309 fprintf(stderr, "full mprotect result %d\n", mprotect_result); 310 311 fprintf(stderr, "expecting a leak again after full mprotect\n"); 312 fflush(stderr); 313 VALGRIND_DO_LEAK_CHECK; 314 315 // allocate memory but keep only interior pointers to trigger various 316 // heuristics 317 // Allocate some memory: 318 interior_ptrs[0] = calloc (nr_ptr * sizeof(char*), 1); 319 320 // Inner pointer after 3 sizeT: triggers the stdstring heuristic: 321 interior_ptrs[2] = interior_ptrs[0] + 3 * sizeof(size_t); 322 323 // Inner pointer after 1 ULong: triggers the length64 heuristic: 324 interior_ptrs[1] = interior_ptrs[0] + sizeof(unsigned long); 325 326 // Inner pointer after a size: triggers the newarray heuristics. 327 interior_ptrs[0] += sizeof(size_t); 328 329 my_mprotect_none( (void*) RNDPAGEDOWN((interior_ptrs[0] - sizeof(size_t))), 330 RNDPAGEDOWN(nr_ptr * sizeof(char*))); 331 fprintf(stderr, "mprotect result %d\n", mprotect_result); 332 333 fprintf(stderr, "expecting heuristic not to crash after full mprotect\n"); 334 fflush(stderr); 335 VALGRIND_DO_LEAK_CHECK; 336 337 fprintf(stderr, "finished\n"); 338 } 339 340 int main(void) 341 { 342 DECLARE_LEAK_COUNTERS; 343 344 GET_INITIAL_LEAK_COUNTS; 345 346 f(); // see leak-cases.c 347 348 349 GET_FINAL_LEAK_COUNTS; 350 351 PRINT_LEAK_COUNTS(stderr); 352 353 return 0; 354 } 355