Home | History | Annotate | Download | only in tests
      1 
      2 // Simplified version of mempool.c, that is more oriented towards
      3 // checking that the description of invalid addresses is correct.
      4 
      5 #include <stdio.h>
      6 #include <unistd.h>
      7 #include "tests/sys_mman.h"
      8 #include <assert.h>
      9 #include <stdlib.h>
     10 
     11 #include "../memcheck.h"
     12 
     13 #define SUPERBLOCK_SIZE 100000
     14 #define REDZONE_SIZE 8
     15 
     16 typedef struct _level_list
     17 {
     18    struct _level_list *next;
     19    char *where;
     20    // Padding ensures the struct is the same size on 32-bit and 64-bit
     21    // machines.
     22    char padding[16 - 2*sizeof(char*)];
     23 } level_list;
     24 
     25 typedef struct _pool {
     26    char *mem;
     27    char *where;
     28    level_list *levels;
     29    int size, left;
     30    // Padding ensures the struct is the same size on 32-bit and 64-bit
     31    // machines.
     32    char padding[24 - 3*sizeof(char*)];
     33 } pool;
     34 
     35 pool *make_pool( int use_mmap )
     36 {
     37    pool *p;
     38 
     39    if (use_mmap) {
     40       p = (pool *)mmap(0, sizeof(pool), PROT_READ|PROT_WRITE|PROT_EXEC,
     41                        MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
     42       p->where = p->mem = (char *)mmap(NULL, SUPERBLOCK_SIZE,
     43                                        PROT_READ|PROT_WRITE|PROT_EXEC,
     44                                        MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
     45    } else {
     46       p = (pool *)malloc(sizeof(pool));
     47       p->where = p->mem = (char *)malloc(SUPERBLOCK_SIZE);
     48    }
     49 
     50    p->size = p->left = SUPERBLOCK_SIZE;
     51    p->levels = NULL;
     52    VALGRIND_MAKE_MEM_NOACCESS(p->where, SUPERBLOCK_SIZE);
     53    return p;
     54 }
     55 
     56 void push(pool *p, int use_mmap )
     57 {
     58    level_list *l;
     59 
     60    if (use_mmap)
     61       l = (level_list *)mmap(0, sizeof(level_list),
     62                              PROT_READ|PROT_WRITE|PROT_EXEC,
     63                              MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
     64    else
     65       l = (level_list *)malloc(sizeof(level_list));
     66 
     67    l->next = p->levels;
     68    l->where = p->where;
     69    VALGRIND_CREATE_MEMPOOL(l->where, REDZONE_SIZE, 0);
     70    p->levels = l;
     71 }
     72 
     73 void pop(pool *p, int use_mmap)
     74 {
     75    level_list *l = p->levels;
     76    p->levels = l->next;
     77    VALGRIND_DESTROY_MEMPOOL(l->where);
     78    VALGRIND_MAKE_MEM_NOACCESS(l->where, p->where-l->where);
     79    p->where = l->where;
     80    if (use_mmap)
     81       munmap(l, sizeof(level_list));
     82    else
     83       free(l);
     84 }
     85 
     86 void destroy_pool(pool *p, int use_mmap)
     87 {
     88    level_list *l = p->levels;
     89 
     90    while(l) {
     91       pop(p, use_mmap);
     92    }
     93    if (use_mmap) {
     94       munmap(p->mem, SUPERBLOCK_SIZE);
     95       munmap(p, sizeof(pool));
     96    } else {
     97       free(p->mem);
     98       free(p);
     99    }
    100 }
    101 
    102 char *allocate(pool *p, int size)
    103 {
    104    char *where;
    105    p->left -= size + (REDZONE_SIZE*2);
    106    where = p->where + REDZONE_SIZE;
    107    p->where += size + (REDZONE_SIZE*2);
    108    VALGRIND_MEMPOOL_ALLOC(p->levels->where, where, size);
    109    return where;
    110 }
    111 
    112 //-------------------------------------------------------------------------
    113 // Rest
    114 //-------------------------------------------------------------------------
    115 
    116 void test(void)
    117 {
    118    char *x1, *x2;
    119    char res = 0;
    120 
    121    // p1 is a malloc-backed pool
    122    pool *p1 = make_pool(0);
    123 
    124    // p2 is a mmap-backed pool
    125    pool *p2 = make_pool(1);
    126 
    127    push(p1, 0);
    128    push(p2, 1);
    129 
    130    x1 = allocate(p1, 10);
    131    x2 = allocate(p2, 20);
    132 
    133    fprintf(stderr,
    134            "\n------ out of range reads in malloc-backed pool ------\n\n");
    135    res += x1[-1];
    136    res += x1[10];
    137 
    138    fprintf(stderr,
    139            "\n------ out of range reads in mmap-backed pool ------\n\n");
    140    res += x2[-1]; // invalid
    141    res += x2[20]; // invalid
    142 
    143    fprintf(stderr,
    144            "\n------ read free in malloc-backed pool ------\n\n");
    145    VALGRIND_MEMPOOL_FREE(p1, x1);
    146    res += x1[5];
    147 
    148    fprintf(stderr,
    149            "\n------ read free in mmap-backed pool ------\n\n");
    150    VALGRIND_MEMPOOL_FREE(p2, x2);
    151    res += x2[11];
    152 
    153    fprintf(stderr,
    154            "\n------ double free in malloc-backed pool ------\n\n");
    155    VALGRIND_MEMPOOL_FREE(p1, x1);
    156 
    157    fprintf(stderr,
    158            "\n------ double free in mmap-backed pool ------\n\n");
    159    VALGRIND_MEMPOOL_FREE(p2, x2);
    160 
    161    {
    162       // test that redzone are still protected even if the user forgets
    163       // to mark the superblock noaccess.
    164       char superblock[100];
    165 
    166       VALGRIND_CREATE_MEMPOOL(superblock, REDZONE_SIZE, 0);
    167       // User should mark the superblock no access to benefit
    168       // from full Valgrind memcheck protection.
    169       // VALGRIND_MEMPOOL_ALLOC will however still ensure the
    170       // redzones are protected.
    171       VALGRIND_MEMPOOL_ALLOC(superblock, superblock+30, 10);
    172 
    173       res += superblock[30]; // valid
    174       res += superblock[39]; // valid
    175 
    176       fprintf(stderr,
    177               "\n------ 2 invalid access in 'no no-access superblock' ---\n\n");
    178       res += superblock[29]; // invalid
    179       res += superblock[40]; // invalid
    180 
    181       VALGRIND_DESTROY_MEMPOOL(superblock);
    182    }
    183    // claim res is used, so gcc can't nuke this all
    184    __asm__ __volatile__("" : : "r"(res));
    185 
    186    fprintf(stderr,
    187            "\n------ done ------\n\n");
    188    pop(p1, 0);
    189    pop(p2, 1);
    190    destroy_pool(p1, 0);
    191    destroy_pool(p2, 1);
    192 }
    193 
    194 int main(void)
    195 {
    196    test();
    197    return 0;
    198 }
    199