DF-0732 / leakcheck.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | /* * DF-0732 leak checker โ definitively detects REAL heap residue in the * returned ACL list, distinguishing it from valid adder MACs. * * Real heap residue (with use_malloc_pattern=1) contains the slab * WEIRD_ADDR pattern 0xdeadc0de (sys/kern/kern_slaballoc.c). Valid adder * MACs are de:ad:be:XX:XX:XX. This checker scans every returned 6-byte * slot and classifies it: * - MAC_ADDER : de ad be ?? ?? ?? (valid adder entry) * - MAC_SEED : aa bb cc dd ?? ?? (valid pre-seeded entry) * - WEIRD : contains de ad c0 de (slab freed-object residue = LEAK) * - ZERO : all six bytes 0x00 (M_ZERO'd, never written = safe) * - OTHER : anything else (suspicious โ print hex) * * BUGGY module: expect WEIRD hits (heap residue leaked). * FIXED module: expect ZERO WEIRD hits (M_ZERO + lock-before-read). * * Build: cc -O2 -pthread -o leakcheck leakcheck.c * Run: ./leakcheck [iterations] */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <pthread.h> struct df0732_list_req { void *buf; size_t len; size_t *ret_len; }; #define DF0732_IOCTL_LIST _IOW('D', 1, struct df0732_list_req) #define BUF_ENTRIES 8192 #define BUF_SIZE (BUF_ENTRIES * 6) static volatile int stop_flag; static int g_fd; static volatile unsigned long weird_hits, zero_slots, adder_slots, other_hits; static volatile unsigned long total_calls; static void classify(const unsigned char *buf, size_t bytes) { size_t slots = bytes / 6, i; for (i = 0; i < slots; i++) { const unsigned char *p = buf + i * 6; if (p[0]==0xde && p[1]==0xad && p[2]==0xbe) { __sync_fetch_and_add(&adder_slots, 1); } else if (p[0]==0xaa && p[1]==0xbb && p[2]==0xcc && p[3]==0xdd) { __sync_fetch_and_add(&adder_slots, 1); /* seed */ } else if ((p[0]|p[1]|p[2]|p[3]|p[4]|p[5]) == 0) { __sync_fetch_and_add(&zero_slots, 1); } else if (p[0]==0xde && p[1]==0xad && p[2]==0xc0 && p[3]==0xde) { __sync_fetch_and_add(&weird_hits, 1); /* WEIRD_ADDR = LEAK */ } else if (p[0]==0xc0 && p[1]==0xde && p[2]==0xad && p[3]==0xbe) { __sync_fetch_and_add(&weird_hits, 1); /* shifted WEIRD_ADDR */ } else { __sync_fetch_and_add(&other_hits, 1); } } } static void * worker(void *arg) { unsigned long iters = (unsigned long)arg; unsigned long n = 0; unsigned char *buf = malloc(BUF_SIZE); while (!stop_flag && buf) { struct df0732_list_req req; size_t ret_len = 0; req.buf = buf; req.len = BUF_SIZE; req.ret_len = &ret_len; memset(buf, 0x5a, BUF_SIZE); /* distinct pre-fill */ if (ioctl(g_fd, DF0732_IOCTL_LIST, &req) == 0) { if (ret_len > BUF_SIZE) ret_len = BUF_SIZE; classify(buf, ret_len); } __sync_fetch_and_add(&total_calls, 1); if (++n >= iters) { stop_flag = 1; break; } } free(buf); return NULL; } int main(int argc, char **argv) { unsigned long iters = (argc > 1) ? strtoul(argv[1], NULL, 0) : 100000UL; int nthreads = (argc > 2) ? atoi(argv[2]) : 4; pthread_t *tids; int i; g_fd = open("/dev/df0732", O_RDWR); if (g_fd < 0) { perror("open /dev/df0732"); return 1; } tids = calloc(nthreads, sizeof(*tids)); for (i = 0; i < nthreads; i++) pthread_create(&tids[i], NULL, worker, (void *)(iters/nthreads+1)); for (i = 0; i < nthreads; i++) pthread_join(tids[i], NULL); printf("leakcheck: %lu LIST ioctls\n", total_calls); printf(" adder/seed MAC slots : %lu\n", adder_slots); printf(" ZERO (M_ZERO) slots : %lu\n", zero_slots); printf(" WEIRD_ADDR residue : %lu %s\n", weird_hits, weird_hits > 0 ? "<== HEAP RESIDUE LEAK" : "(none โ leak eliminated)"); printf(" OTHER (suspicious) : %lu\n", other_hits); close(g_fd); return (weird_hits > 0) ? 1 : 0; } |