From add582678f89a935e6f1b23b79cb56e5bd16b041 Mon Sep 17 00:00:00 2001 From: Zachary Ferguson Date: Sun, 1 Nov 2026 19:45:40 -0500 Subject: [PATCH 1/4] Fix out-of-bounds read in GCP core_vertex_ids - Map each core point to its vertex slot instead of indexing the vertex ids with DOF indices - Return one id per core point (N_CORE_POINTS) rather than per DOF Co-Authored-By: Claude Sonnet 5.5 --- src/ipc/gcp/collisions/gcp_collision.cpp | 17 ++++++++++++----- src/ipc/gcp/collisions/gcp_collision.hpp | 5 ++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/ipc/gcp/collisions/gcp_collision.cpp b/src/ipc/gcp/collisions/gcp_collision.cpp index b6e7d0328..3b0d2ee2d 100644 --- a/src/ipc/gcp/collisions/gcp_collision.cpp +++ b/src/ipc/gcp/collisions/gcp_collision.cpp @@ -486,12 +486,19 @@ double GCPCollisionTemplate::compute_distance( template auto GCPCollisionTemplate::core_vertex_ids() const - -> std::array + -> std::array { - std::array vids {}; - auto ids = get_core_indices(); - for (int i = 0; i < N_CORE_DOFS; i++) { - vids[i] = m_vertex_ids[ids[i]]; + // m_vertex_ids has one entry per vertex, so we cannot index it with the + // DOF indices from get_core_indices(). Those run up to ELEMENT_SIZE and + // read past the end of the vector. Instead, we map each core point to its + // vertex slot: the core points are the first vertices of each primitive. + std::array vids {}; + for (int i = 0; i < PrimitiveA::N_CORE_POINTS; i++) { + vids[i] = m_vertex_ids[i]; + } + for (int i = 0; i < PrimitiveB::N_CORE_POINTS; i++) { + vids[PrimitiveA::N_CORE_POINTS + i] = + m_vertex_ids[primitive_a->n_vertices() + i]; } return vids; } diff --git a/src/ipc/gcp/collisions/gcp_collision.hpp b/src/ipc/gcp/collisions/gcp_collision.hpp index 1df8ee54c..1a50c178f 100644 --- a/src/ipc/gcp/collisions/gcp_collision.hpp +++ b/src/ipc/gcp/collisions/gcp_collision.hpp @@ -171,7 +171,10 @@ class GCPCollisionTemplate : public GCPCollision { CollisionType type() const override; Eigen::Vector get_core_indices() const; - std::array core_vertex_ids() const; + /// @brief Get the mesh vertex ids of the core points. + /// @return One vertex id per core point: the core points of primitive A + /// followed by those of primitive B. + std::array core_vertex_ids() const; int num_vertices() const override { From 0e5619dbb5128412e063b51466af80f7fad39e3b Mon Sep 17 00:00:00 2001 From: Zachary Ferguson Date: Sun, 1 Nov 2026 20:03:37 -0500 Subject: [PATCH 2/4] Update scalable_ccd.cmake --- cmake/recipes/scalable_ccd.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/recipes/scalable_ccd.cmake b/cmake/recipes/scalable_ccd.cmake index f783d3744..aca0b6675 100644 --- a/cmake/recipes/scalable_ccd.cmake +++ b/cmake/recipes/scalable_ccd.cmake @@ -8,7 +8,7 @@ message(STATUS "Third-party: creating target 'scalable_ccd::scalable_ccd'") include(CPM) CPMAddPackage( - URI "gh:continuous-collision-detection/scalable-ccd#ccf4bc19aba432b9d5abdd41ae55d15ea166a0a5" + URI "gh:continuous-collision-detection/scalable-ccd#049d8af986c9cc115e013df7c8af791c2a513dcc" OPTIONS "SCALABLE_CCD_WITH_CUDA ${IPC_TOOLKIT_WITH_CUDA}" ) From 4d8e940ce4d7b14ade0b60e684b72bcdd393eed4 Mon Sep 17 00:00:00 2001 From: Zachary Ferguson Date: Sun, 1 Nov 2026 20:35:09 -0500 Subject: [PATCH 3/4] Match finite-diff Eigen vectorization to ipc_toolkit - Define EIGEN_DONT_VECTORIZE on finite-diff when SIMD is on, as for MeshFEMSparse - Avoids alignment and allocator mismatches on matrices that cross the library boundary Co-Authored-By: Claude Sonnet 5.5 --- cmake/recipes/finite_diff.cmake | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cmake/recipes/finite_diff.cmake b/cmake/recipes/finite_diff.cmake index 34edda78b..d2688591b 100644 --- a/cmake/recipes/finite_diff.cmake +++ b/cmake/recipes/finite_diff.cmake @@ -9,5 +9,16 @@ message(STATUS "Third-party: creating target 'finitediff::finitediff'") include(CPM) CPMAddPackage("gh:zfergus/finite-diff@1.0.4") +# ipc_toolkit compiles with EIGEN_DONT_VECTORIZE=1 when SIMD is enabled, which +# changes how Eigen aligns and allocates matrices. finite-diff takes matrices +# that our lambdas allocate and then reshapes and frees them inside its own +# library code. If the two sides disagree about alignment, the library either +# trips Eigen's "data is not aligned" assert (Debug) or frees a pointer that +# was never allocated with Eigen's aligned allocator (Release). We make the +# define PUBLIC so both sides always agree, the same way we do for MeshFEMSparse. +if(IPC_TOOLKIT_WITH_SIMD) + target_compile_definitions(finitediff_finitediff PUBLIC EIGEN_DONT_VECTORIZE=1) +endif() + # Folder name for IDE set_target_properties(finitediff_finitediff PROPERTIES FOLDER "ThirdParty") \ No newline at end of file From 6c8f9da52e1092ab18446d9d2bfc0491dfd91f26 Mon Sep 17 00:00:00 2001 From: Zachary Ferguson Date: Sun, 1 Nov 2026 20:58:46 -0500 Subject: [PATCH 4/4] Ignore lcov consistency errors when filtering coverage lcov 2.4 on ubuntu-26.04 reports an unhit deleting destructor as an inconsistency, which fails the --remove step. Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index be126ddc9..e05462490 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -79,7 +79,7 @@ jobs: cd build ctest --verbose -j ${{ steps.cpu-cores.outputs.count }} lcov --directory . --capture --output-file coverage.info --ignore-errors inconsistent,format,gcov,mismatch - lcov --remove coverage.info --ignore-errors unused '/usr/*' "$HOME/.cache/*" "*tests/*" --output-file coverage.info + lcov --remove coverage.info --ignore-errors unused,inconsistent '/usr/*' "$HOME/.cache/*" "*tests/*" --output-file coverage.info - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v7