Skip to content

Conversation

@vincent-noel
Copy link
Collaborator

I had some crash that I wanted to fix for a while, and finally found the issues:

  • pAttackTarget is not cleaned when the target is deleted, resulting in a use after free
  • neighbors are not always symmetrical, but when we delete a cell we suppose they are, resulting in a use after free
  • spring_attachments are not always symmetrical, but when we delete a cell we suppose they are, resulting in a use after free.

Here the fixes I'm proposing for now:

  • When deleting a cell, I check that no other cell has it as an attack target and remove it if so
  • When deleting a cell, after removing it from its known neighbors, check every cell to remove from unknown (non-symmetrical) neighbors
  • When deleting a cell, after removing it from its known spring_attachments, check every cell to remove from unknown (non-symmetrical) spring_attachments

Clearly, this can be improved, but it fixes the present issues.

This comment was marked as outdated.

vincent-noel and others added 5 commits December 2, 2025 14:45
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

This comment was marked as outdated.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +3477 to +3483
for (int j=0; j < all_cells->size(); j++)
{
Cell* pC = (*all_cells)[j];
if (( pC != this) && pC->phenotype.cell_interactions.pAttackTarget == this) {
pC->phenotype.cell_interactions.pAttackTarget = NULL;
}
}
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop iterates over all_cells without synchronization, and the #pragma omp parallel for creates concurrent writes to pAttackTarget. This can cause race conditions if cells are being deleted or modified concurrently. Consider removing the parallel directive or adding proper synchronization.

Copilot uses AI. Check for mistakes.
Comment on lines +3474 to +3484
void Cell::remove_self_from_attackers( void )
{
#pragma omp parallel for
for (int j=0; j < all_cells->size(); j++)
{
Cell* pC = (*all_cells)[j];
if (( pC != this) && pC->phenotype.cell_interactions.pAttackTarget == this) {
pC->phenotype.cell_interactions.pAttackTarget = NULL;
}
}
}
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This O(n) loop iterates through all cells to clean up attack targets, which can be expensive for large simulations with many cells. Consider using a reverse index (e.g., a set/list of attackers stored on each potential target cell) to make this operation O(k) where k is the number of attacking cells, which is typically much smaller than n.

Copilot uses AI. Check for mistakes.
Comment on lines +3409 to +3422
for ( int i = 0; i < all_cells->size(); i++ )
{
Cell* pC = (*all_cells)[i];
if (pC != this)
{
auto SearchResult = std::find(
pC->state.neighbors.begin(),pC->state.neighbors.end(),this );
if ( SearchResult != pC->state.neighbors.end() )
{
std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a neighbor!" << std::endl;
pC->state.neighbors.erase( SearchResult );
}
}
}
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loop iterates over all_cells without thread safety protections. If cell deletion can occur concurrently (e.g., from multiple threads), this could lead to iterator invalidation or accessing deallocated memory. Consider adding appropriate synchronization or documenting that delete_cell must be called from a single-threaded context.

Copilot uses AI. Check for mistakes.
Comment on lines +3455 to +3469
for ( int i = 0; i < all_cells->size(); i++ )
{
Cell* pC = (*all_cells)[i];
if (pC != this)
{
auto SearchResult = std::find(
pC->state.spring_attachments.begin(),pC->state.spring_attachments.end(),this );
if ( SearchResult != pC->state.spring_attachments.end() )
{
std::cout << "Cell " << pC->ID << " still has cell " << this->ID << " as a spring attachment!" << std::endl;
pC->state.spring_attachments.erase( SearchResult );
}

}
}
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loop iterates over all_cells without thread safety protections. If cell deletion can occur concurrently (e.g., from multiple threads), this could lead to iterator invalidation or accessing deallocated memory. Consider adding appropriate synchronization or documenting that delete_cell must be called from a single-threaded context.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant