Issue 143129
Summary [Sema] Memory leak in `SemaOverload.cpp`
Labels new issue
Assignees
Reporter lux-QAQ
    

### **Description**

Hello, I've observed a memory leak in `SemaOverload.cpp` that occurs during template overload resolution.

My investigation **suggests a possible root cause** related to the lifecycle of `DeductionFailureInfo` objects. **It appears that** these objects, created to store details about why a template deduction failed, are allocated using placement `new (Context)` but are not explicitly deallocated when their owning `OverloadCandidate` is destroyed.

**If this hypothesis is correct, it could explain why** a definite leak occurs for members that perform their own heap allocations, such as the `SmallVector` within `clang::ConstraintSatisfaction`. The destructor for `ConstraintSatisfaction` **would presumably not be called**, which would in turn orphan its internal buffer, matching the behavior shown in the ASan report.

---
### **ASan Report**
[build2.log](https://github.com/user-attachments/files/20628340/build2.log)
[build1.log](https://github.com/user-attachments/files/20628341/build1.log)
This ASan report clearly shows the leak originating from `MakeDeductionFailureInfo` during the handling of unsatisfied constraints.



```cpp
==386608==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 7992 byte(s) in 37 object(s) allocated from:
    #0 0x55c8fab23dc3 in malloc (...)
    #1 0x7f81bb63dbcb in safe_malloc (...)
 #2 0x7f81bb63dbcb in llvm::SmallVectorBase<unsigned int>::grow_pod(...)
 #3 ...
    #7 0x7f81c86b806f in clang::ConstraintSatisfaction::operator=(...) /home/****/llvminstall/clang/include/clang/AST/ASTConcept.h:36:7
    #8 0x7f81c942c8fe in clang::MakeDeductionFailureInfo(clang::ASTContext&, ...) /home/****/llvminstall/clang/lib/Sema/SemaOverload.cpp:804:25
    #9 0x7f81c9952e56 in getPatternForClassTemplateSpecialization (...)
    #10 0x7f81c9952e56 in clang::Sema::InstantiateClassTemplateSpecialization(...)
 ...
```
---
### **Trigger Context and Build Failure**

The ASan reports are generated during the build process itself, specifically when the newly-built Clang attempts to compile tests for `libcxx`. The sequence of events is as follows:

1.  `ninja` starts executing the compilation command for a specific test file, for instance:
    ```sh
    [1980/1983] Building CXX object libcxx/test/tools/clang_tidy_checks/CMakeFiles/cxx-tidy.dir/hide_from_abi.cpp.o
 /home/****/code/llvmleak/llvm-project/build/bin/clang++ --target=x86_64-pc-linux-gnu ... -c .../hide_from_abi.cpp
    ```

2. Upon completion of the `clang++` process, LeakSanitizer (linked into `clang++`) runs its analysis and **prints the memory leak report** to the console first.

3.  After the ASan report is printed, `ninja` detects that the `clang++` subcommand has terminated with a non-zero (failure) exit code, and **then prints its `FAILED` message**:
    ```
    FAILED: libcxx/test/tools/clang_tidy_checks/CMakeFiles/cxx-tidy.dir/hide_from_abi.cpp.o
 ```

4.  These repeated failures cause the entire `ninja` build to halt with a final status:
    ```
    ninja: build stopped: subcommand failed.
 FAILED: runtimes/runtimes-stamps/runtimes-build /home/****/code/llvmleak/llvm-project/build/runtimes/runtimes-stamps/runtimes-build
 cd /home/****/code/llvmleak/llvm-project/build/runtimes/runtimes-bins && /usr/local/bin/cmake --build .
    ```

---
### **Code Analysis**
[clang/lib/Sema/SemaOverload.cpp](https://github.com/llvm/llvm-project/blob/main/clang/lib/Sema/SemaOverload.cpp)
The issue appears to be a memory management mismatch. Allocation happens via the `ASTContext` arena in `MakeDeductionFailureInfo`, as noted by the `FIXME`:

*File: `clang/lib/Sema/SemaOverload.cpp`*
```cpp
//...
  case TemplateDeductionResult::DeducedMismatch:
  case TemplateDeductionResult::DeducedMismatchNested: {
    // FIXME: Should allocate from normal heap so that we can free this later.
    auto *Saved = new (Context) DFIDeducedMismatchArgs; // <--- Allocation
 //...
```

But the corresponding `Destroy` method is a no-op, which also contains a `FIXME`:

*File: `clang/lib/Sema/SemaOverload.cpp`*
```cpp
void DeductionFailureInfo::Destroy() {
  switch (static_cast<TemplateDeductionResult>(Result)) {
  //...
  case TemplateDeductionResult::ConstraintsNotSatisfied:
    // FIXME: Destroy the data?
    Data = "" // <--- No deallocation
    break;
  //...
 }
}
```

---
### **Questions Regarding a Fix and CI**

I would like to contribute a PR to fix this and have two questions:

**1. Fixme:** The `FIXME` comments suggest using the normal heap. I could change `new (Context)` to a standard `new` and add a corresponding `delete` in `Destroy()`. However, I am concerned about performance implications and consistency with Clang's memory management model. What would be the architecturally correct approach?

**2. CI Coverage:** I am trying to determine if this is an issue specific to my local build environment, or if it indicates a potential gap in the CI's test coverage. The leak is consistently reproducible for me with the configuration below. Given this, I am wondering why the leak might not have been detected by the official CI pipelines.

---
### **Build Configuration**

Here is the full CMake command I used to configure the build that produced the ASan reports:

```cmake
cmake -G "Ninja" \
  -S /home/****/code/llvmleak/llvm-project/llvm \
  -B /home/****/code/llvmleak/llvm-project/build \
 -DCMAKE_INSTALL_PREFIX=$HOME/ollvm \
 -DCMAKE_INSTALL_RPATH=$HOME/ollvm/lib \
 -DLLVM_ENABLE_PROJECTS="bolt;clang;clang-tools-extra;cross-project-tests;libclc;lld;lldb;mlir;pstl" \
  -DLLVM_ENABLE_RUNTIMES="all" \
  -DLLVM_ENABLE_Z3_SOLVER=ON \
 -DLLVM_FORCE_BUILD_RUNTIME=ON \
  -DCMAKE_C_COMPILER=/usr/bin/clang \
 -DCMAKE_CXX_COMPILER=/usr/bin/clang++ \
 -DCMAKE_CXX_COMPILER_TARGET=x86_64-pc-linux-gnu \
 -DCMAKE_C_COMPILER_TARGET=x86_64-pc-linux-gnu \
 -DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-pc-linux-gnu \
 -DCMAKE_CXX_FLAGS="-O3 -march=native" \
  -DCMAKE_C_FLAGS="-O3 -march=native" \
  -DCMAKE_LINKER=/usr/bin/ld.lld \
 -DLLVM_LIBC_FULL_BUILD=ON \
  -DLLVM_ENABLE_LLD=ON \
 -DLIBCXX_USE_COMPILER_RT=ON \
  -DLIBCXXABI_USE_COMPILER_RT=ON \
 -DLIBCXXABI_ENABLE_STATIC_UNWINDER=ON \
  -DLLVM_PROFILE_GENERATE=OFF \
 -DLIBCXX_INSTALL_MODULES=ON \
  -DCMAKE_AR=/usr/bin/llvm-ar \
 -DCMAKE_RANLIB=/usr/bin/llvm-ranlib \
  -DLLVM_ENABLE_OPENMP=ON \
 -DLLVM_ENABLE_LIBUNWIND=ON \
  -DBOOTSTRAP_LLVM_ENABLE_LTO="Thin" \
 -DLLVM_ENABLE_LIBCXXABI=ON \
  -DLLVM_BUILD_DOCS=ON \
 -DLLVM_ENABLE_DOXYGEN=ON \
  -DCMAKE_BUILD_TYPE=Release \
 -DLLVM_BUILD_LLVM_DYLIB=ON \
  -DLLVM_LINK_LLVM_DYLIB=ON \
 -DLLVM_ENABLE_RTTI=ON \
  -DLLVM_ENABLE_EH=ON \
 -DLLVM_ENABLE_EXCEPTIONS=ON \
  -DCMAKE_BUILD_WITH_INSTALL_RPATH=TRUE \
 -DCMAKE_SHARED_LINKER_FLAGS="-Wl" \
  -DLLVM_USE_SANITIZER=Address \
 -DLLVM_TARGETS_TO_BUILD="X86"
```

---
### **System Environment**

* **OS:** `Ubuntu 24.04 LTS on Windows 10 x86_64`
* **LLVM Project Version:** `aaec9e5f5b1abb79bda62ca7cb25258acfb1acc3`
* **Host Compiler:** 
``` shell
Ubuntu clang version 18.1.3 (1ubuntu1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
```

### **Steps to Reproduce**

1.  **Initial Build Attempt:** Configure the build with the CMake command provided above and run `ninja`. The build will likely fail midway through with a `subcommand failed` error, because host tools cannot find newly-built shared libraries.

2.  **Set Library Path:** To resolve this initial linking issue, set the dynamic library path to point to the build's output directory.
    ```bash
    export LD_LIBRARY_PATH=/home/****/code/llvmleak/llvm-project/build/lib:$LD_LIBRARY_PATH
 ```

3.  **Resume Build and Trigger Leak:** Run `ninja` again in the same terminal. The build will now proceed to the `runtimes` stage. It is here that the `clang++` compilation commands will first cause ASan to print leak reports, and then cause the build to fail again.


_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to