Fix for heap-buffer-overflow in Eigen Map/TensorMap cached pointers#226
Fix for heap-buffer-overflow in Eigen Map/TensorMap cached pointers#226The9Cat wants to merge 7 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to eliminate a heap-buffer-overflow risk stemming from cached Eigen Map/TensorMap objects becoming stale when the underlying store buffers are resized, and updates downstream call sites to use accessor APIs (getCoefs()/setCoefs()/initCoefMap()) instead of directly manipulating cached map pointers.
Changes:
- Refactors coefficient structures to invalidate/recreate cached Eigen views around storage reallocation and exposes accessor methods for map/tensor access and assignment.
- Updates multiple basis/IO/utility call sites to use the new accessor/assignment APIs instead of
*coefs/coefs->.... - Converts
FieldBasiscached tensor maps fromshared_ptrtounique_ptrand adjusts coefficient loading/copying logic.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/SphericalBasis.cc | Switches coefficient access from *cur->coefs to cur->getCoefs() in HDF5 dump path. |
| src/SlabSL.cc | Switches coefficient assignment to cur->setCoefs(...) after allocate(). |
| src/PolarBasis.cc | Switches coefficient access from *cur->coefs to cur->getCoefs() in HDF5 dump path. |
| src/Cylinder.cc | Switches coefficient access from *cur->coefs to cur->getCoefs() in HDF5 dump path. |
| src/Cube.cc | Switches coefficient assignment to cur->setCoefs(...) after allocate(). |
| expui/FieldBasis.H | Changes cached tensor-map ownership from shared_ptr to unique_ptr. |
| expui/FieldBasis.cc | Updates tensor-map creation and rewrites set_coefs() to copy data instead of sharing internal pointers. |
| expui/CoefStruct.H | Refactors multiple coefficient structs to use cached-map accessors and reset maps prior to store.resize(). |
| expui/Coefficients.cc | Updates coefficient storage/mapping initialization and assignments to use new APIs. |
| expui/CoefContainer.cc | Updates coefficient reads/writes to use getCoefs() instead of dereferencing coefs. |
| expui/BiorthBasis.cc | Updates coefficient creation and access to use initCoefMap() / getCoefs() instead of coefs directly. |
Comments suppressed due to low confidence (2)
expui/CoefStruct.H:250
- Same issue as
SphStruct:getCoefMap()dereferencescoefswhen null (coefs->resize(...)), and resizing anEigen::Mapis not a safe way to bind it tostore.data(). Construct the map lazily with the correct pointer/dimensions instead.
if (!coefs) {
int rows = mmax+1;
int cols = nmax;
coefs->resize(rows, cols);
}
expui/FieldBasis.cc:315
- Same issue in the dof!=2 path: the size check is inverted and the
std::copydirection is reversed, soset_coefs()overwrites the input coefficient structure instead of copying intoFieldBasis::store[0].
// Check that the size of the coefficient data matches the expected size
if (p->getCoefs().size() == store[0].size())
throw std::runtime_error
("FieldBasis::set_coefs: SphFldStruct data size mismatch with FieldBasis store");
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
@copilot Can you rewrite |
|
@copilot Never mind the last request. I misunderstood my own code. In this code, every struct is a row of the table indexed (if you like) by the first entry. So, yes, it is okay as is. |
Summary
This PR implements a fix for heap-buffer-overflow in Eigen Map/TensorMap cached pointers. This error has not appeared in general usage, as far as I know, but it is a bug nonetheless. These changes affect a lot of coefficient handling code so this PR needs thorough testing. There are no user-facing changes. This could be (should be?) rebased on
mainsince it is a bug but we would need comprehensive testing.Description
This PR fixes a critical heap-buffer-overflow bug in
CoefStruct.Hcaused by dangling pointers in cachedEigen::MapandEigen::TensorMapobjects. The bug was detected by AddressSanitizer when running with 128 MPI processes and manifested as memory corruption during object destruction during cleanup operations.Cause
The original implementation cached Eigen::Map and Eigen::TensorMap
objects in shared_ptr members:
The problem:
Eigen::VectorXcdthat owns the actual memorycoefsis ashared_ptr<Map>pointing to store.data()tore.resize()is called, the vector reallocates its buffercoefsshared_ptrstill points to freed memory (dangling pointer)Maptries to clean up already-freed memory→ heap-buffer-overflow
Explanation
Eigen::MapandEigen::TensorMapare non-owning views. They shouldNEVER be wrapped in shared_ptr because shared_ptr assumes ownership
and will attempt to delete memory it doesn't own. My mistake...
A solution
Implemented a hybrid caching approach with explicit lifecycle management:
coefsmembers to private section with mutable keyword forlazy initialization
getCoefMap() const: Lazy-creates and caches Map on first readgetCoefMapMutable(): Always recreates Map before assignmentcoefs.reset()calls inallocate()methods BEFOREstore.resize()assign()methods to usegetCoefMapMutable()Key benefits:
Changes
Modified:
src/CoefStruct.HClasses updated (all 8 derived classes using Maps/TensorMaps):
SphStruct:Eigen::Map<MatrixXcd>CylStruct:Eigen::Map<MatrixXcd>SlabStruct:Eigen::TensorMap<Tensor<complex<double>, 3>>CubeStruct:Eigen::TensorMap<Tensor<complex<double>, 3>>TblStruct:Eigen::Map<VectorXcd>TrajStruct:Eigen::Map<MatrixXcd>SphFldStruct:Eigen::TensorMap<Tensor<complex<double>, 3>>CylFldStruct:Eigen::TensorMap<Tensor<complex<double>, 3>>For each class:
getCoefMap()andgetCoefMapMutable()methodscoefs.reset()before allstore.resize()callsassign()methods to use mutable accessorUpdated clients to to use (e.g.)
c->getCoefs()to get a constantreference to the data rather than
c->coefsto get a shared -pointerto the data. Similarly, clients must use
c->setCoefs(data)ratherthan
*c-coefs = datato assign data.FieldBasiswas an outlier in the pattern by setting both the Eigeninterface from its own data store directly from the
CoefStructclient. I updated this to use a
unique_ptrrather than ashared_ptrand usestd::copyto set the internal data instancefrom a passed
CoefStruct.Performance considerations
Memory overhead: Negligible
Computation overhead: None
Read performance: Optimized
Testing
Tests so far
Recommended additional testing:
Checklist
Breaking changes
None. The change is internal to
CoefStructimplementation. All publicAPIs remain unchanged. No changes in algorithmic logic and the most
of the changes should be "self-checked" by the compiler.
Related issues
128-process MPI run
SlabSL::dump_coefs_h5()