System information
- solvespace master 952c11c0, built on Arch Linux
- Also present in every release since 7c60be82 (2016)
Description
The pass that removes overlapping line segments at the end of
ExportLinesAndMesh walks the edge list with raw SEdge * and calls
sel->AddEdge() when it splits a segment. List::Add may reallocate, and
sei, sej, pAj and pBj all point into that array, so the writes right
after the call can land in memory that has been moved:
// split segment
if(ta < 0.0 - eps && tb > 1.0 + eps) {
sel->AddEdge(sei->b, *pBj, sej->auxA, sej->auxB);
*pBj = sei->a; // pBj may dangle here
continue;
}
and a few lines further down:
if(ta > 0.0 + eps && tb < 1.0 - eps) {
sel->AddEdge(*pBj, sei->b, sei->auxA, sei->auxB);
sei->b = *pAj; // and sei, pAj here
i--;
break;
}
How visible it is
Hard to hit, because the collinearity test above it uses eps = 1e-6, so
splits are rare. It is not only theoretical: of 172 sketches of mine, three
export a different file once the pointers are refetched - by two vertices on
one and by 0.015 of total length on another. Those three are reading moved
memory today.
It becomes easy to hit as soon as splits are common. I widened that
collinearity tolerance in a fork while chasing a different problem, and the
first file that reached this code segfaulted.
Fix
Take the values before the call, then re-fetch the pointers after it. I can
send a PR if that is the shape you want; pAj/pBj need recomputing from the
refetched sej since they may be swapped.
System information
Description
The pass that removes overlapping line segments at the end of
ExportLinesAndMesh walks the edge list with raw SEdge * and calls
sel->AddEdge() when it splits a segment. List::Add may reallocate, and
sei, sej, pAj and pBj all point into that array, so the writes right
after the call can land in memory that has been moved:
// split segment
if(ta < 0.0 - eps && tb > 1.0 + eps) {
sel->AddEdge(sei->b, *pBj, sej->auxA, sej->auxB);
*pBj = sei->a; // pBj may dangle here
continue;
}
and a few lines further down:
if(ta > 0.0 + eps && tb < 1.0 - eps) {
sel->AddEdge(*pBj, sei->b, sei->auxA, sei->auxB);
sei->b = *pAj; // and sei, pAj here
i--;
break;
}
How visible it is
Hard to hit, because the collinearity test above it uses eps = 1e-6, so
splits are rare. It is not only theoretical: of 172 sketches of mine, three
export a different file once the pointers are refetched - by two vertices on
one and by 0.015 of total length on another. Those three are reading moved
memory today.
It becomes easy to hit as soon as splits are common. I widened that
collinearity tolerance in a fork while chasing a different problem, and the
first file that reached this code segfaulted.
Fix
Take the values before the call, then re-fetch the pointers after it. I can
send a PR if that is the shape you want; pAj/pBj need recomputing from the
refetched sej since they may be swapped.