Home | History | Annotate | Download | only in tests
      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