DF-4835 / df4835_harness.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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | /* * DF-4835 harness โ mrsas_alloc_mfi_cmds()/mrsas_create_frame_pool() * error path leaves dangling frame pointers + destroyed tag; * mrsas_attach -> mrsas_free_mem() then DOUBLE-FREES every frame and the tag. * * Flow replicated verbatim from sys/dev/raid/mrsas/: * mrsas_ioctl.c:464-474 create_frame_pool: frames[0..K-1] alloc'd, fail at K * mrsas_ioctl.c:422-431 alloc_mfi_cmds error path: * for (i = 0; i < 32; i++) mrsas_free_frame(sc, list[i]); // frees * if (sc->mficmd_frame_tag) bus_dma_tag_destroy(...); // tag freed * return ENOMEM; // frame_mem/frame_phys_addr NOT cleared, * // mfi_cmd_list NOT cleared, tag NOT NULLed * mrsas_ioctl.c:532-538 mrsas_free_frame: * if (frame_phys_addr) bus_dmamap_unload(tag, map); * if (frame_mem) bus_dmamem_free(tag, frame_mem, map); // no clearing * mrsas.c:1719 init_fw -> return 1 * mrsas.c:800,842 attach: goto attach_fail_fw -> mrsas_free_mem() * mrsas.c:1011-1018 free_mem: * for (i = 0; i < 32; i++) mrsas_free_frame(sc, list[i]); // 2nd free! * if (mficmd_frame_tag) bus_dma_tag_destroy(...); // 2nd destroy * * Arms: * baseline โ verbatim sequence; the 2nd free pass must trip the allocator's * double-free detection (abort) => would-be kernel heap corruption * fixed โ error path clears frame_mem/frame_phys_addr and NULLs the tag * (as in fix.diff) => free_mem runs clean */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <sys/wait.h> #define MRSAS_MAX_MFI_CMDS 32 #define FAIL_AT 5 /* frame pool dies at cmd #5 */ struct mrsas_mfi_cmd { void *frame_mem; void *frame_dmamap; uint64_t frame_phys_addr; }; static struct mrsas_mfi_cmd cmds[MRSAS_MAX_MFI_CMDS]; static struct mrsas_mfi_cmd *list[MRSAS_MAX_MFI_CMDS]; static void *g_tag; /* sc->mficmd_frame_tag */ static int bus_dmamem_alloc_fail_at = -1; /* which alloc fails (inject OOM) */ /* --- mock allocator with double-free / use-after-free detection --- */ static void *live_ptr[256]; static int nlive; static void *freed_ptr[256]; static int nfreed; static int g_free_faults; static int is_freed(void *p) { for (int i = 0; i < nfreed; i++) if (freed_ptr[i] == p) return 1; return 0; } static void *kalloc(size_t n) { void *p = malloc(n); live_ptr[nlive++] = p; return p; } static void kfree_real(void *p) { if (is_freed(p)) { g_free_faults++; printf(" ** DOUBLE FREE of %p detected (kernel: heap corruption)\n", p); } else { freed_ptr[nfreed++] = p; } free(p); /* keep going to count every occurrence */ } static void kmcheck(void *p) { /* read of possibly-freed kernel memory */ if (p && is_freed(p)) { g_free_faults++; printf(" ** USE-AFTER-FREE touch of freed %p\n", p); } } static void *tag_create(void) { return kalloc(64); } static int tag_destroy(void *t) { kfree_real(t); return 0; } /* mrsas_ioctl.c:486-503 mrsas_alloc_frame */ static void *alloc_frame(struct mrsas_mfi_cmd *cmd, int idx) { if (idx == bus_dmamem_alloc_fail_at) return NULL; /* ENOMEM injected */ cmd->frame_mem = kalloc(1024); /* MRSAS_MFI_FRAME_SIZE */ cmd->frame_dmamap = (void *)0x1; cmd->frame_phys_addr = 0x1000 + idx * 0x1000; /* loaded */ return cmd->frame_mem; } static void mrsas_free_frame(struct mrsas_mfi_cmd *cmd); static void bus_dmamap_unload(struct mrsas_mfi_cmd *cmd) { /* real one touches the tag; on the 2nd pass the tag is freed memory */ if (g_tag) kmcheck(g_tag); cmd->frame_dmamap = (void *)0x2; /* map state cleared */ } /* mrsas_ioctl.c:532-538 โ verbatim semantics: NO field clearing */ static void mrsas_free_frame(struct mrsas_mfi_cmd *cmd) { if (cmd->frame_phys_addr) bus_dmamap_unload(cmd); /* unload(tag, map) */ if (cmd->frame_mem != NULL) { kmcheck(cmd->frame_mem); kfree_real(cmd->frame_mem); } } /* mrsas_ioctl.c:446-477 */ static int mrsas_create_frame_pool(int fixed) { g_tag = tag_create(); for (int i = 0; i < MRSAS_MAX_MFI_CMDS; i++) { if (alloc_frame(cmds + i, i) == NULL) { printf(" create_frame_pool: alloc failed at cmd %d " "(BUS_DMA_NOWAIT ENOMEM)\n", i); if (fixed) { /* fix.diff: unwind properly */ for (int j = 0; j < i; j++) { cmds[j].frame_phys_addr = 0; kfree_real(cmds[j].frame_mem); cmds[j].frame_mem = NULL; } tag_destroy(g_tag); g_tag = NULL; return -1; } return -1; /* original: just fail */ } } return 0; } /* mrsas_ioctl.c:382-434 */ static int mrsas_alloc_mfi_cmds(int fixed) { for (int i = 0; i < MRSAS_MAX_MFI_CMDS; i++) list[i] = cmds + i; memset(cmds, 0, sizeof(cmds)); if (mrsas_create_frame_pool(fixed)) { if (!fixed) { for (int i = 0; i < MRSAS_MAX_MFI_CMDS; i++) /* :424-427 */ mrsas_free_frame(list[i]); if (g_tag != NULL) { tag_destroy(g_tag); } /* :428-429, not NULLed */ } /* both: mfi_cmd_list leaked/alive, returns ENOMEM */ return -1; } return 0; } /* mrsas.c:927 mrsas_free_mem (MFI part :1011-1018) */ static int mrsas_free_mem(void) { int faults = 0; for (int i = 0; i < MRSAS_MAX_MFI_CMDS; i++) mrsas_free_frame(list[i]); /* 2nd pass */ if (g_tag != NULL) { if (g_free_faults) faults++; tag_destroy(g_tag); } return faults; } int main(int argc, char **argv) { setvbuf(stdout, NULL, _IONBF, 0); int fixed = (argc > 1 && strcmp(argv[1], "fixed") == 0); pid_t pid = fork(); if (pid == 0) { int prev = g_free_faults; (void)prev; bus_dmamem_alloc_fail_at = FAIL_AT; if (mrsas_alloc_mfi_cmds(fixed) != 0) { printf(" alloc_mfi_cmds -> ENOMEM -> init_fw fails -> " "attach_fail_fw -> mrsas_free_mem()\n"); } int faults = mrsas_free_mem(); printf(" mrsas_free_mem: %s (use-after-free/double-free detected: %s)\n", faults ? "HEAP CORRUPTION" : "clean", (faults || g_free_faults) ? "YES" : "no"); exit(faults ? 42 : 0); } int st; waitpid(pid, &st, 0); if (WIFSIGNALED(st)) printf("[%s] child KILLED by %s (allocator double-free abort โ " "kernel equivalent: heap corruption/UAF)\n", fixed ? "FIXED " : "BASELINE", strsignal(WTERMSIG(st))); else printf("[%s] child exit=%d\n", fixed ? "FIXED " : "BASELINE", WEXITSTATUS(st)); return 0; } |