DF-3269 / zharness.c
/* * DF-3269: userland reproduction of the deflateInit2_ allocation-failure * path against the *kernel source file itself* (sys/net/zlib.c compiled * without _KERNEL, which its own #ifdef ladder explicitly supports). * * deflateInit2_ allocates the deflate_state with the consumer zalloc and * only sets s->status in deflateReset (zlib.c:785-800 vs :1120 region). * If any of the follow-on allocations fails, the caller is expected to * run deflateEnd, which READS s->status (uninitialized!) and, unless the * garbage equals INIT/BUSY/FINISH_STATE, returns Z_STREAM_ERROR *before * any TRY_FREE* -> every allocation made so far leaks (zlib.c:1108-1127). * * The harness zalloc: (a) fills each allocation with 0xAA so the state is * provably uninitialized; (b) fails the Nth allocation on demand; (c) * counts live allocations, so the leak is measured, not argued. * * build: cc -O -o zharness zharness.c zlib_userland.c -I. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "zlib.h" static long allocs_live, allocs_total, alloc_bytes_live; static int fail_at = -1; /* 1-based allocation ordinal */ static void * count_zalloc(voidpf opaque, uInt items, uInt size) { void *p; (void)opaque; allocs_total++; if (allocs_total == fail_at) return (Z_NULL); p = malloc((size_t)items * size); if (p) { memset(p, 0xAA, (size_t)items * size); /* uninit state, visible */ allocs_live++; alloc_bytes_live += (size_t)items * size; } return (p); } static void count_zfree(voidpf opaque, voidpf ptr) { (void)opaque; if (ptr) { /* recover size from our ledger: we simply track count */ allocs_live--; free(ptr); } } int main(void) { z_stream z; int r; /* --- case 1: window allocation fails (2nd alloc) --- */ memset(&z, 0, sizeof(z)); z.zalloc = count_zalloc; z.zfree = count_zfree; z.opaque = Z_NULL; allocs_live = allocs_total = 0; fail_at = 2; printf("case 1: fail the window allocation (alloc #2)\n"); r = deflateInit2_(&z, 6, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY, ZLIB_VERSION, (int)sizeof(z)); printf(" deflateInit2_ -> %d\n", r); printf(" allocs made=%ld\n", allocs_total); /* kernel/ng_deflate error path: call deflateEnd on the failed init */ r = deflateEnd(&z); printf(" deflateEnd -> %d (%s)\n", r, r == Z_STREAM_ERROR ? "Z_STREAM_ERROR: status garbage" : r == Z_OK ? "Z_OK: cleanup ran" : "other"); printf(" live allocations after deflateEnd: %ld %s\n", allocs_live, allocs_live ? "<<<< LEAKED" : "(none leaked)"); /* --- case 2: no induced failure, normal round trip sanity --- */ memset(&z, 0, sizeof(z)); z.zalloc = count_zalloc; z.zfree = count_zfree; z.opaque = Z_NULL; allocs_live = allocs_total = 0; fail_at = -1; r = deflateInit2_(&z, 6, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY, ZLIB_VERSION, (int)sizeof(z)); printf("case 2: no failure: init -> %d, deflateEnd -> %d, live=%ld\n", r, deflateEnd(&z), allocs_live); return 0; } |