Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,20 @@ TEST_F(InterventionalRadiologyController_test, action_movesControlledInstrument)
EXPECT_NEAR(ctrl->d_xTip.getValue()[0], x0Before, 1e-9);
}

/// A negative controlledInstrument must be rejected (bounds check), not used as a negative index.
TEST_F(InterventionalRadiologyController_test, action_negativeControlledInstrument_isRejected)
{
loadScene(singleInstrumentScene("controlledInstrument='-1'"));
auto* ctrl = getController();
ASSERT_NE(ctrl, nullptr);

const Real x0Before = ctrl->d_xTip.getValue()[0];

// applyAction returns early for an out-of-range (negative) instrument: no crash, no change.
ctrl->applyAction(BeamAdapterAction::MOVE_FORWARD);
EXPECT_NEAR(ctrl->d_xTip.getValue()[0], x0Before, 1e-9);
}


///////////////////////////////////// Getters / helpers ///////////////////////////////////////////

Expand Down Expand Up @@ -595,4 +609,20 @@ TEST_F(InterventionalRadiologyController_test, deploy_runsStable)
EXPECT_EQ(ctrl->getComponentState(), ComponentState::Valid);
}

/// A negative controlledInstrument during stepping must fall back to instrument 0 (no negative index).
TEST_F(InterventionalRadiologyController_test, deploy_negativeControlledInstrument_fallsBackToZero)
{
loadAndInitRoot(deploymentScene("xtip='0' step='0.5' speed='10' controlledInstrument='-1'"));

auto* ctrl = getController();
ASSERT_NE(ctrl, nullptr);
ASSERT_EQ(ctrl->getComponentState(), ComponentState::Valid);

for (int i = 0; i < 10; ++i)
sofa::simulation::node::animate(m_root.get(), 0.01);

// the step is applied to the fallback instrument 0 instead of dereferencing index -1
EXPECT_GT(ctrl->d_xTip.getValue()[0], 0.0);
}

} // namespace beamadapter_test
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ void InterventionalRadiologyController<DataTypes>::onBeginAnimationStep(const do
if(m_FF || m_RW)
{
int id = d_controlledInstrument.getValue();
if (id >= (int)xInstrTip.size())
if (id < 0 || id >= (int)xInstrTip.size())
{
msg_warning()<<"Controlled Instument num "<<id<<" does not exist (size ="<< xInstrTip.size() <<") use instrument 0 instead";
id=0;
Expand Down Expand Up @@ -406,7 +406,7 @@ template <class DataTypes>
void InterventionalRadiologyController<DataTypes>::applyAction(BeamAdapterAction action)
{
int id = d_controlledInstrument.getValue();
if (id >= int(m_instrumentsList.size()))
if (id < 0 || id >= int(m_instrumentsList.size()))
{
msg_warning() << "Controlled Instrument num " << id << " does not exist (size =" << m_instrumentsList.size() << ").";
return;
Expand Down Expand Up @@ -500,6 +500,14 @@ void InterventionalRadiologyController<DataTypes>::computeInstrumentsCurvAbs(typ
type::vector<sofa::Size> density_I;
m_instrumentsList[i]->getSamplingParameters(xP_noticeable_I, density_I); // sampling of the different section of this instrument

// an instrument must provide at least one noticeable point; otherwise the loop below and the
// final key point access (xP_noticeable_I.size()-1) would read out of bounds.
Comment on lines +503 to +504

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
// an instrument must provide at least one noticeable point; otherwise the loop below and the
// final key point access (xP_noticeable_I.size()-1) would read out of bounds.
// Detect if the instrument does not provide at least one noticeable point

if (xP_noticeable_I.empty())
{
msg_error() << "Instrument " << i << " provides no sampling parameters (no noticeable point). Skipping it.";
continue;
}

// check each interval of noticeable point to see if they go out (>0) and use corresponding density to sample the interval.
for (int j=0; j<(int)(xP_noticeable_I.size()-1); j++)
{
Expand Down Expand Up @@ -603,10 +611,8 @@ void InterventionalRadiologyController<DataTypes>::interventionalRadiologyCollis
Real xAbsCurv = m_nodeCurvAbs[node];
int firstInstruOnx = m_idInstrumentCurvAbsTable[node][0];

type::vector<unsigned int> segRemove;

for (unsigned int it=0; it<m_instrumentsList.size(); it++)
segRemove.push_back(0);
// -1 means "nothing to remove" for that instrument
type::vector<int> segRemove(m_instrumentsList.size(), -1);

for (int i = static_cast<int>(xPointList.size()) - 1; i>=0; i--)
{
Expand Down Expand Up @@ -659,7 +665,7 @@ void InterventionalRadiologyController<DataTypes>::interventionalRadiologyCollis

for (unsigned int it=0; it<m_instrumentsList.size(); it++)
{
if(segRemove[it]!=0)
if(segRemove[it]!=-1)
removeEdge.push_back(segRemove[it]);
}

Expand Down Expand Up @@ -850,6 +856,11 @@ void InterventionalRadiologyController<DataTypes>::applyInterventionalRadiologyC
break;
}

// If no previous node was found beyond xCurvAbs (see the "Case 1" warning above), the loop
// ends with prev_xId == m_nodeCurvAbs.size(): clamp to the last node to avoid out-of-bounds access.
Comment on lines +859 to +860

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
// If no previous node was found beyond xCurvAbs (see the "Case 1" warning above), the loop
// ends with prev_xId == m_nodeCurvAbs.size(): clamp to the last node to avoid out-of-bounds access.
// In case prev_xId exceeds size of m_nodeCurvAbs, clamp to the last node to avoid out-of-bounds access

if (prev_xId >= m_nodeCurvAbs.size())
prev_xId = m_nodeCurvAbs.size() - 1;

sofa::Index prev_globalNodeId = prev_numberOfUnactiveNodes + prev_xId;
const Real prev_xCurvAbs = m_nodeCurvAbs[prev_xId];

Expand Down Expand Up @@ -950,7 +961,8 @@ void InterventionalRadiologyController<DataTypes>::applyInterventionalRadiologyC
{
RealConstIterator it = rigidCurvAbs->begin();

for (unsigned int i=0; i<newCurvAbs.size(); i++)
// firstSimulatedNode + i indexes into the dofs, so it must stay within [0, numberOfNodes-1]
for (unsigned int i=0; i<newCurvAbs.size() && (firstSimulatedNode + i) < numberOfNodes; i++)
{
if (newCurvAbs[i] < ((*it)+ std::numeric_limits<float>::epsilon()) && newCurvAbs[i] > ((*it)- std::numeric_limits<float>::epsilon())) // node= border of the rigid segment
{
Expand Down
Loading