| 1 | // RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.InnerPointer \ |
| 2 | // RUN: %s -analyzer-output=text -verify |
| 3 | |
| 4 | #include "Inputs/system-header-simulator-cxx.h" |
| 5 | namespace std { |
| 6 | |
| 7 | template <typename T> |
| 8 | void func_ref(T &a); |
| 9 | |
| 10 | template <typename T> |
| 11 | void func_const_ref(const T &a); |
| 12 | |
| 13 | template <typename T> |
| 14 | void func_value(T a); |
| 15 | |
| 16 | string my_string = "default"; |
| 17 | void default_arg(int a = 42, string &b = my_string); |
| 18 | |
| 19 | } // end namespace std |
| 20 | |
| 21 | void consume(const char *) {} |
| 22 | void consume(const wchar_t *) {} |
| 23 | void consume(const char16_t *) {} |
| 24 | void consume(const char32_t *) {} |
| 25 | |
| 26 | //=--------------------------------------=// |
| 27 | // `std::string` member functions // |
| 28 | //=--------------------------------------=// |
| 29 | |
| 30 | void deref_after_scope_char(bool cond) { |
| 31 | const char *c, *d; |
| 32 | { |
| 33 | std::string s; |
| 34 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 35 | d = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 36 | } // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}} |
| 37 | // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}} |
| 38 | std::string s; |
| 39 | const char *c2 = s.c_str(); |
| 40 | if (cond) { |
| 41 | // expected-note@-1 {{Assuming 'cond' is not equal to 0}} |
| 42 | // expected-note@-2 {{Taking true branch}} |
| 43 | // expected-note@-3 {{Assuming 'cond' is 0}} |
| 44 | // expected-note@-4 {{Taking false branch}} |
| 45 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 46 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 47 | } else { |
| 48 | consume(d); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 49 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | void deref_after_scope_char_data_non_const() { |
| 54 | char *c; |
| 55 | { |
| 56 | std::string s; |
| 57 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 58 | } // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}} |
| 59 | std::string s; |
| 60 | char *c2 = s.data(); |
| 61 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 62 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 63 | } |
| 64 | |
| 65 | void deref_after_scope_wchar_t(bool cond) { |
| 66 | const wchar_t *c, *d; |
| 67 | { |
| 68 | std::wstring s; |
| 69 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::wstring' obtained here}} |
| 70 | d = s.data(); // expected-note {{Pointer to inner buffer of 'std::wstring' obtained here}} |
| 71 | } // expected-note {{Inner buffer of 'std::wstring' deallocated by call to destructor}} |
| 72 | // expected-note@-1 {{Inner buffer of 'std::wstring' deallocated by call to destructor}} |
| 73 | std::wstring s; |
| 74 | const wchar_t *c2 = s.c_str(); |
| 75 | if (cond) { |
| 76 | // expected-note@-1 {{Assuming 'cond' is not equal to 0}} |
| 77 | // expected-note@-2 {{Taking true branch}} |
| 78 | // expected-note@-3 {{Assuming 'cond' is 0}} |
| 79 | // expected-note@-4 {{Taking false branch}} |
| 80 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 81 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 82 | } else { |
| 83 | consume(d); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 84 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | void deref_after_scope_char16_t_cstr() { |
| 89 | const char16_t *c16; |
| 90 | { |
| 91 | std::u16string s16; |
| 92 | c16 = s16.c_str(); // expected-note {{Pointer to inner buffer of 'std::u16string' obtained here}} |
| 93 | } // expected-note {{Inner buffer of 'std::u16string' deallocated by call to destructor}} |
| 94 | std::u16string s16; |
| 95 | const char16_t *c16_2 = s16.c_str(); |
| 96 | consume(c16); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 97 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 98 | } |
| 99 | |
| 100 | void deref_after_scope_char32_t_data() { |
| 101 | const char32_t *c32; |
| 102 | { |
| 103 | std::u32string s32; |
| 104 | c32 = s32.data(); // expected-note {{Pointer to inner buffer of 'std::u32string' obtained here}} |
| 105 | } // expected-note {{Inner buffer of 'std::u32string' deallocated by call to destructor}} |
| 106 | std::u32string s32; |
| 107 | const char32_t *c32_2 = s32.data(); |
| 108 | consume(c32); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 109 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 110 | } |
| 111 | |
| 112 | void multiple_symbols(bool cond) { |
| 113 | const char *c1, *d1; |
| 114 | { |
| 115 | std::string s1; |
| 116 | c1 = s1.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 117 | d1 = s1.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 118 | const char *local = s1.c_str(); |
| 119 | consume(local); // no-warning |
| 120 | } // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}} |
| 121 | // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}} |
| 122 | std::string s2; |
| 123 | const char *c2 = s2.c_str(); |
| 124 | if (cond) { |
| 125 | // expected-note@-1 {{Assuming 'cond' is not equal to 0}} |
| 126 | // expected-note@-2 {{Taking true branch}} |
| 127 | // expected-note@-3 {{Assuming 'cond' is 0}} |
| 128 | // expected-note@-4 {{Taking false branch}} |
| 129 | consume(c1); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 130 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 131 | } else { |
| 132 | consume(d1); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 133 | } // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 134 | } |
| 135 | |
| 136 | void deref_after_scope_ok(bool cond) { |
| 137 | const char *c, *d; |
| 138 | std::string s; |
| 139 | { |
| 140 | c = s.c_str(); |
| 141 | d = s.data(); |
| 142 | } |
| 143 | if (cond) |
| 144 | consume(c); // no-warning |
| 145 | else |
| 146 | consume(d); // no-warning |
| 147 | } |
| 148 | |
| 149 | void deref_after_equals() { |
| 150 | const char *c; |
| 151 | std::string s = "hello"; |
| 152 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 153 | s = "world"; // expected-note {{Inner buffer of 'std::string' reallocated by call to 'operator='}} |
| 154 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 155 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 156 | } |
| 157 | |
| 158 | void deref_after_plus_equals() { |
| 159 | const char *c; |
| 160 | std::string s = "hello"; |
| 161 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 162 | s += " world"; // expected-note {{Inner buffer of 'std::string' reallocated by call to 'operator+='}} |
| 163 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 164 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 165 | } |
| 166 | |
| 167 | void deref_after_clear() { |
| 168 | const char *c; |
| 169 | std::string s; |
| 170 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 171 | s.clear(); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'clear'}} |
| 172 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 173 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 174 | } |
| 175 | |
| 176 | void deref_after_append() { |
| 177 | const char *c; |
| 178 | std::string s = "hello"; |
| 179 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 180 | s.append(2, 'x'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'append'}} |
| 181 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 182 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 183 | } |
| 184 | |
| 185 | void deref_after_assign() { |
| 186 | const char *c; |
| 187 | std::string s; |
| 188 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 189 | s.assign(4, 'a'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'assign'}} |
| 190 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 191 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 192 | } |
| 193 | |
| 194 | void deref_after_erase() { |
| 195 | const char *c; |
| 196 | std::string s = "hello"; |
| 197 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 198 | s.erase(0, 2); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'erase'}} |
| 199 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 200 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 201 | } |
| 202 | |
| 203 | void deref_after_insert() { |
| 204 | const char *c; |
| 205 | std::string s = "ello"; |
| 206 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 207 | s.insert(0, 1, 'h'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'insert'}} |
| 208 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 209 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 210 | } |
| 211 | |
| 212 | void deref_after_replace() { |
| 213 | const char *c; |
| 214 | std::string s = "hello world"; |
| 215 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 216 | s.replace(6, 5, "string"); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'replace'}} |
| 217 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 218 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 219 | } |
| 220 | |
| 221 | void deref_after_pop_back() { |
| 222 | const char *c; |
| 223 | std::string s; |
| 224 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 225 | s.pop_back(); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'pop_back'}} |
| 226 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 227 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 228 | } |
| 229 | |
| 230 | void deref_after_push_back() { |
| 231 | const char *c; |
| 232 | std::string s; |
| 233 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 234 | s.push_back('c'); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'push_back'}} |
| 235 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 236 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 237 | } |
| 238 | |
| 239 | void deref_after_reserve() { |
| 240 | const char *c; |
| 241 | std::string s; |
| 242 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 243 | s.reserve(5); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'reserve'}} |
| 244 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 245 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 246 | } |
| 247 | |
| 248 | void deref_after_resize() { |
| 249 | const char *c; |
| 250 | std::string s; |
| 251 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 252 | s.resize(5); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'resize'}} |
| 253 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 254 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 255 | } |
| 256 | |
| 257 | void deref_after_shrink_to_fit() { |
| 258 | const char *c; |
| 259 | std::string s; |
| 260 | c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 261 | s.shrink_to_fit(); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'shrink_to_fit'}} |
| 262 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 263 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 264 | } |
| 265 | |
| 266 | void deref_after_swap() { |
| 267 | const char *c; |
| 268 | std::string s1, s2; |
| 269 | c = s1.data(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 270 | s1.swap(s2); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'swap'}} |
| 271 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 272 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 273 | } |
| 274 | |
| 275 | struct S { |
| 276 | std::string s; |
| 277 | const char *name() { |
| 278 | return s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 279 | // expected-note@-1 {{Pointer to inner buffer of 'std::string' obtained here}} |
| 280 | } |
| 281 | void clear() { |
| 282 | s.clear(); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'clear'}} |
| 283 | } |
| 284 | ~S() {} // expected-note {{Inner buffer of 'std::string' deallocated by call to destructor}} |
| 285 | }; |
| 286 | |
| 287 | void cleared_through_method() { |
| 288 | S x; |
| 289 | const char *c = x.name(); // expected-note {{Calling 'S::name'}} |
| 290 | // expected-note@-1 {{Returning from 'S::name'}} |
| 291 | x.clear(); // expected-note {{Calling 'S::clear'}} |
| 292 | // expected-note@-1 {{Returning; inner buffer was reallocated}} |
| 293 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 294 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 295 | } |
| 296 | |
| 297 | void destroyed_through_method() { |
| 298 | S y; |
| 299 | const char *c = y.name(); // expected-note {{Calling 'S::name'}} |
| 300 | // expected-note@-1 {{Returning from 'S::name'}} |
| 301 | y.~S(); // expected-note {{Calling '~S'}} |
| 302 | // expected-note@-1 {{Returning; inner buffer was deallocated}} |
| 303 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 304 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 305 | } |
| 306 | |
| 307 | //=---------------------------=// |
| 308 | // Other STL functions // |
| 309 | //=---------------------------=// |
| 310 | |
| 311 | void STL_func_ref() { |
| 312 | const char *c; |
| 313 | std::string s; |
| 314 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 315 | std::func_ref(s); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'func_ref'}} |
| 316 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 317 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 318 | } |
| 319 | |
| 320 | void STL_func_const_ref() { |
| 321 | const char *c; |
| 322 | std::string s; |
| 323 | c = s.c_str(); |
| 324 | std::func_const_ref(s); |
| 325 | consume(c); // no-warning |
| 326 | } |
| 327 | |
| 328 | void STL_func_value() { |
| 329 | const char *c; |
| 330 | std::string s; |
| 331 | c = s.c_str(); |
| 332 | std::func_value(s); |
| 333 | consume(c); // no-warning |
| 334 | } |
| 335 | |
| 336 | void func_ptr_known() { |
| 337 | const char *c; |
| 338 | std::string s; |
| 339 | void (*func_ptr)(std::string &) = std::func_ref<std::string>; |
| 340 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 341 | func_ptr(s); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'func_ref'}} |
| 342 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 343 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 344 | } |
| 345 | |
| 346 | void func_ptr_unknown(void (*func_ptr)(std::string &)) { |
| 347 | const char *c; |
| 348 | std::string s; |
| 349 | c = s.c_str(); |
| 350 | func_ptr(s); |
| 351 | consume(c); // no-warning |
| 352 | } |
| 353 | |
| 354 | void func_default_arg() { |
| 355 | const char *c; |
| 356 | std::string s; |
| 357 | c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 358 | default_arg(3, s); // expected-note {{Inner buffer of 'std::string' reallocated by call to 'default_arg'}} |
| 359 | consume(c); // expected-warning {{Inner pointer of container used after re/deallocation}} |
| 360 | // expected-note@-1 {{Inner pointer of container used after re/deallocation}} |
| 361 | } |
| 362 | |
| 363 | struct T { |
| 364 | std::string to_string() { return s; } |
| 365 | private: |
| 366 | std::string s; |
| 367 | }; |
| 368 | |
| 369 | const char *escape_via_return_temp() { |
| 370 | T x; |
| 371 | return x.to_string().c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 372 | // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}} |
| 373 | // expected-warning@-2 {{Inner pointer of container used after re/deallocation}} |
| 374 | // expected-note@-3 {{Inner pointer of container used after re/deallocation}} |
| 375 | } |
| 376 | |
| 377 | const char *escape_via_return_local() { |
| 378 | std::string s; |
| 379 | return s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' obtained here}} |
| 380 | // expected-note@-1 {{Inner buffer of 'std::string' deallocated by call to destructor}} |
| 381 | // expected-warning@-2 {{Inner pointer of container used after re/deallocation}} |
| 382 | // expected-note@-3 {{Inner pointer of container used after re/deallocation}} |
| 383 | } |
| 384 | |
| 385 | |
| 386 | char *c(); |
| 387 | class A {}; |
| 388 | |
| 389 | void no_CXXRecordDecl() { |
| 390 | A a, *b; |
| 391 | *(void **)&b = c() + 1; |
| 392 | *b = a; // no-crash |
| 393 | } |
| 394 | |
| 395 | void checkReference(std::string &s) { |
| 396 | const char *c = s.c_str(); |
| 397 | } |
| 398 | |