Clang Project

clang_source_code/test/Analysis/mig.mm
1// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,osx.MIG\
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;
18
19kern_return_t vm_deallocate(mach_port_name_t, vm_address_t, vm_size_t);
20kern_return_t mach_vm_deallocate(mach_port_name_t, vm_address_t, vm_size_t);
21void mig_deallocate(vm_address_t, vm_size_t);
22kern_return_t mach_port_deallocate(ipc_space_t, mach_port_name_t);
23
24#define MIG_SERVER_ROUTINE __attribute__((mig_server_routine))
25
26// IOKit wrappers.
27
28class OSObject;
29typedef kern_return_t IOReturn;
30#define kIOReturnError 1
31
32enum {
33  kOSAsyncRef64Count = 8,
34};
35
36typedef io_user_reference_t OSAsyncReference64[kOSAsyncRef64Count];
37
38struct IOExternalMethodArguments {
39  io_user_reference_t *asyncReference;
40};
41
42struct IOExternalMethodDispatch {};
43
44class IOUserClient {
45public:
46  static IOReturn releaseAsyncReference64(OSAsyncReference64);
47
48  MIG_SERVER_ROUTINE
49  virtual IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments *arguments,
50                                  IOExternalMethodDispatch *dispatch = 0, OSObject *target = 0, void *reference = 0);
51};
52
53
54// Tests.
55
56MIG_SERVER_ROUTINE
57kern_return_t basic_test(mach_port_name_t port, vm_address_t address, vm_size_t size) {
58  vm_deallocate(port, address, size); // expected-note{{Value passed through parameter 'address' is deallocated}}
59  if (size > 10) { // expected-note{{Assuming 'size' is > 10}}
60                   // expected-note@-1{{Taking true branch}}
61    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}}
62  // 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}}
63  }
64  return KERN_SUCCESS;
65}
66
67MIG_SERVER_ROUTINE
68kern_return_t test_unknown_return_value(mach_port_name_t port, vm_address_t address, vm_size_t size) {
69  extern kern_return_t foo();
70
71  vm_deallocate(port, address, size);
72  // We don't know if it's a success or a failure.
73  return foo(); // no-warning
74}
75
76// Make sure we don't crash when they forgot to write the return statement.
77MIG_SERVER_ROUTINE
78kern_return_t no_crash(mach_port_name_t port, vm_address_t address, vm_size_t size) {
79  vm_deallocate(port, address, size);
80}
81
82// When releasing two parameters, add a note for both of them.
83// Also when returning a variable, explain why do we think that it contains
84// a non-success code.
85MIG_SERVER_ROUTINE
86kern_return_t release_twice(mach_port_name_t port, vm_address_t addr1, vm_address_t addr2, vm_size_t size) {
87  kern_return_t ret = KERN_ERROR; // expected-note{{'ret' initialized to 1}}
88  vm_deallocate(port, addr1, size); // expected-note{{Value passed through parameter 'addr1' is deallocated}}
89  vm_deallocate(port, addr2, size); // expected-note{{Value passed through parameter 'addr2' is deallocated}}
90  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}}
91                     // 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}}
92}
93
94// Make sure we find the bug when the object is destroyed within an
95// automatic destructor.
96MIG_SERVER_ROUTINE
97kern_return_t test_vm_deallocate_in_automatic_dtor(mach_port_name_t port, vm_address_t address, vm_size_t size) {
98  struct WillDeallocate {
99    mach_port_name_t port;
100    vm_address_t address;
101    vm_size_t size;
102    ~WillDeallocate() {
103      vm_deallocate(port, address, size); // expected-note{{Value passed through parameter 'address' is deallocated}}
104    }
105  } will_deallocate{port, address, size};
106
107 if (size > 10) {
108    // expected-note@-1{{Assuming 'size' is > 10}}
109    // expected-note@-2{{Taking true branch}}
110    return KERN_ERROR;
111    // expected-note@-1{{Calling '~WillDeallocate'}}
112    // expected-note@-2{{Returning from '~WillDeallocate'}}
113    // 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}}
114    // 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}}
115  }
116  return KERN_SUCCESS;
117}
118
119// Check that we work on Objective-C messages and blocks.
120@interface I
121- (kern_return_t)fooAtPort:(mach_port_name_t)port withAddress:(vm_address_t)address ofSize:(vm_size_t)size;
122@end
123
124@implementation I
125- (kern_return_t)fooAtPort:(mach_port_name_t)port
126               withAddress:(vm_address_t)address
127                    ofSize:(vm_size_t)size MIG_SERVER_ROUTINE {
128  vm_deallocate(port, address, size); // expected-note{{Value passed through parameter 'address' is deallocated}}
129  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}}
130                     // 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}}
131}
132@end
133
134void test_block() {
135  kern_return_t (^block)(mach_port_name_t, vm_address_t, vm_size_t) =
136      ^MIG_SERVER_ROUTINE (mach_port_name_t port,
137                           vm_address_t address, vm_size_t size) {
138        vm_deallocate(port, address, size); // expected-note{{Value passed through parameter 'address' is deallocated}}
139        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}}
140                           // 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}}
141      };
142}
143
144void test_block_with_weird_return_type() {
145  struct Empty {};
146
147  // The block is written within a function so that it was actually analyzed as
148  // a top-level function during analysis. If we were to write it as a global
149  // variable of block type instead, it would not have been analyzed, because
150  // ASTConsumer won't find the block's code body within the VarDecl.
151  // At the same time, we shouldn't call it from the function, because otherwise
152  // it will be analyzed as an inlined function rather than as a top-level
153  // function.
154  Empty (^block)(mach_port_name_t, vm_address_t, vm_size_t) =
155      ^MIG_SERVER_ROUTINE(mach_port_name_t port,
156                          vm_address_t address, vm_size_t size) {
157        vm_deallocate(port, address, size);
158        return Empty{}; // no-crash
159      };
160}
161
162// Test various APIs.
163MIG_SERVER_ROUTINE
164kern_return_t test_mach_vm_deallocate(mach_port_name_t port, vm_address_t address, vm_size_t size) {
165  mach_vm_deallocate(port, address, size); // expected-note{{Value passed through parameter 'address' is deallocated}}
166  return KERN_ERROR;                 // expected-warning{{MIG callback fails with error after deallocating argument value}}
167                                     // expected-note@-1{{MIG callback fails with error after deallocating argument value}}
168}
169
170MIG_SERVER_ROUTINE
171kern_return_t test_mach_port_deallocate(ipc_space_t space,
172                                        mach_port_name_t port) {
173  mach_port_deallocate(space, port); // expected-note{{Value passed through parameter 'port' is deallocated}}
174  return KERN_ERROR;                 // expected-warning{{MIG callback fails with error after deallocating argument value}}
175                                     // expected-note@-1{{MIG callback fails with error after deallocating argument value}}
176}
177
178MIG_SERVER_ROUTINE
179kern_return_t test_mig_deallocate(vm_address_t address, vm_size_t size) {
180  mig_deallocate(address, size); // expected-note{{Value passed through parameter 'address' is deallocated}}
181  return KERN_ERROR;             // expected-warning{{MIG callback fails with error after deallocating argument value}}
182                                 // expected-note@-1{{MIG callback fails with error after deallocating argument value}}
183}
184
185// Let's try the C++11 attribute spelling syntax as well.
186[[clang::mig_server_routine]]
187IOReturn test_releaseAsyncReference64(IOExternalMethodArguments *arguments) {
188  IOUserClient::releaseAsyncReference64(arguments->asyncReference); // expected-note{{Value passed through parameter 'arguments' is deallocated}}
189  return kIOReturnError;                                            // expected-warning{{MIG callback fails with error after deallocating argument value}}
190                                                                    // expected-note@-1{{MIG callback fails with error after deallocating argument value}}
191}
192
193MIG_SERVER_ROUTINE
194kern_return_t test_no_reply(ipc_space_t space, mach_port_name_t port) {
195  mach_port_deallocate(space, port);
196  return MIG_NO_REPLY; // no-warning
197}
198
199class MyClient: public IOUserClient {
200  // The MIG_SERVER_ROUTINE annotation is intentionally skipped.
201  // It should be picked up from the superclass.
202  IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments *arguments,
203                          IOExternalMethodDispatch *dispatch = 0, OSObject *target = 0, void *reference = 0) override {
204
205    releaseAsyncReference64(arguments->asyncReference); // expected-note{{Value passed through parameter 'arguments' is deallocated}}
206    return kIOReturnError;                              // expected-warning{{MIG callback fails with error after deallocating argument value}}
207                                                        // expected-note@-1{{MIG callback fails with error after deallocating argument value}}
208  }
209};
210