xref: /llvm-project/clang/test/Analysis/dtor.cpp (revision b032e3ff6121a969b2e90ad7bf493c2d5d7ac3a2)
1 // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,debug.ExprInspection,cplusplus -analyzer-config c++-inlining=destructors -Wno-null-dereference -Wno-inaccessible-base -verify -analyzer-config eagerly-assume=false %s
2 
3 void clang_analyzer_eval(bool);
4 void clang_analyzer_checkInlined(bool);
5 
6 class A {
7 public:
~A()8   ~A() {
9     int *x = 0;
10     *x = 3; // expected-warning{{Dereference of null pointer}}
11   }
12 };
13 
main()14 int main() {
15   A a;
16 }
17 
18 
19 typedef __typeof(sizeof(int)) size_t;
20 void *malloc(size_t);
21 void free(void *);
22 
23 class SmartPointer {
24   void *X;
25 public:
SmartPointer(void * x)26   SmartPointer(void *x) : X(x) {}
~SmartPointer()27   ~SmartPointer() {
28     free(X);
29   }
30 };
31 
testSmartPointer()32 void testSmartPointer() {
33   char *mem = (char*)malloc(4);
34   {
35     SmartPointer Deleter(mem);
36     // destructor called here
37   }
38   *mem = 0; // expected-warning{{Use of memory after it is freed}}
39 }
40 
41 
42 void doSomething();
testSmartPointer2()43 void testSmartPointer2() {
44   char *mem = (char*)malloc(4);
45   {
46     SmartPointer Deleter(mem);
47     // Remove dead bindings...
48     doSomething();
49     // destructor called here
50   }
51   *mem = 0; // expected-warning{{Use of memory after it is freed}}
52 }
53 
54 
55 class Subclass : public SmartPointer {
56 public:
Subclass(void * x)57   Subclass(void *x) : SmartPointer(x) {}
58 };
59 
testSubclassSmartPointer()60 void testSubclassSmartPointer() {
61   char *mem = (char*)malloc(4);
62   {
63     Subclass Deleter(mem);
64     // Remove dead bindings...
65     doSomething();
66     // destructor called here
67   }
68   *mem = 0; // expected-warning{{Use of memory after it is freed}}
69 }
70 
71 
72 class MultipleInheritance : public Subclass, public SmartPointer {
73 public:
MultipleInheritance(void * a,void * b)74   MultipleInheritance(void *a, void *b) : Subclass(a), SmartPointer(b) {}
75 };
76 
testMultipleInheritance1()77 void testMultipleInheritance1() {
78   char *mem = (char*)malloc(4);
79   {
80     MultipleInheritance Deleter(mem, 0);
81     // Remove dead bindings...
82     doSomething();
83     // destructor called here
84   }
85   *mem = 0; // expected-warning{{Use of memory after it is freed}}
86 }
87 
testMultipleInheritance2()88 void testMultipleInheritance2() {
89   char *mem = (char*)malloc(4);
90   {
91     MultipleInheritance Deleter(0, mem);
92     // Remove dead bindings...
93     doSomething();
94     // destructor called here
95   }
96   *mem = 0; // expected-warning{{Use of memory after it is freed}}
97 }
98 
testMultipleInheritance3()99 void testMultipleInheritance3() {
100   char *mem = (char*)malloc(4);
101   {
102     MultipleInheritance Deleter(mem, mem);
103     // Remove dead bindings...
104     doSomething();
105     // destructor called here
106     // expected-warning@28 {{Attempt to free released memory}}
107   }
108 }
109 
110 
111 class SmartPointerMember {
112   SmartPointer P;
113 public:
SmartPointerMember(void * x)114   SmartPointerMember(void *x) : P(x) {}
115 };
116 
testSmartPointerMember()117 void testSmartPointerMember() {
118   char *mem = (char*)malloc(4);
119   {
120     SmartPointerMember Deleter(mem);
121     // Remove dead bindings...
122     doSomething();
123     // destructor called here
124   }
125   *mem = 0; // expected-warning{{Use of memory after it is freed}}
126 }
127 
128 
129 struct IntWrapper {
IntWrapperIntWrapper130   IntWrapper() : x(0) {}
131   ~IntWrapper();
132   int *x;
133 };
134 
testArrayInvalidation()135 void testArrayInvalidation() {
136   int i = 42;
137   int j = 42;
138 
139   {
140     IntWrapper arr[2];
141 
142     // There should be no undefined value warnings here.
143     clang_analyzer_eval(arr[0].x == 0); // expected-warning{{TRUE}}
144     clang_analyzer_eval(arr[1].x == 0); // expected-warning{{TRUE}}
145 
146     arr[0].x = &i;
147     arr[1].x = &j;
148     clang_analyzer_eval(*arr[0].x == 42); // expected-warning{{TRUE}}
149     clang_analyzer_eval(*arr[1].x == 42); // expected-warning{{TRUE}}
150   }
151 
152   // The destructors should have invalidated i and j.
153   clang_analyzer_eval(i == 42); // expected-warning{{UNKNOWN}}
154   clang_analyzer_eval(j == 42); // expected-warning{{UNKNOWN}}
155 }
156 
157 
158 
159 // Don't crash on a default argument inside an initializer.
160 struct DefaultArg {
DefaultArgDefaultArg161   DefaultArg(int x = 0) {}
162   ~DefaultArg();
163 };
164 
165 struct InheritsDefaultArg : DefaultArg {
InheritsDefaultArgInheritsDefaultArg166   InheritsDefaultArg() {}
167   virtual ~InheritsDefaultArg();
168 };
169 
testDefaultArg()170 void testDefaultArg() {
171   InheritsDefaultArg a;
172   // Force a bug to be emitted.
173   *(char *)0 = 1; // expected-warning{{Dereference of null pointer}}
174 }
175 
176 
177 namespace DestructorVirtualCalls {
178   class A {
179   public:
180     int *out1, *out2, *out3;
181 
get()182     virtual int get() { return 1; }
183 
~A()184     ~A() {
185       *out1 = get();
186     }
187   };
188 
189   class B : public A {
190   public:
get()191     virtual int get() { return 2; }
192 
~B()193     ~B() {
194       *out2 = get();
195     }
196   };
197 
198   class C : public B {
199   public:
get()200     virtual int get() { return 3; }
201 
~C()202     ~C() {
203       *out3 = get();
204     }
205   };
206 
test()207   void test() {
208     int a, b, c;
209 
210     // New scope for the C object.
211     {
212       C obj;
213       clang_analyzer_eval(obj.get() == 3); // expected-warning{{TRUE}}
214 
215       // Correctness check for devirtualization.
216       A *base = &obj;
217       clang_analyzer_eval(base->get() == 3); // expected-warning{{TRUE}}
218 
219       obj.out1 = &a;
220       obj.out2 = &b;
221       obj.out3 = &c;
222     }
223 
224     clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
225     clang_analyzer_eval(b == 2); // expected-warning{{TRUE}}
226     clang_analyzer_eval(c == 3); // expected-warning{{TRUE}}
227   }
228 }
229 
230 
231 namespace DestructorsShouldNotAffectReturnValues {
232   class Dtor {
233   public:
~Dtor()234     ~Dtor() {
235       clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
236     }
237   };
238 
allocate()239   void *allocate() {
240     Dtor d;
241     return malloc(4); // no-warning
242   }
243 
test()244   void test() {
245     // At one point we had an issue where the statements inside an
246     // inlined destructor kept us from finding the return statement,
247     // leading the analyzer to believe that the malloc'd memory had leaked.
248     void *p = allocate();
249     free(p); // no-warning
250   }
251 }
252 
253 namespace MultipleInheritanceVirtualDtors {
254   class VirtualDtor {
255   protected:
~VirtualDtor()256     virtual ~VirtualDtor() {
257       clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
258     }
259   };
260 
261   class NonVirtualDtor {
262   protected:
~NonVirtualDtor()263     ~NonVirtualDtor() {
264       clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
265     }
266   };
267 
268   class SubclassA : public VirtualDtor, public NonVirtualDtor {
269   public:
~SubclassA()270     virtual ~SubclassA() {}
271   };
272   class SubclassB : public NonVirtualDtor, public VirtualDtor {
273   public:
~SubclassB()274     virtual ~SubclassB() {}
275   };
276 
test()277   void test() {
278     SubclassA a;
279     SubclassB b;
280   }
281 }
282 
283 namespace ExplicitDestructorCall {
284   class VirtualDtor {
285   public:
~VirtualDtor()286     virtual ~VirtualDtor() {
287       clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
288     }
289   };
290 
291   class Subclass : public VirtualDtor {
292   public:
~Subclass()293     virtual ~Subclass() {
294       clang_analyzer_checkInlined(false); // no-warning
295     }
296   };
297 
destroy(Subclass * obj)298   void destroy(Subclass *obj) {
299     obj->VirtualDtor::~VirtualDtor();
300   }
301 }
302 
303 
304 namespace MultidimensionalArrays {
testArrayInvalidation()305   void testArrayInvalidation() {
306     int i = 42;
307     int j = 42;
308 
309     {
310       IntWrapper arr[2][2];
311 
312       // There should be no undefined value warnings here.
313       clang_analyzer_eval(arr[0][0].x == 0); // expected-warning{{TRUE}}
314       clang_analyzer_eval(arr[1][1].x == 0); // expected-warning{{TRUE}}
315 
316       arr[0][0].x = &i;
317       arr[1][1].x = &j;
318       clang_analyzer_eval(*arr[0][0].x == 42); // expected-warning{{TRUE}}
319       clang_analyzer_eval(*arr[1][1].x == 42); // expected-warning{{TRUE}}
320     }
321 
322     // The destructors should have invalidated i and j.
323     clang_analyzer_eval(i == 42); // expected-warning{{UNKNOWN}}
324     clang_analyzer_eval(j == 42); // expected-warning{{UNKNOWN}}
325   }
326 }
327 
328 namespace LifetimeExtension {
329   struct IntWrapper {
330 	int x;
IntWrapperLifetimeExtension::IntWrapper331 	IntWrapper(int y) : x(y) {}
IntWrapperLifetimeExtension::IntWrapper332 	IntWrapper() {
333       extern void use(int);
334       use(x); // no-warning
335 	}
336   };
337 
338   struct DerivedWrapper : public IntWrapper {
DerivedWrapperLifetimeExtension::DerivedWrapper339 	DerivedWrapper(int y) : IntWrapper(y) {}
340   };
341 
get()342   DerivedWrapper get() {
343 	return DerivedWrapper(1);
344   }
345 
test()346   void test() {
347 	const DerivedWrapper &d = get(); // lifetime extended here
348   }
349 
350 
351   class SaveOnDestruct {
352   public:
353     static int lastOutput;
354     int value;
355 
356     SaveOnDestruct();
~SaveOnDestruct()357     ~SaveOnDestruct() {
358       lastOutput = value;
359     }
360   };
361 
testSimple()362   void testSimple() {
363     {
364       const SaveOnDestruct &obj = SaveOnDestruct();
365       if (obj.value != 42)
366         return;
367       // destructor called here
368     }
369 
370     clang_analyzer_eval(SaveOnDestruct::lastOutput == 42); // expected-warning{{TRUE}}
371   }
372 
373   struct NRCheck {
374     bool bool_;
NRCheckLifetimeExtension::NRCheck375     NRCheck():bool_(true) {}
376     ~NRCheck() __attribute__((noreturn));
operator boolLifetimeExtension::NRCheck377     operator bool() const { return bool_; }
378   };
379 
380   struct CheckAutoDestructor {
381     bool bool_;
CheckAutoDestructorLifetimeExtension::CheckAutoDestructor382     CheckAutoDestructor():bool_(true) {}
operator boolLifetimeExtension::CheckAutoDestructor383     operator bool() const { return bool_; }
384   };
385 
386   struct CheckCustomDestructor {
387     bool bool_;
CheckCustomDestructorLifetimeExtension::CheckCustomDestructor388     CheckCustomDestructor():bool_(true) {}
389     ~CheckCustomDestructor();
operator boolLifetimeExtension::CheckCustomDestructor390     operator bool() const { return bool_; }
391   };
392 
testUnnamedNR()393   bool testUnnamedNR() {
394     if (NRCheck())
395       return true;
396     return false;
397   }
398 
testNamedNR()399   bool testNamedNR() {
400     if (NRCheck c = NRCheck())
401       return true;
402     return false;
403   }
404 
testUnnamedAutoDestructor()405   bool testUnnamedAutoDestructor() {
406     if (CheckAutoDestructor())
407       return true;
408     return false;
409   }
410 
testNamedAutoDestructor()411   bool testNamedAutoDestructor() {
412     if (CheckAutoDestructor c = CheckAutoDestructor())
413       return true;
414     return false;
415   }
416 
testUnnamedCustomDestructor()417   bool testUnnamedCustomDestructor() {
418     if (CheckCustomDestructor())
419       return true;
420     return false;
421   }
422 
423   // This case used to cause an unexpected "Undefined or garbage value returned
424   // to caller" warning
testNamedCustomDestructor()425   bool testNamedCustomDestructor() {
426     if (CheckCustomDestructor c = CheckCustomDestructor())
427       return true;
428     return false;
429   }
430 
testMultipleTemporariesCustomDestructor()431   bool testMultipleTemporariesCustomDestructor() {
432     if (CheckCustomDestructor c = (CheckCustomDestructor(), CheckCustomDestructor()))
433       return true;
434     return false;
435   }
436 
437   class VirtualDtorBase {
438   public:
439     int value;
~VirtualDtorBase()440     virtual ~VirtualDtorBase() {}
441   };
442 
443   class SaveOnVirtualDestruct : public VirtualDtorBase {
444   public:
445     static int lastOutput;
446 
447     SaveOnVirtualDestruct();
~SaveOnVirtualDestruct()448     virtual ~SaveOnVirtualDestruct() {
449       lastOutput = value;
450     }
451   };
452 
testVirtual()453   void testVirtual() {
454     {
455       const VirtualDtorBase &obj = SaveOnVirtualDestruct();
456       if (obj.value != 42)
457         return;
458       // destructor called here
459     }
460 
461     clang_analyzer_eval(SaveOnVirtualDestruct::lastOutput == 42); // expected-warning{{TRUE}}
462   }
463 }
464 
465 namespace NoReturn {
466   struct NR {
467     ~NR() __attribute__((noreturn));
468   };
469 
f(int ** x)470   void f(int **x) {
471     NR nr;
472   }
473 
g()474   void g() {
475     int *x;
476     f(&x);
477     *x = 47; // no warning
478   }
479 
g2(int * x)480   void g2(int *x) {
481     if (! x) NR();
482     *x = 47; // no warning
483   }
484 }
485 
486 namespace PseudoDtor {
487   template <typename T>
destroy(T & obj)488   void destroy(T &obj) {
489     clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
490     obj.~T();
491   }
492 
test()493   void test() {
494     int i;
495     destroy(i);
496     clang_analyzer_eval(true); // expected-warning{{TRUE}}
497   }
498 }
499 
500 namespace Incomplete {
501   class Foo; // expected-note{{forward declaration}}
f(Foo * foo)502   void f(Foo *foo) { delete foo; } // expected-warning{{deleting pointer to incomplete type}}
503 }
504 
505 namespace TypeTraitExpr {
506 template <bool IsSimple, typename T>
507 struct copier {
508   static void do_copy(T *dest, const T *src, unsigned count);
509 };
510 template <typename T, typename U>
do_copy(T * dest,const U * src,unsigned count)511 void do_copy(T *dest, const U *src, unsigned count) {
512   const bool IsSimple = __is_trivial(T) && __is_same(T, U);
513   copier<IsSimple, T>::do_copy(dest, src, count);
514 }
515 struct NonTrivial {
516   int *p;
NonTrivialTypeTraitExpr::NonTrivial517   NonTrivial() : p(new int[1]) { p[0] = 0; }
NonTrivialTypeTraitExpr::NonTrivial518   NonTrivial(const NonTrivial &other) {
519     p = new int[1];
520     do_copy(p, other.p, 1);
521   }
operator =TypeTraitExpr::NonTrivial522   NonTrivial &operator=(const NonTrivial &other) {
523     p = other.p;
524     return *this;
525   }
~NonTrivialTypeTraitExpr::NonTrivial526   ~NonTrivial() {
527     delete[] p; // expected-warning {{free released memory}}
528   }
529 };
530 
f()531 void f() {
532   NonTrivial nt1;
533   NonTrivial nt2(nt1);
534   nt1 = nt2;
535   clang_analyzer_eval(__is_trivial(NonTrivial)); // expected-warning{{FALSE}}
536   clang_analyzer_eval(__alignof(NonTrivial) > 0); // expected-warning{{TRUE}}
537 }
538 }
539 
540 namespace dtor_over_loc_concrete_int {
541 struct A {
~Adtor_over_loc_concrete_int::A542   ~A() {}
543 };
544 
545 struct B {
546   A a;
~Bdtor_over_loc_concrete_int::B547   ~B() {}
548 };
549 
550 struct C : A {
~Cdtor_over_loc_concrete_int::C551   ~C() {}
552 };
553 
testB()554 void testB() {
555   B *b = (B *)-1;
556   b->~B(); // no-crash
557 }
558 
testC()559 void testC() {
560   C *c = (C *)-1;
561   c->~C(); // no-crash
562 }
563 
testAutoDtor()564 void testAutoDtor() {
565   const A &a = *(A *)-1;
566   // no-crash
567 }
568 } // namespace dtor_over_loc_concrete_int
569 
570 // Test overriden new/delete operators
571 struct CustomOperators {
operator newCustomOperators572   void *operator new(size_t count) {
573     return malloc(count);
574   }
575 
operator deleteCustomOperators576   void operator delete(void *addr) {
577     free(addr);
578   }
579 
580 private:
581   int i;
582 };
583 
compliant()584 void compliant() {
585   auto *a = new CustomOperators();
586   delete a;
587 }
588 
overrideLeak()589 void overrideLeak() {
590   auto *a = new CustomOperators();
591 } // expected-warning{{Potential leak of memory pointed to by 'a'}}
592 
overrideDoubleDelete()593 void overrideDoubleDelete() {
594   auto *a = new CustomOperators();
595   delete a;
596   delete a; // expected-warning@577 {{Attempt to free released memory}}
597 }
598