DF-4353 / df4353.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 | /* * DF-4353 model proof - dce110_resource.c (DCE11 / Carrizo-Stoney) * ---------------------------------------------------------------- * 1) dce110_create_resource_pool() (dce110_resource.c:1398-1414): * * pool = kzalloc(sizeof(struct dce110_resource_pool)); // :1403 * if (construct(...)) return &pool->base; * return NULL; // :1413 * * construct()'s failure arm runs destruct(pool) (:1394) which frees * only the SUB-objects - the ~KB pool allocation itself is never * kfree'd. Identical error-path shape to DF-4318 (dce80/81/83); the * caller (core/dc.c dc_construct) never received the pointer and can * only destruct(dc), so the pool is orphaned. Happens on every * failed pool build on Carrizo/Stoney (any sub-object create failure, * irq-service create failure, resource_construct failure, or VBIOS * without an external DP clock source -> :1253-1257). * * 2) Sibling asymmetry: construct() also does * * if (dc->config.fbc_support) * dc->fbc_compressor = dce110_compressor_create(ctx); // :1373 * * and dce110_compressor_destroy() (dce110_compressor.c:439) has ZERO * callers in the tree - the compressor is never freed either. * * This userspace model reproduces the allocation accounting of the * failure arm (DF-4318's methodology). */ #include <stdio.h> #include <stdlib.h> #include <string.h> static int live_allocs = 0; static void *xkzalloc(size_t n) { void *p = calloc(1, n); if (p) live_allocs++; return p; } static void xkfree(void **p) { if (*p) { free(*p); (*p) = NULL; live_allocs--; } } struct pool { /* stand-in for struct dce110_resource_pool */ void *dccg, *dmcu, *abm, *irqs, *fbc_compressor; /* sub-objects */ int funcs; /* pool->base */ }; /* destruct() model - dce110_resource.c:703-773: frees SUB-OBJECTS only, * never the pool itself (the pool is freed by * dce110_destroy_resource_pool:1077-1084, which is NOT on the failure arm) */ static void destruct(struct pool *pool) { xkfree(&pool->dccg); xkfree(&pool->dmcu); xkfree(&pool->abm); xkfree(&pool->irqs); } /* construct() model - failure at the irq-service create step * (dce110_resource.c:1304-1310) with fbc enabled (:1372-1373) */ static int construct(struct pool *pool, int fail_irq, int fbc_support) { pool->dccg = xkzalloc(64); if (!pool->dccg) goto fail; pool->dmcu = xkzalloc(64); if (!pool->dmcu) goto fail; if (fbc_support) { pool->fbc_compressor = xkzalloc(64); /* :1373 - stored on dc */ if (!pool->fbc_compressor) goto fail; } if (fail_irq) goto fail; /* :1308 */ pool->irqs = xkzalloc(64); if (!pool->irqs) goto fail; return 1; fail: destruct(pool); /* :1394 */ return 0; } /* baseline: dce110_create_resource_pool (dce110_resource.c:1398-1414) */ static struct pool *create_resource_pool_baseline(int fail_irq, int fbc_support) { struct pool *pool = xkzalloc(sizeof(*pool)); /* :1403 */ if (!pool) return NULL; if (construct(pool, fail_irq, fbc_support)) return pool; /* :1410 */ /* :1412-1413: BREAK_TO_DEBUGGER(); return NULL; -- no kfree(pool) */ return NULL; } /* fixed: kfree(pool) on the failure arm (+ compressor teardown) */ static struct pool *create_resource_pool_fixed(int fail_irq, int fbc_support) { struct pool *pool = xkzalloc(sizeof(*pool)); if (!pool) return NULL; if (construct(pool, fail_irq, fbc_support)) return pool; xkfree(&pool->fbc_compressor); /* fix part 2 */ xkfree((void **)&pool); /* fix part 1 */ return NULL; } int main(void) { struct pool *p; int baseline_orphans; printf("DF-4353 model: dce110_create_resource_pool failure-arm " "allocation accounting\n\n"); /* sanity: successful path */ live_allocs = 0; p = create_resource_pool_baseline(0, 1); printf("successful construct : live_allocs=%d (owner: dc)\n", live_allocs); /* baseline failure arm */ live_allocs = 0; p = create_resource_pool_baseline(1, 1); printf("\nbaseline failed construct : returned=%p, live_allocs=%d\n", p, live_allocs); printf(" (sub-objects freed by destruct; %d allocation(s) orphaned = " "the kzalloc'd pool + never-destroyed fbc_compressor)\n", live_allocs); if (live_allocs >= 2) printf("LEAK CONFIRMED (model)\n"); baseline_orphans = live_allocs; /* fixed failure arm */ live_allocs = 0; p = create_resource_pool_fixed(1, 1); printf("\nfixed failed construct : returned=%p, live_allocs=%d\n", p, live_allocs); if (live_allocs == 0) printf("FIX MODEL CLEAN (model)\n"); printf("\nnote: sizeof(struct dce110_resource_pool) embeds the full " "resource_pool base\n(core_types.h:120-198, incl. 12-ptr " "stream_enc[], 6-ptr engine arrays); on DCE11 every failed " "amdgpu bind leaks it.\n"); return baseline_orphans >= 2 ? 0 : 1; } |