260 lines · plain
1// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,osx.MIG -std=c++14 \2// RUN: -analyzer-output=text -fblocks -verify %s3 4typedef unsigned uint32_t;5 6// XNU APIs.7 8typedef int kern_return_t;9#define KERN_SUCCESS 010#define KERN_ERROR 111#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 148 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_ROUTINE67 virtual IOReturn externalMethod(68 uint32_t selector, IOExternalMethodArguments *arguments,69 IOExternalMethodDispatch *dispatch = 0, OSObject *target = 0,70 void *reference = 0);71 72 MIG_SERVER_ROUTINE73 virtual IOReturn registerNotificationPort(mach_port_t, UInt32, UInt32);74};75 76// Tests.77 78MIG_SERVER_ROUTINE79kern_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_ROUTINE90kern_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-warning96}97 98// Make sure we don't crash when they forgot to write the return statement.99MIG_SERVER_ROUTINE100kern_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 contains106// a non-success code.107MIG_SERVER_ROUTINE108kern_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_ROUTINE117kern_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-note119 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 an125// automatic destructor.126MIG_SERVER_ROUTINE127kern_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 I151- (kern_return_t)fooAtPort:(mach_port_name_t)port withAddress:(vm_address_t)address ofSize:(vm_size_t)size;152@end153 154@implementation I155- (kern_return_t)fooAtPort:(mach_port_name_t)port156 withAddress:(vm_address_t)address157 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@end163 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 as178 // a top-level function during analysis. If we were to write it as a global179 // variable of block type instead, it would not have been analyzed, because180 // 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 otherwise182 // it will be analyzed as an inlined function rather than as a top-level183 // 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-crash189 };190}191 192// Test various APIs.193MIG_SERVER_ROUTINE194kern_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_ROUTINE201kern_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_ROUTINE209kern_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_ROUTINE216kern_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_ROUTINE231kern_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-warning234}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_ROUTINE255kern_return_t test_os_ref_retain(thread_t thread) {256 thread_reference_internal(thread);257 thread_deallocate(thread);258 return KERN_ERROR; // no-warning259}260