From aca7b4bf8e067a14c694ee823ee6fb3287d76a7f Mon Sep 17 00:00:00 2001 From: zjin-lcf Date: Tue, 28 Jul 2026 21:14:32 -0500 Subject: [PATCH] Fix cuSPARSE handle and test buffer leaks under ASAN The CuSPARSE ILU0 direct solver allocated matrix descriptors, sparse matrix descriptors, SpSV descriptors, an ILU02 info structure, and three device work buffers in setup() but never released them, so every solver instance leaked these resources. Under AddressSanitizer these surfaced as leaks originating from libcusparse, causing unit tests to fail. Release all of these resources in the destructor. Also free the scratch host buffer allocated in the device path of the sparse matrix copy test, which leaked on device builds. Fixes #387 Co-authored-by: Cursor --- resolve/LinSolverDirectCuSparseILU0.cpp | 49 +++++++++++++++++++++++++ tests/unit/matrix/SparseTests.hpp | 7 ++++ 2 files changed, 56 insertions(+) diff --git a/resolve/LinSolverDirectCuSparseILU0.cpp b/resolve/LinSolverDirectCuSparseILU0.cpp index 81c534b24..21a39c5f3 100644 --- a/resolve/LinSolverDirectCuSparseILU0.cpp +++ b/resolve/LinSolverDirectCuSparseILU0.cpp @@ -19,6 +19,55 @@ namespace ReSolve mem_.deleteOnDevice(d_aux1_); mem_.deleteOnDevice(d_aux2_); mem_.deleteOnDevice(d_ILU_vals_); + + if (buffer_ != nullptr) + { + mem_.deleteOnDevice(buffer_); + buffer_ = nullptr; + } + if (buffer_L_ != nullptr) + { + mem_.deleteOnDevice(buffer_L_); + buffer_L_ = nullptr; + } + if (buffer_U_ != nullptr) + { + mem_.deleteOnDevice(buffer_U_); + buffer_U_ = nullptr; + } + + if (descr_spsv_L_ != nullptr) + { + cusparseSpSV_destroyDescr(descr_spsv_L_); + descr_spsv_L_ = nullptr; + } + if (descr_spsv_U_ != nullptr) + { + cusparseSpSV_destroyDescr(descr_spsv_U_); + descr_spsv_U_ = nullptr; + } + + if (mat_L_ != nullptr) + { + cusparseDestroySpMat(mat_L_); + mat_L_ = nullptr; + } + if (mat_U_ != nullptr) + { + cusparseDestroySpMat(mat_U_); + mat_U_ = nullptr; + } + + if (info_A_ != nullptr) + { + cusparseDestroyCsrilu02Info(info_A_); + info_A_ = nullptr; + } + if (descr_A_ != nullptr) + { + cusparseDestroyMatDescr(descr_A_); + descr_A_ = nullptr; + } } int LinSolverDirectCuSparseILU0::setup(matrix::Sparse* A, diff --git a/tests/unit/matrix/SparseTests.hpp b/tests/unit/matrix/SparseTests.hpp index 28ed73c4b..fadbe8ea4 100644 --- a/tests/unit/matrix/SparseTests.hpp +++ b/tests/unit/matrix/SparseTests.hpp @@ -240,6 +240,13 @@ namespace ReSolve // Clean up allocated memory delete[] val_data; + // In the device case h_val_data is a scratch host buffer allocated + // above; on host it aliases the matrix-owned data and must not be freed. + if (memspace_ != memory::HOST) + { + delete[] h_val_data; + } + if (A.destroyMatrixData(memspace_) != 0) { std::cout << "Failed to destroy matrix data.\n";