1// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,osx.MIG -std=c++14 \ 2// RUN: -analyzer-output=text -fblocks -verify %s 3 4typedef unsigned uint32_t; 5 6// XNU APIs. 7 8typedef int kern_return_t; 9#define KERN_SUCCESS 0 10#define KERN_ERROR 1 11#define MIG_NO_REPLY (-305) 12 13typedef unsigned mach_port_name_t; 14typedef unsigned vm_address_t; 15typedef unsigned vm_size_t; 16typedef void *ipc_space_t; 17typedef unsigned long io_user_reference_t; 18typedef struct ipc_port *ipc_port_t; 19typedef unsigned mach_port_t; 20typedef uint32_t UInt32; 21 22struct os_refcnt {}; 23typedef struct os_refcnt os_refcnt_t; 24 25struct thread { 26 os_refcnt_t ref_count; 27}; 28typedef struct thread *thread_t; 29 30kern_return_t vm_deallocate(mach_port_name_t, vm_address_t, vm_size_t); 31kern_return_t mach_vm_deallocate(mach_port_name_t, vm_address_t, vm_size_t); 32void mig_deallocate(vm_address_t, vm_size_t); 33kern_return_t mach_port_deallocate(ipc_space_t, mach_port_name_t); 34void ipc_port_release(ipc_port_t); 35void thread_deallocate(thread_t); 36 37static void os_ref_retain(struct os_refcnt *rc); 38 39#define thread_reference_internal(thread) os_ref_retain(&(thread)->ref_count); 40 41#define MIG_SERVER_ROUTINE __attribute__((mig_server_routine)) 42 43// IOKit wrappers. 44 45class OSObject; 46typedef kern_return_t IOReturn; 47#define kIOReturnError 1 48 49enum { 50 kOSAsyncRef64Count = 8, 51}; 52 53typedef io_user_reference_t OSAsyncReference64[kOSAsyncRef64Count]; 54 55struct IOExternalMethodArguments { 56 io_user_reference_t *asyncReference; 57}; 58 59struct IOExternalMethodDispatch {}; 60 61class IOUserClient { 62public: 63 static IOReturn releaseAsyncReference64(OSAsyncReference64); 64 static IOReturn releaseNotificationPort(mach_port_t port); 65 66 MIG_SERVER_ROUTINE 67 virtual IOReturn externalMethod( 68 uint32_t selector, IOExternalMethodArguments *arguments, 69 IOExternalMethodDispatch *dispatch = 0, OSObject *target = 0, 70 void *reference = 0); 71 72 MIG_SERVER_ROUTINE 73 virtual IOReturn registerNotificationPort(mach_port_t, UInt32, UInt32); 74}; 75 76// Tests. 77 78MIG_SERVER_ROUTINE 79kern_return_t basic_test(mach_port_name_t port, vm_address_t address, vm_size_t size) { 80 vm_deallocate(port, address, size); // expected-note{{Value passed through parameter 'address' is deallocated}} 81 if (size > 10) { // expected-note{{Assuming 'size' is > 10}} 82 // expected-note@-1{{Taking true branch}} 83 return KERN_ERROR; // expected-warning{{MIG callback fails with error after deallocating argument value. This is a use-after-free vulnerability because the caller will try to deallocate it again}} 84 // expected-note@-1{{MIG callback fails with error after deallocating argument value. This is a use-after-free vulnerability because the caller will try to deallocate it again}} 85 } 86 return KERN_SUCCESS; 87} 88 89MIG_SERVER_ROUTINE 90kern_return_t test_unknown_return_value(mach_port_name_t port, vm_address_t address, vm_size_t size) { 91 extern kern_return_t foo(); 92 93 vm_deallocate(port, address, size); 94 // We don't know if it's a success or a failure. 95 return foo(); // no-warning 96} 97 98// Make sure we don't crash when they forgot to write the return statement. 99MIG_SERVER_ROUTINE 100kern_return_t no_crash(mach_port_name_t port, vm_address_t address, vm_size_t size) { 101 vm_deallocate(port, address, size); 102} 103 104// When releasing two parameters, add a note for both of them. 105// Also when returning a variable, explain why do we think that it contains 106// a non-success code. 107MIG_SERVER_ROUTINE 108kern_return_t release_twice(mach_port_name_t port, vm_address_t addr1, vm_address_t addr2, vm_size_t size) { 109 kern_return_t ret = KERN_ERROR; // expected-note{{'ret' initialized to 1}} 110 vm_deallocate(port, addr1, size); // expected-note{{Value passed through parameter 'addr1' is deallocated}} 111 vm_deallocate(port, addr2, size); // expected-note{{Value passed through parameter 'addr2' is deallocated}} 112 return ret; // expected-warning{{MIG callback fails with error after deallocating argument value. This is a use-after-free vulnerability because the caller will try to deallocate it again}} 113 // expected-note@-1{{MIG callback fails with error after deallocating argument value. This is a use-after-free vulnerability because the caller will try to deallocate it again}} 114} 115 116MIG_SERVER_ROUTINE 117kern_return_t no_unrelated_notes(mach_port_name_t port, vm_address_t address, vm_size_t size) { 118 vm_deallocate(port, address, size); // no-note 119 1 / 0; // expected-warning{{Division by zero}} 120 // expected-note@-1{{Division by zero}} 121 return KERN_SUCCESS; 122} 123 124// Make sure we find the bug when the object is destroyed within an 125// automatic destructor. 126MIG_SERVER_ROUTINE 127kern_return_t test_vm_deallocate_in_automatic_dtor(mach_port_name_t port, vm_address_t address, vm_size_t size) { 128 struct WillDeallocate { 129 mach_port_name_t port; 130 vm_address_t address; 131 vm_size_t size; 132 ~WillDeallocate() { 133 vm_deallocate(port, address, size); // expected-note{{Value passed through parameter 'address' is deallocated}} 134 } 135 } will_deallocate{port, address, size}; 136 137 if (size > 10) { 138 // expected-note@-1{{Assuming 'size' is > 10}} 139 // expected-note@-2{{Taking true branch}} 140 return KERN_ERROR; 141 // expected-note@-1{{Calling '~WillDeallocate'}} 142 // expected-note@-2{{Returning from '~WillDeallocate'}} 143 // expected-warning@-3{{MIG callback fails with error after deallocating argument value. This is a use-after-free vulnerability because the caller will try to deallocate it again}} 144 // expected-note@-4 {{MIG callback fails with error after deallocating argument value. This is a use-after-free vulnerability because the caller will try to deallocate it again}} 145 } 146 return KERN_SUCCESS; 147} 148 149// Check that we work on Objective-C messages and blocks. 150@interface I 151- (kern_return_t)fooAtPort:(mach_port_name_t)port withAddress:(vm_address_t)address ofSize:(vm_size_t)size; 152@end 153 154@implementation I 155- (kern_return_t)fooAtPort:(mach_port_name_t)port 156 withAddress:(vm_address_t)address 157 ofSize:(vm_size_t)size MIG_SERVER_ROUTINE { 158 vm_deallocate(port, address, size); // expected-note{{Value passed through parameter 'address' is deallocated}} 159 return KERN_ERROR; // expected-warning{{MIG callback fails with error after deallocating argument value. This is a use-after-free vulnerability because the caller will try to deallocate it again}} 160 // expected-note@-1{{MIG callback fails with error after deallocating argument value. This is a use-after-free vulnerability because the caller will try to deallocate it again}} 161} 162@end 163 164void test_block() { 165 kern_return_t (^block)(mach_port_name_t, vm_address_t, vm_size_t) = 166 ^MIG_SERVER_ROUTINE (mach_port_name_t port, 167 vm_address_t address, vm_size_t size) { 168 vm_deallocate(port, address, size); // expected-note{{Value passed through parameter 'address' is deallocated}} 169 return KERN_ERROR; // expected-warning{{MIG callback fails with error after deallocating argument value. This is a use-after-free vulnerability because the caller will try to deallocate it again}} 170 // expected-note@-1{{MIG callback fails with error after deallocating argument value. This is a use-after-free vulnerability because the caller will try to deallocate it again}} 171 }; 172} 173 174void test_block_with_weird_return_type() { 175 struct Empty {}; 176 177 // The block is written within a function so that it was actually analyzed as 178 // a top-level function during analysis. If we were to write it as a global 179 // variable of block type instead, it would not have been analyzed, because 180 // ASTConsumer won't find the block's code body within the VarDecl. 181 // At the same time, we shouldn't call it from the function, because otherwise 182 // it will be analyzed as an inlined function rather than as a top-level 183 // function. 184 Empty (^block)(mach_port_name_t, vm_address_t, vm_size_t) = 185 ^MIG_SERVER_ROUTINE(mach_port_name_t port, 186 vm_address_t address, vm_size_t size) { 187 vm_deallocate(port, address, size); 188 return Empty{}; // no-crash 189 }; 190} 191 192// Test various APIs. 193MIG_SERVER_ROUTINE 194kern_return_t test_mach_vm_deallocate(mach_port_name_t port, vm_address_t address, vm_size_t size) { 195 mach_vm_deallocate(port, address, size); // expected-note{{Value passed through parameter 'address' is deallocated}} 196 return KERN_ERROR; // expected-warning{{MIG callback fails with error after deallocating argument value}} 197 // expected-note@-1{{MIG callback fails with error after deallocating argument value}} 198} 199 200MIG_SERVER_ROUTINE 201kern_return_t test_mach_port_deallocate(ipc_space_t space, 202 mach_port_name_t port) { 203 mach_port_deallocate(space, port); // expected-note{{Value passed through parameter 'port' is deallocated}} 204 return KERN_ERROR; // expected-warning{{MIG callback fails with error after deallocating argument value}} 205 // expected-note@-1{{MIG callback fails with error after deallocating argument value}} 206} 207 208MIG_SERVER_ROUTINE 209kern_return_t test_mig_deallocate(vm_address_t address, vm_size_t size) { 210 mig_deallocate(address, size); // expected-note{{Value passed through parameter 'address' is deallocated}} 211 return KERN_ERROR; // expected-warning{{MIG callback fails with error after deallocating argument value}} 212 // expected-note@-1{{MIG callback fails with error after deallocating argument value}} 213} 214 215MIG_SERVER_ROUTINE 216kern_return_t test_ipc_port_release(ipc_port_t port) { 217 ipc_port_release(port); // expected-note{{Value passed through parameter 'port' is deallocated}} 218 return KERN_ERROR; // expected-warning{{MIG callback fails with error after deallocating argument value}} 219 // expected-note@-1{{MIG callback fails with error after deallocating argument value}} 220} 221 222// Let's try the C++11 attribute spelling syntax as well. 223[[clang::mig_server_routine]] 224IOReturn test_releaseAsyncReference64(IOExternalMethodArguments *arguments) { 225 IOUserClient::releaseAsyncReference64(arguments->asyncReference); // expected-note{{Value passed through parameter 'arguments' is deallocated}} 226 return kIOReturnError; // expected-warning{{MIG callback fails with error after deallocating argument value}} 227 // expected-note@-1{{MIG callback fails with error after deallocating argument value}} 228} 229 230MIG_SERVER_ROUTINE 231kern_return_t test_no_reply(ipc_space_t space, mach_port_name_t port) { 232 mach_port_deallocate(space, port); 233 return MIG_NO_REPLY; // no-warning 234} 235 236class MyClient: public IOUserClient { 237 // The MIG_SERVER_ROUTINE annotation is intentionally skipped. 238 // It should be picked up from the superclass. 239 IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments *arguments, 240 IOExternalMethodDispatch *dispatch = 0, OSObject *target = 0, void *reference = 0) override { 241 242 releaseAsyncReference64(arguments->asyncReference); // expected-note{{Value passed through parameter 'arguments' is deallocated}} 243 return kIOReturnError; // expected-warning{{MIG callback fails with error after deallocating argument value}} 244 // expected-note@-1{{MIG callback fails with error after deallocating argument value}} 245 } 246 247 IOReturn registerNotificationPort(mach_port_t port, UInt32 x, UInt32 y) { 248 releaseNotificationPort(port); // expected-note{{Value passed through parameter 'port' is deallocated}} 249 return kIOReturnError; // expected-warning{{MIG callback fails with error after deallocating argument value}} 250 // expected-note@-1{{MIG callback fails with error after deallocating argument value}} 251 } 252}; 253 254MIG_SERVER_ROUTINE 255kern_return_t test_os_ref_retain(thread_t thread) { 256 thread_reference_internal(thread); 257 thread_deallocate(thread); 258 return KERN_ERROR; // no-warning 259} 260