| // RUN: %clang_analyze_cc1 -verify -analyzer-output=text %s \ | |
| // RUN: -analyzer-checker=core \ | |
| // RUN: -analyzer-checker=cplusplus \ | |
| // RUN: -analyzer-checker=unix | |
| #include "Inputs/system-header-simulator-for-malloc.h" | |
| //===----------------------------------------------------------------------===// | |
| // unique_ptr test cases | |
| //===----------------------------------------------------------------------===// | |
| namespace unique_ptr_tests { | |
| // Custom unique_ptr implementation for testing | |
| template <typename T> | |
| struct unique_ptr { | |
| T* ptr; | |
| unique_ptr(T* p) : ptr(p) {} | |
| ~unique_ptr() { | |
| // This destructor intentionally doesn't delete 'ptr' to validate that the | |
| // heuristic trusts that smart pointers (based on their class name) will | |
| // release the pointee even if it doesn't understand their destructor. | |
| } | |
| unique_ptr(unique_ptr&& other) : ptr(other.ptr) { other.ptr = nullptr; } | |
| T* get() const { return ptr; } | |
| }; | |
| template <typename T, typename... Args> | |
| unique_ptr<T> make_unique(Args&&... args) { | |
| return unique_ptr<T>(new T(args...)); | |
| } | |
| // Test 1: Check that we report leaks for malloc when passing smart pointers | |
| void add_unique_ptr(unique_ptr<int> ptr) { | |
| // The unique_ptr destructor will be called when ptr goes out of scope | |
| } | |
| void test_malloc_with_smart_ptr() { | |
| void *ptr = malloc(4); // expected-note {{Memory is allocated}} | |
| add_unique_ptr(make_unique<int>(1)); | |
| (void)ptr; | |
| // expected-warning@+1 {{Potential leak of memory pointed to by 'ptr'}} expected-note@+1 {{Potential leak of memory pointed to by 'ptr'}} | |
| } | |
| // Test 2: Check that we don't report leaks for unique_ptr in temporary objects | |
| struct Foo { | |
| unique_ptr<int> i; | |
| }; | |
| void add_foo(Foo foo) { | |
| // The unique_ptr destructor will be called when foo goes out of scope | |
| } | |
| void test_temporary_object() { | |
| // No warning should be emitted for this - the memory is managed by unique_ptr | |
| // in the temporary Foo object, which will properly clean up the memory | |
| add_foo({make_unique<int>(1)}); | |
| } | |
| // Test 3: Check that we don't report leaks for smart pointers in base class fields | |
| struct Base { | |
| unique_ptr<int> base_ptr; | |
| Base() : base_ptr(nullptr) {} | |
| Base(unique_ptr<int>&& ptr) : base_ptr(static_cast<unique_ptr<int>&&>(ptr)) {} | |
| }; | |
| struct Derived : public Base { | |
| int derived_field; | |
| Derived() : Base(), derived_field(0) {} | |
| Derived(unique_ptr<int>&& ptr, int field) : Base(static_cast<unique_ptr<int>&&>(ptr)), derived_field(field) {} | |
| }; | |
| void add_derived(Derived derived) { | |
| // The unique_ptr destructor will be called when derived goes out of scope | |
| // This should include the base_ptr field from the base class | |
| } | |
| void test_base_class_smart_ptr() { | |
| // No warning should be emitted for this - the memory is managed by unique_ptr | |
| // in the base class field of the temporary Derived object | |
| add_derived(Derived(make_unique<int>(1), 42)); | |
| } | |
| // Test 4: Check that we don't report leaks for multiple owning arguments | |
| struct SinglePtr { | |
| unique_ptr<int> ptr; | |
| SinglePtr(unique_ptr<int>&& p) : ptr(static_cast<unique_ptr<int>&&>(p)) {} | |
| }; | |
| struct MultiPtr { | |
| unique_ptr<int> ptr1; | |
| unique_ptr<int> ptr2; | |
| unique_ptr<int> ptr3; | |
| MultiPtr(unique_ptr<int>&& p1, unique_ptr<int>&& p2, unique_ptr<int>&& p3) | |
| : ptr1(static_cast<unique_ptr<int>&&>(p1)) | |
| , ptr2(static_cast<unique_ptr<int>&&>(p2)) | |
| , ptr3(static_cast<unique_ptr<int>&&>(p3)) {} | |
| }; | |
| void addMultiple(SinglePtr single, MultiPtr multi) { | |
| // All unique_ptr destructors will be called when the objects go out of scope | |
| // This tests handling of multiple by-value arguments with smart pointer fields | |
| } | |
| void test_multiple_owning_args() { | |
| // No warning should be emitted - all memory is properly managed by unique_ptr | |
| // in the temporary objects, which will properly clean up the memory | |
| addMultiple( | |
| SinglePtr(make_unique<int>(1)), | |
| MultiPtr(make_unique<int>(2), make_unique<int>(3), make_unique<int>(4)) | |
| ); | |
| } | |
| // Test 5: Check that we DO report leaks for raw pointers in mixed ownership scenarios | |
| struct MixedOwnership { | |
| unique_ptr<int> smart_ptr; // Should NOT leak (smart pointer managed) | |
| int *raw_ptr; // Should leak (raw pointer) | |
| MixedOwnership() : smart_ptr(make_unique<int>(1)), raw_ptr(new int(42)) {} // expected-note {{Memory is allocated}} | |
| }; | |
| void consume(MixedOwnership obj) { | |
| // The unique_ptr destructor will be called when obj goes out of scope | |
| // But raw_ptr will leak! | |
| } | |
| void test_mixed_ownership() { | |
| // This should report a leak for raw_ptr but not for smart_ptr | |
| consume(MixedOwnership()); // expected-note {{Calling default constructor for 'MixedOwnership'}} expected-note {{Returning from default constructor for 'MixedOwnership'}} | |
| } // expected-warning {{Potential memory leak}} expected-note {{Potential memory leak}} | |
| // Test 6: Check that we handle direct smart pointer constructor calls correctly | |
| void test_direct_constructor() { | |
| // Direct constructor call - should not leak | |
| int* raw_ptr = new int(42); | |
| unique_ptr<int> smart(raw_ptr); // This should escape the raw_ptr symbol | |
| // No leak should be reported here since smart pointer takes ownership | |
| } | |
| void test_mixed_direct_constructor() { | |
| int* raw1 = new int(1); | |
| int* raw2 = new int(2); // expected-note {{Memory is allocated}} | |
| unique_ptr<int> smart(raw1); // This should escape raw1 | |
| // raw2 should leak since it's not managed by any smart pointer | |
| int x = *raw2; // expected-warning {{Potential leak of memory pointed to by 'raw2'}} expected-note {{Potential leak of memory pointed to by 'raw2'}} | |
| } | |
| // Test 7: Multiple memory owning arguments - demonstrates addTransition API usage | |
| void addMultipleOwningArgs( | |
| unique_ptr<int> ptr1, | |
| unique_ptr<int> ptr2, | |
| unique_ptr<int> ptr3 | |
| ) { | |
| // All unique_ptr destructors will be called when arguments go out of scope | |
| // This tests handling of multiple smart pointer parameters in a single call | |
| } | |
| void test_multiple_memory_owning_arguments() { | |
| // No warning should be emitted - all memory is properly managed by unique_ptr | |
| // This test specifically exercises the addTransition API with multiple owning arguments | |
| addMultipleOwningArgs( | |
| make_unique<int>(1), | |
| make_unique<int>(2), | |
| make_unique<int>(3) | |
| ); | |
| } | |
| } // namespace unique_ptr_tests | |
| //===----------------------------------------------------------------------===// | |
| // Variadic constructor test cases | |
| //===----------------------------------------------------------------------===// | |
| namespace variadic_constructor_tests { | |
| // Variadic constructor - test for potential out-of-bounds access | |
| // This is the only test in this namespace and tests a scenario where Call.getNumArgs() > CD->getNumParams() | |
| // We use a synthetic unique_ptr here to activate the specific logic in the MallocChecker that will test out of bounds | |
| template <typename T> | |
| struct unique_ptr { | |
| T* ptr; | |
| // Constructor with ellipsis - can receive more arguments than parameters | |
| unique_ptr(T* p, ...) : ptr(p) {} | |
| ~unique_ptr() { | |
| // This destructor intentionally doesn't delete 'ptr' to validate that the | |
| // heuristic trusts that smart pointers (based on their class name) will | |
| // release the pointee even if it doesn't understand their destructor. | |
| } | |
| }; | |
| void process_variadic_smart_ptr(unique_ptr<int> ptr) { | |
| // Function body doesn't matter for this test | |
| } | |
| void test_variadic_constructor_bounds() { | |
| void *malloc_ptr = malloc(4); // expected-note {{Memory is allocated}} | |
| // This call creates a smart pointer with more arguments than formal parameters | |
| // The constructor has 1 formal parameter (T* p) plus ellipsis, but we pass multiple args | |
| // This should trigger the bounds checking issue in handleSmartPointerConstructorArguments | |
| int* raw_ptr = new int(42); | |
| process_variadic_smart_ptr(unique_ptr<int>(raw_ptr, 1, 2, 3, 4, 5)); | |
| (void)malloc_ptr; | |
| } // expected-warning {{Potential leak of memory pointed to by 'malloc_ptr'}} | |
| // expected-note@-1 {{Potential leak of memory pointed to by 'malloc_ptr'}} | |
| } // namespace variadic_constructor_tests | |
| //===----------------------------------------------------------------------===// | |
| // shared_ptr test cases | |
| //===----------------------------------------------------------------------===// | |
| namespace shared_ptr_tests { | |
| // Custom shared_ptr implementation for testing | |
| template <typename T> | |
| struct shared_ptr { | |
| T* ptr; | |
| shared_ptr(T* p) : ptr(p) {} | |
| ~shared_ptr() { | |
| // This destructor intentionally doesn't delete 'ptr' to validate that the | |
| // heuristic trusts that smart pointers (based on their class name) will | |
| // release the pointee even if it doesn't understand their destructor. | |
| } | |
| shared_ptr(shared_ptr&& other) : ptr(other.ptr) { other.ptr = nullptr; } | |
| T* get() const { return ptr; } | |
| }; | |
| template <typename T, typename... Args> | |
| shared_ptr<T> make_shared(Args&&... args) { | |
| return shared_ptr<T>(new T(args...)); | |
| } | |
| // Test 1: Check that we don't report leaks for shared_ptr in temporary objects | |
| struct Foo { | |
| shared_ptr<int> i; | |
| }; | |
| void add_foo(Foo foo) { | |
| // The shared_ptr destructor will be called when foo goes out of scope | |
| } | |
| void test_temporary_object() { | |
| // No warning should be emitted for this - the memory is managed by shared_ptr | |
| // in the temporary Foo object, which will properly clean up the memory | |
| add_foo({make_shared<int>(1)}); | |
| } | |
| } // namespace shared_ptr_tests |