From 849e1d672f54bc92581b63143d1bfe7139d760f1 Mon Sep 17 00:00:00 2001 From: Alan Garny Date: Thu, 23 Jul 2026 22:42:45 +1200 Subject: [PATCH 1/2] New version. --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index 8f0b140aa..f2a72c157 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.20260723.0 +1.20260723.1 From 4da6a2465587748d217f159d69577a1857f1a451 Mon Sep 17 00:00:00 2001 From: Alan Garny Date: Thu, 23 Jul 2026 22:42:29 +1200 Subject: [PATCH 2/2] CellML support: removed the global CellML runtime cache to prevent use-after-free on instance deletion. Indeed, the global `sRuntimes` cache shared one JIT-compiled runtime across all `SedInstance` objects for the same CellML file. When an instance was destroyed, the cached runtime's JIT-compiled code still referenced the old thread-local solver address (`sNlaSolverAddress`), which pointed to freed memory on std::async thread-pool reuse. Each instance now creates its own runtime, eliminating the shared-state corruption. --- src/support/cellml/cellmlfile.cpp | 46 +------------------------------ src/support/cellml/cellmlfile.h | 1 - 2 files changed, 1 insertion(+), 46 deletions(-) diff --git a/src/support/cellml/cellmlfile.cpp b/src/support/cellml/cellmlfile.cpp index d8654b3ba..ed01f8136 100644 --- a/src/support/cellml/cellmlfile.cpp +++ b/src/support/cellml/cellmlfile.cpp @@ -27,20 +27,8 @@ limitations under the License. #include "libopencor/solvercvode.h" #include "libopencor/solverkinsol.h" -#include -#include - namespace libOpenCOR { -namespace { - -// Cache of compiled runtimes, keyed by CellmlFile pointer. - -std::mutex sRuntimesMutex; // NOLINT -std::unordered_map sRuntimes; // NOLINT - -} // namespace - CellmlFile::Impl::Impl(const FilePtr &pFile, const libcellml::ModelPtr &pModel, bool pStrict) : mFile(pFile) , mModel(pModel) @@ -128,28 +116,7 @@ libcellml::AnalyserModelPtr CellmlFile::Impl::analyserModel() const CellmlFileRuntimePtr CellmlFile::Impl::runtime(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver) { - // Check whether we already have a compiled runtime and if so then return it. - - { - const std::scoped_lock lock(sRuntimesMutex); - const auto it = sRuntimes.find(pCellmlFile.get()); - - if (it != sRuntimes.end()) { - return it->second; - } - } - - // There is no compiled runtime for this CellML file, so create one, track it, and return it. - - auto runtime = CellmlFileRuntime::create(pCellmlFile, pNlaSolver); - - { - const std::scoped_lock lock(sRuntimesMutex); - - sRuntimes.try_emplace(pCellmlFile.get(), runtime); - } - - return runtime; + return CellmlFileRuntime::create(pCellmlFile, pNlaSolver); } CellmlFile::CellmlFile(const FilePtr &pFile, const libcellml::ModelPtr &pModel, bool pStrict) @@ -157,17 +124,6 @@ CellmlFile::CellmlFile(const FilePtr &pFile, const libcellml::ModelPtr &pModel, { } -CellmlFile::~CellmlFile() -{ - // Stop tracking our compiled runtime. - - { - const std::scoped_lock lock(sRuntimesMutex); - - sRuntimes.erase(this); - } -} - CellmlFile::Impl *CellmlFile::pimpl() { return static_cast(Logger::mPimpl.get()); diff --git a/src/support/cellml/cellmlfile.h b/src/support/cellml/cellmlfile.h index cbc4e5a2b..e0ec93bba 100644 --- a/src/support/cellml/cellmlfile.h +++ b/src/support/cellml/cellmlfile.h @@ -46,7 +46,6 @@ class LIBOPENCOR_UNIT_TESTING_EXPORT CellmlFile: public Logger { public: CellmlFile() = delete; - ~CellmlFile() override; CellmlFile(const CellmlFile &pOther) = delete; CellmlFile(CellmlFile &&pOther) noexcept = delete;