// Simplified version of mempool.c, that is more oriented towards // checking that the description of invalid addresses is correct. #include <stdio.h> #include <unistd.h> #include "tests/sys_mman.h" #include <assert.h> #include <stdlib.h> #include "../memcheck.h" #define SUPERBLOCK_SIZE 100000 #define REDZONE_SIZE 8 typedef struct _level_list { struct _level_list *next; char *where; // Padding ensures the struct is the same size on 32-bit and 64-bit // machines. char padding[16 - 2*sizeof(char*)]; } level_list; typedef struct _pool { char *mem; char *where; level_list *levels; int size, left; // Padding ensures the struct is the same size on 32-bit and 64-bit // machines. char padding[24 - 3*sizeof(char*)]; } pool; pool *make_pool( int use_mmap ) { pool *p; if (use_mmap) { p = (pool *)mmap(0, sizeof(pool), PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); p->where = p->mem = (char *)mmap(NULL, SUPERBLOCK_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); } else { p = (pool *)malloc(sizeof(pool)); p->where = p->mem = (char *)malloc(SUPERBLOCK_SIZE); } p->size = p->left = SUPERBLOCK_SIZE; p->levels = NULL; VALGRIND_MAKE_MEM_NOACCESS(p->where, SUPERBLOCK_SIZE); return p; } void push(pool *p, int use_mmap ) { level_list *l; if (use_mmap) l = (level_list *)mmap(0, sizeof(level_list), PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); else l = (level_list *)malloc(sizeof(level_list)); l->next = p->levels; l->where = p->where; VALGRIND_CREATE_MEMPOOL(l->where, REDZONE_SIZE, 0); p->levels = l; } void pop(pool *p, int use_mmap) { level_list *l = p->levels; p->levels = l->next; VALGRIND_DESTROY_MEMPOOL(l->where); VALGRIND_MAKE_MEM_NOACCESS(l->where, p->where-l->where); p->where = l->where; if (use_mmap) munmap(l, sizeof(level_list)); else free(l); } void destroy_pool(pool *p, int use_mmap) { level_list *l = p->levels; while(l) { pop(p, use_mmap); } if (use_mmap) { munmap(p->mem, SUPERBLOCK_SIZE); munmap(p, sizeof(pool)); } else { free(p->mem); free(p); } } char *allocate(pool *p, int size) { char *where; p->left -= size + (REDZONE_SIZE*2); where = p->where + REDZONE_SIZE; p->where += size + (REDZONE_SIZE*2); VALGRIND_MEMPOOL_ALLOC(p->levels->where, where, size); return where; } //------------------------------------------------------------------------- // Rest //------------------------------------------------------------------------- void test(void) { char *x1, *x2; char res = 0; // p1 is a malloc-backed pool pool *p1 = make_pool(0); // p2 is a mmap-backed pool pool *p2 = make_pool(1); push(p1, 0); push(p2, 1); x1 = allocate(p1, 10); x2 = allocate(p2, 20); fprintf(stderr, "\n------ out of range reads in malloc-backed pool ------\n\n"); res += x1[-1]; res += x1[10]; fprintf(stderr, "\n------ out of range reads in mmap-backed pool ------\n\n"); res += x2[-1]; // invalid res += x2[20]; // invalid fprintf(stderr, "\n------ read free in malloc-backed pool ------\n\n"); VALGRIND_MEMPOOL_FREE(p1, x1); res += x1[5]; fprintf(stderr, "\n------ read free in mmap-backed pool ------\n\n"); VALGRIND_MEMPOOL_FREE(p2, x2); res += x2[11]; fprintf(stderr, "\n------ double free in malloc-backed pool ------\n\n"); VALGRIND_MEMPOOL_FREE(p1, x1); fprintf(stderr, "\n------ double free in mmap-backed pool ------\n\n"); VALGRIND_MEMPOOL_FREE(p2, x2); { // test that redzone are still protected even if the user forgets // to mark the superblock noaccess. char superblock[100]; VALGRIND_CREATE_MEMPOOL(superblock, REDZONE_SIZE, 0); // User should mark the superblock no access to benefit // from full Valgrind memcheck protection. // VALGRIND_MEMPOOL_ALLOC will however still ensure the // redzones are protected. VALGRIND_MEMPOOL_ALLOC(superblock, superblock+30, 10); res += superblock[30]; // valid res += superblock[39]; // valid fprintf(stderr, "\n------ 2 invalid access in 'no no-access superblock' ---\n\n"); res += superblock[29]; // invalid res += superblock[40]; // invalid VALGRIND_DESTROY_MEMPOOL(superblock); } // claim res is used, so gcc can't nuke this all __asm__ __volatile__("" : : "r"(res)); fprintf(stderr, "\n------ done ------\n\n"); pop(p1, 0); pop(p2, 1); destroy_pool(p1, 0); destroy_pool(p2, 1); } int main(void) { test(); return 0; }