mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-08 04:52:10 +00:00
Merge pull request #1494 from tangency-labs/fix/ub175-v101-guards
difftest: run the UB-175 inputs on v1.0.1; narrow the UB-241 stop to the GlueVertices path
This commit is contained in:
@@ -375,10 +375,10 @@ fn to_output(o: &[f64]) -> Result<Output, HarnessError> {
|
||||
}
|
||||
|
||||
fn cpp(i: &Inputs) -> Result<Output, HarnessError> {
|
||||
if ub_175(i)? {
|
||||
if ub_242(i)? {
|
||||
return Ok(Output::rejected(
|
||||
"UB-175: ~SmProtoTopologyManager frees a proto edge that a proto vertex \
|
||||
still lists, then ~SmProtoVertex reads it; not run",
|
||||
"UB-242: IntersectInsertRelate ignores a failed imprint that edited a B-rep \
|
||||
and goes on with the half-edited B-reps; not run",
|
||||
));
|
||||
}
|
||||
match ffi::run(&to_call(i)) {
|
||||
@@ -402,17 +402,18 @@ fn measured_output(o: &[f64]) -> Result<Output, HarnessError> {
|
||||
Ok(to_output(head)?.values("measures", measures.to_vec()))
|
||||
}
|
||||
|
||||
/// Whether the Boolean reaches UB-175 (`plan/upstream-issues.md`): the teardown of
|
||||
/// `IntersectInsertRelate`'s proto topology manager reads a proto edge it already
|
||||
/// freed, so the input is undefined in C++ (AGENTS.md section 4, step 5.6) and [`cpp`]
|
||||
/// rejects it. Decided from the inputs and the Rust side only: the port replays the
|
||||
/// teardown, goes on with its defined result and records `"UB-175"` on the kernel
|
||||
/// ([`Kernel::upstream_ub_passed`]).
|
||||
fn ub_175(i: &Inputs) -> Result<bool, HarnessError> {
|
||||
/// Whether the Boolean reaches UB-242 (`plan/upstream-issues/`): an imprint in
|
||||
/// `IntersectInsertRelate` fails after it may have edited a B-rep, and upstream goes on
|
||||
/// with the half-edited B-reps, so the input is undefined in C++ (AGENTS.md section 4,
|
||||
/// step 5.6) and [`cpp`] rejects it. Decided from the inputs and the Rust side only: the
|
||||
/// port stops there (tangency-labs/smlib af8db37) and records `"UB-242"` on the kernel
|
||||
/// ([`Kernel::upstream_ub_passed`]). (Until the v1.0.1 pin this guard was UB-175's, the
|
||||
/// proto topology manager teardown, which v1.0.1 defines.)
|
||||
fn ub_242(i: &Inputs) -> Result<bool, HarnessError> {
|
||||
let mut k = Kernel::default();
|
||||
match rust_call_with(&mut k, i, &|_| {}) {
|
||||
Ok(_) | Err(Stop::Deferred(_) | Stop::Torus(_)) => {
|
||||
Ok(k.upstream_ub_passed.contains(&"UB-175"))
|
||||
Ok(k.upstream_ub_passed.contains(&"UB-242"))
|
||||
}
|
||||
Err(Stop::Harness(e)) => Err(e),
|
||||
}
|
||||
|
||||
@@ -4313,15 +4313,12 @@ fn run_part2_op(
|
||||
o.push(NULL_DEREF);
|
||||
return Ok(());
|
||||
}
|
||||
// (where upstream reads a freed proto edge, UB-175, the shim's verbatim copy
|
||||
// unlinks the stale entries and answers -2; the port goes on with its defined
|
||||
// teardown and records the condition on the kernel. After a failed imprint,
|
||||
// UB-242, both stop and the shim answers -2.)
|
||||
// (after a failed imprint, UB-242, both stop and the shim answers -2. The
|
||||
// v1.0.1 manager teardown is defined, so a run the port records UB-175 for,
|
||||
// a v1.0.0 defect, is compared like any other.)
|
||||
k.upstream_ub_passed.clear();
|
||||
let r = ti.intersect_insert_relate(k, two, TI_HANDLE);
|
||||
if k.upstream_ub_passed.contains(&"UB-242")
|
||||
|| k.upstream_ub_passed.contains(&"UB-175")
|
||||
{
|
||||
if k.upstream_ub_passed.contains(&"UB-242") {
|
||||
o.push(NULL_DEREF);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
//! Coordinates/dimensions are bounded by 1e100 through the shared reader, leaving
|
||||
//! headroom for endpoint addition and squared-distance products in constructors.
|
||||
//! This is the oracle's supported numeric domain, not a kernel input restriction.
|
||||
//! UB-175 [PSIRT] in `IntersectInsertRelate` / `~SmProtoTopologyManager` is not
|
||||
//! exercised by the current empty/vertex/open-line/plane operand families (no
|
||||
//! closed edges). Adding circles or closed wires requires rechecking that guard.
|
||||
//! UB-175 [PSIRT] (`~SmProtoTopologyManager`) is a v1.0.0 defect: v1.0.1's teardown
|
||||
//! is defined, so closed edges need no guard here.
|
||||
use crate::{call_with_buffer, FfiError};
|
||||
|
||||
mod sys {
|
||||
|
||||
@@ -45,9 +45,9 @@
|
||||
//! are not vertices where upstream dereferences them, edges of a vertex without
|
||||
//! curve, faces without surface, 46 without the maps reaching `Relate`'s insert, 52's
|
||||
//! edges without curve, vertices or (the B-rep vertex's) primary edge use) and
|
||||
//! answers `-2`; it also answers `-2` for a 47 whose manager teardown would read a
|
||||
//! freed proto edge (UB-175: the shim runs a verbatim copy of
|
||||
//! `IntersectInsertRelate` that unlinks the stale entries first).
|
||||
//! answers `-2`; it also answers `-2` for a 47 whose failed imprint may have edited a
|
||||
//! B-rep (UB-242: the shim runs a verbatim copy of `IntersectInsertRelate` that stops
|
||||
//! there).
|
||||
//!
|
||||
//! 49 checks the index against both classifications only: upstream bounds its
|
||||
//! `index + 1` writes by the B-rep classification's size, so with a shorter other
|
||||
|
||||
@@ -30,11 +30,10 @@
|
||||
//! deleting a proto edge on its own (the difftest's script ops) stays upstream's.
|
||||
//! Without it, the stale id names a deleted slot and the later passes fail with
|
||||
//! `InvalidInput`.
|
||||
//! - **Deleted vertex in `GlueCoincidentBrepVertices`** (SolidCraft fix,
|
||||
//! tangency-labs/smlib da8d05e, UB-241). For two coincident vertices joined by
|
||||
//! several common edges, upstream goes on to the next edge after squeezing or
|
||||
//! gluing away the second vertex, and uses it after the free. The loop stops once
|
||||
//! that vertex is recorded as deleted, in both B-reps' halves.
|
||||
//! - **UB-241 in `GlueCoincidentBrepVertices`** (SolidCraft fix, tangency-labs/smlib
|
||||
//! da8d05e): use-after-free in `GlueCoincidentBrepVertices` on the `GlueVertices`
|
||||
//! path; the port stops before that call. The delete branch is defined and runs as
|
||||
//! upstream.
|
||||
//! - **B-rep access.** Topology objects are the caller's handles `O`, as in
|
||||
//! [`p1`](super::p1). What these functions read from vertices and edges, and the
|
||||
//! store new curves go to, is [`ProtoBrep`] (of `topology.proto_topology_p2`, which
|
||||
@@ -1155,13 +1154,10 @@ impl<O: ClassObject> ProtoTopologyManager<O> {
|
||||
}
|
||||
} else {
|
||||
for &edge in edges {
|
||||
// SolidCraft change (tangency-labs/smlib da8d05e): squeezing
|
||||
// or gluing for an earlier common edge deleted v2; every
|
||||
// later step would use it (and the edges it ended) after the
|
||||
// free, so stop.
|
||||
if deleted_v.get(jj).copied().flatten() == Some(v2) {
|
||||
break;
|
||||
}
|
||||
// UB-241 (SolidCraft fix, tangency-labs/smlib da8d05e): stop
|
||||
// before the GlueVertices path; the delete branch runs as
|
||||
// upstream.
|
||||
let v2_deleted = deleted_v.get(jj).copied().flatten() == Some(v2);
|
||||
let curve_handle =
|
||||
brep.edge_curve(edge).ok_or(dangling("edge is unknown"))?;
|
||||
let ivl = brep
|
||||
@@ -1171,6 +1167,10 @@ impl<O: ClassObject> ProtoTopologyManager<O> {
|
||||
.approximate_length(k, &ivl, 5, false)?
|
||||
.0;
|
||||
if len < xs {
|
||||
if v2_deleted && brep.edge_other_vertex(edge, Some(v1))? != Some(v1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// If any to-be-deleted topology is mated, remove the
|
||||
// relationship when done.
|
||||
let edge_mate = mates
|
||||
@@ -1230,6 +1230,10 @@ impl<O: ClassObject> ProtoTopologyManager<O> {
|
||||
Err(e) => ret = Err(e),
|
||||
}
|
||||
} else {
|
||||
// UB-241: stop before GlueVertices
|
||||
if v2_deleted {
|
||||
break;
|
||||
}
|
||||
// coincident Vertices connected by a long Edge: Glue.
|
||||
if v1 != v2 {
|
||||
ignore_soft(brep.glue_vertices(
|
||||
@@ -1847,24 +1851,35 @@ mod tests {
|
||||
|
||||
/// Vertices 0 and 1 of B-rep 1 coincide and are joined by the long edges 0 and 1
|
||||
/// (both on curve 0); vertices 2 and 3 of B-rep 2 are far apart. Records every
|
||||
/// `GlueVertices` call and refuses one of a vertex already glued away.
|
||||
/// `GlueVertices` call and refuses one of a vertex already glued away; records
|
||||
/// every `SqueezeEdge` and `DeleteEdge` (after a squeeze, `edge_other_vertex` answers
|
||||
/// vertex 0).
|
||||
struct Pool {
|
||||
curves: Vec<Curve>,
|
||||
glued: Vec<(H, H)>,
|
||||
squeezed: Vec<H>,
|
||||
deleted: Vec<H>,
|
||||
}
|
||||
|
||||
impl Pool {
|
||||
fn new() -> Self {
|
||||
Self::with_length(10.0)
|
||||
}
|
||||
|
||||
/// As [`Pool::new`] with edges `length` long.
|
||||
fn with_length(length: f64) -> Self {
|
||||
let line = Line::create_line_segment(
|
||||
3,
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
Vec3::new(10.0, 0.0, 0.0),
|
||||
Vec3::new(length, 0.0, 0.0),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
Self {
|
||||
curves: vec![Curve::from(line)],
|
||||
glued: Vec::new(),
|
||||
squeezed: Vec::new(),
|
||||
deleted: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1905,7 +1920,11 @@ mod tests {
|
||||
Ok(Some(vtx(0)))
|
||||
}
|
||||
fn edge_other_vertex(&self, _edge: H, _vertex: Option<H>) -> Result<Option<H>> {
|
||||
Ok(Some(vtx(1)))
|
||||
Ok(Some(if self.squeezed.is_empty() {
|
||||
vtx(1)
|
||||
} else {
|
||||
vtx(0)
|
||||
}))
|
||||
}
|
||||
fn vertex_point(&self, vertex: H) -> Result<Point3> {
|
||||
Ok(match vertex.0 {
|
||||
@@ -1983,10 +2002,11 @@ mod tests {
|
||||
&mut self,
|
||||
_k: &mut Kernel,
|
||||
_brep: H,
|
||||
_edge: H,
|
||||
edge: H,
|
||||
_survivor: H,
|
||||
) -> Result<()> {
|
||||
Err(SolidError::invalid_input("test", "unused"))
|
||||
self.squeezed.push(edge);
|
||||
Ok(())
|
||||
}
|
||||
fn brep_point_3d_classify(
|
||||
&mut self,
|
||||
@@ -2019,7 +2039,8 @@ mod tests {
|
||||
}
|
||||
|
||||
impl BrepGlue<H> for Pool {
|
||||
fn delete_edge(&mut self, _k: &mut Kernel, _brep: H, _edge: H) -> Result<()> {
|
||||
fn delete_edge(&mut self, _k: &mut Kernel, _brep: H, edge: H) -> Result<()> {
|
||||
self.deleted.push(edge);
|
||||
Ok(())
|
||||
}
|
||||
fn glue_vertices(
|
||||
@@ -2079,6 +2100,37 @@ mod tests {
|
||||
assert_eq!(m.proto_vertex(pv).unwrap().vertex_definitions().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn glue_deletes_a_second_short_common_edge_after_the_squeeze() {
|
||||
// the delete branch runs as upstream after a squeeze;
|
||||
// boolean.api_brep crossing_creases_union_d1e-05
|
||||
let mut k = Kernel::default();
|
||||
let mut pool = Pool::with_length(1e-6);
|
||||
let b = H(100, ClassObjectKind::Other);
|
||||
let e = Brep::new();
|
||||
let mut m = ProtoTopologyManager::new(&k, (b, &e), (b, &e), b, 1e-5, 1e-3, -1.0).unwrap();
|
||||
let p = Point3::new(0.0, 0.0, 0.0);
|
||||
let pv = m.new_proto_vertex(p, 1e-5).unwrap();
|
||||
m.add_proto_vertex(pv);
|
||||
let r = H(50, ClassObjectKind::Region);
|
||||
for (v1, v2) in [(vtx(0), vtx(2)), (vtx(1), vtx(3))] {
|
||||
let mut vd =
|
||||
VertexDefinition::new(&k, &pool, ®ion_pc(r), ®ion_pc(r), p, 1e-5, 1e-6, None)
|
||||
.unwrap();
|
||||
vd.set_final_vertex(1, Some(v1));
|
||||
vd.set_final_vertex(2, Some(v2));
|
||||
let id = m.insert_vertex_definition(vd).unwrap();
|
||||
m.add_vertex_definition(pv, id).unwrap();
|
||||
}
|
||||
|
||||
m.glue_coincident_brep_vertices(&mut k, &mut pool, &mut NoMates)
|
||||
.unwrap();
|
||||
assert_eq!(pool.squeezed, vec![edg(0)]);
|
||||
assert_eq!(pool.deleted, vec![edg(1)]);
|
||||
assert!(pool.glued.is_empty());
|
||||
assert_eq!(m.proto_vertex(pv).unwrap().vertex_definitions().len(), 1);
|
||||
}
|
||||
|
||||
/// A vertex definition first placed by a grazing crossing is placed again by a
|
||||
/// steeper one, whose misplacement bound along the edge (gap over sine) is smaller,
|
||||
/// and not by a later, more grazing one (WR-410, #1059).
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
// sm_FindFirstLowestDimensionSol, sm_IntersectShellVertices, sm_IntersectWireLaminaEdges,
|
||||
// sm_SetEdgeClass, sm_CheckInteriorEdge, sReasonType, sm_TestAndSplitTwoEdges and
|
||||
// sm_TestAndImprintFace are VERBATIM copies of upstream (commit c9c5979, lines 1272-1572,
|
||||
// 2676-2829 and 6564-6861) inside namespace sc_ti_p1 (SM_DEBUG_CODE and CBI_CACHE are not
|
||||
// defined in the reference build).
|
||||
// 2676-2829 and 6564-6861; byte-identical in v1.0.1 3fd5b95, lines 1272-1572, 2676-2829 and
|
||||
// 6578-6875) inside namespace sc_ti_p1 (SM_DEBUG_CODE and CBI_CACHE are not defined in the
|
||||
// reference build).
|
||||
//
|
||||
// One entry point, sc_boolean_topology_intersector_p1_eval(op, path, in, n_in, out, cap,
|
||||
// out_len). Layouts are documented in
|
||||
@@ -820,62 +821,6 @@ struct TiMgr
|
||||
};
|
||||
template struct TiRob<TiMgr, &SmTopologyIntersector::m_pProtoTopoMgr>;
|
||||
|
||||
// UB-175: ~SmProtoTopologyManager deletes the listed proto edges before the listed proto
|
||||
// vertices; each ~SmProtoEdge unlists itself once from its start vertex and once from a
|
||||
// different end vertex, then each ~SmProtoVertex calls RemoveProtoVertex on every edge it
|
||||
// still lists. An edge whose ends coincide is listed twice there (AddProtoEdge, DD1) and a
|
||||
// combined vertex keeps entries of edges that no longer end at it: those entries are freed
|
||||
// edges. Replays the unlisting on copies and returns whether any listed vertex keeps a
|
||||
// listed edge; with `unlink`, removes those entries first so the destructor stays defined
|
||||
// (the script's own manager: its teardown is ours, lesson 23). Scope: only the edge-phase
|
||||
// unlisting is modelled; a proto vertex freed before the manager's destructor would be a
|
||||
// different use-after-free and is not flagged (no such path in source for these ops; the
|
||||
// pre-ready valgrind sample of the op-47 family covers it).
|
||||
static bool mgr_teardown_stale(SmProtoTopologyManager* mgr, bool unlink)
|
||||
{
|
||||
SmTArray<SmProtoVertex*>& pvs = mgr->GetProtoVertices();
|
||||
SmTArray<SmProtoEdge*>& pes = mgr->GetProtoEdges();
|
||||
std::map<SmProtoVertex*, std::vector<SmProtoEdge*>> lists;
|
||||
for (ULONG i = 0; i < pvs.GetSize(); i++)
|
||||
{
|
||||
if (pvs[i] == nullptr || lists.count(pvs[i]))
|
||||
continue;
|
||||
std::vector<SmProtoEdge*>& l = lists[pvs[i]];
|
||||
SmTArray<SmProtoEdge*>& src = pvs[i]->GetProtoEdges();
|
||||
for (ULONG j = 0; j < src.GetSize(); j++)
|
||||
l.push_back(src[j]);
|
||||
}
|
||||
auto unlist = [&](SmProtoVertex* v, SmProtoEdge* e) {
|
||||
auto it = lists.find(v);
|
||||
if (it == lists.end())
|
||||
return;
|
||||
auto f = std::find(it->second.begin(), it->second.end(), e);
|
||||
if (f != it->second.end())
|
||||
it->second.erase(f);
|
||||
};
|
||||
std::vector<SmProtoEdge*> freed;
|
||||
for (ULONG i = 0; i < pes.GetSize(); i++)
|
||||
{
|
||||
SmProtoEdge* e = pes[i];
|
||||
if (e == nullptr || std::find(freed.begin(), freed.end(), e) != freed.end())
|
||||
continue;
|
||||
freed.push_back(e);
|
||||
unlist(e->GetStartProtoVertex(), e);
|
||||
if (e->GetEndProtoVertex() != e->GetStartProtoVertex())
|
||||
unlist(e->GetEndProtoVertex(), e);
|
||||
}
|
||||
bool stale = false;
|
||||
for (auto& kv : lists)
|
||||
for (SmProtoEdge* e : kv.second)
|
||||
if (std::find(freed.begin(), freed.end(), e) != freed.end())
|
||||
{
|
||||
stale = true;
|
||||
if (unlink)
|
||||
kv.first->RemoveProtoEdge(e);
|
||||
}
|
||||
return stale;
|
||||
}
|
||||
|
||||
enum TiOp : int32_t
|
||||
{
|
||||
TI_EXPORT = 0,
|
||||
@@ -1751,11 +1696,7 @@ void run_script(Rd& rd, Two& t, std::vector<double>& o)
|
||||
}
|
||||
}
|
||||
delete ti; // the destructor frees the table (and its curves), maps and subset
|
||||
if (mgr)
|
||||
{
|
||||
mgr_teardown_stale(mgr, true);
|
||||
delete mgr;
|
||||
}
|
||||
delete mgr;
|
||||
}
|
||||
|
||||
// UB-166: the B-spline surface constructor's sm_FixupKnotVector can raise a v knot
|
||||
|
||||
@@ -221,8 +221,9 @@ bool set_rels_maps_ok(SmProtoTopologyManager* mgr, int64_t ctor_kind)
|
||||
return true;
|
||||
}
|
||||
|
||||
// ImprintIntersections (SmTopologyIntersector.cpp:2302-2413, upstream commit c9c5979)
|
||||
// VERBATIM, apart from the SM_DEBUG_CODE blocks and the members reached through accessors,
|
||||
// ImprintIntersections (SmTopologyIntersector.cpp:2302-2413, upstream commit c9c5979;
|
||||
// byte-identical in v1.0.1 3fd5b95 at the same lines) VERBATIM, apart from the SM_DEBUG_CODE
|
||||
// blocks and the members reached through accessors,
|
||||
// plus one addition: `edited` answers whether a failing imprint may have edited a B-rep,
|
||||
// as the port's ImprintOutcome::failure_edited does. A vertex definition's imprint and the
|
||||
// glue edit first; an edge definition's imprint edits nothing when it stops before its
|
||||
@@ -312,16 +313,17 @@ SmStatus ii_verbatim(SmTopologyIntersector* ti, bool& edited)
|
||||
return eRetStat;
|
||||
}
|
||||
|
||||
// IntersectInsertRelate (SmTopologyIntersector.cpp:1092-1270, upstream commit c9c5979)
|
||||
// VERBATIM, apart from the
|
||||
// IntersectInsertRelate (SmTopologyIntersector.cpp:1092-1270, upstream commit c9c5979;
|
||||
// byte-identical in v1.0.1 3fd5b95 at the same lines) VERBATIM, apart from the
|
||||
// SM_VALIDATE_TOPOLOGY, SM_DEBUG_CODE and SM_USE_TBB blocks (not defined in the reference
|
||||
// build) and the members reached through accessors, plus one addition: before the delete,
|
||||
// whether ~SmProtoTopologyManager would read a freed proto edge (UB-175); if so the stale
|
||||
// entries are unlinked first, so the run stays defined, and `ub` is set (the port answers
|
||||
// InvalidInput there). A second addition stops after a failed ImprintIntersections (UB-242)
|
||||
// whose failure may have edited a B-rep (ii_verbatim above), where upstream goes on with
|
||||
// half-imprinted B-reps: `ub` is set there too. The real IntersectInsertRelate is no longer exercised by this
|
||||
// difftest, so re-diff this copy against upstream whenever the pinned commit moves.
|
||||
// build) and the members reached through accessors, plus one addition: it stops after a
|
||||
// failed ImprintIntersections (UB-242) whose failure may have edited a B-rep (ii_verbatim
|
||||
// above), where upstream goes on with half-imprinted B-reps, and sets `ub` (the port
|
||||
// answers InvalidInput there). v1.0.1's ~SmProtoTopologyManager cuts the proto vertex /
|
||||
// proto edge links before deleting them, so the teardown is defined (UB-175 is a v1.0.0
|
||||
// defect) and the copy deletes the manager as upstream does. The real
|
||||
// IntersectInsertRelate is no longer exercised by this difftest, so re-diff this copy
|
||||
// against upstream whenever the pinned commit moves.
|
||||
SmStatus iir_verbatim(SmTopologyIntersector* ti, bool& ub)
|
||||
{
|
||||
SmProtoTopologyManager*& m_pProtoTopoMgr = ti->*ti_get(TiMgr());
|
||||
@@ -357,7 +359,7 @@ SmStatus iir_verbatim(SmTopologyIntersector* ti, bool& ub)
|
||||
// B-reps; stop here as the port does, deleting the manager as below, and answer -2)
|
||||
if (eStat != SM_SUCCESS && bEdited)
|
||||
{
|
||||
if(m_pProtoTopoMgr) { mgr_teardown_stale(m_pProtoTopoMgr, true); delete m_pProtoTopoMgr ; m_pProtoTopoMgr = NULL; }
|
||||
if(m_pProtoTopoMgr) { delete m_pProtoTopoMgr ; m_pProtoTopoMgr = NULL; }
|
||||
ub = true;
|
||||
return eStat;
|
||||
}
|
||||
@@ -366,7 +368,7 @@ SmStatus iir_verbatim(SmTopologyIntersector* ti, bool& ub)
|
||||
eStat = ti->SetRelationships();
|
||||
|
||||
// all done - clean up and exit
|
||||
if(m_pProtoTopoMgr) { ub = mgr_teardown_stale(m_pProtoTopoMgr, true); delete m_pProtoTopoMgr ; m_pProtoTopoMgr = NULL; }
|
||||
if(m_pProtoTopoMgr) { delete m_pProtoTopoMgr ; m_pProtoTopoMgr = NULL; }
|
||||
return eStat;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -112,12 +112,12 @@ SmExtent2d domain(const SmSurface& s, const double* f) {
|
||||
double u=n.GetUMin(),v=n.GetVMin(),du=n.GetUMax()-u,dv=n.GetVMax()-v;
|
||||
return SmExtent2d(u+du*f[0],v+dv*f[1],u+du*f[2],v+dv*f[3]);
|
||||
}
|
||||
// UB-154 (plan/upstream-issues.md): the file-static sm_GetEllipseTrimIntervals
|
||||
// (SmCone.cpp:5282) ignores the statuses of the smgu_LinePlaneIntersect calls on the
|
||||
// trimmed cone's two seam lines and then compares and uses the uninitialised dParam[0..1]
|
||||
// (:5399-5405, :5463-5477). It is reached only from inside IntersectWithCone and
|
||||
// IntersectWithPlane, with an ellipse they compute, so the shim replays the same calls in
|
||||
// the same order up to that point (seam_lines_defined: the head of
|
||||
// UB-154 (plan/upstream-issues/; SmCone.cpp line numbers here are v1.0.1's, 3fd5b95): the
|
||||
// file-static sm_GetEllipseTrimIntervals (SmCone.cpp:5315) ignores the statuses of the
|
||||
// smgu_LinePlaneIntersect calls on the trimmed cone's two seam lines and then compares and
|
||||
// uses the uninitialised dParam[0..1] (:5434-5439, :5505-5521). It is reached only from
|
||||
// inside IntersectWithCone and IntersectWithPlane, with an ellipse they compute, so the shim
|
||||
// replays the same calls in the same order up to that point (seam_lines_defined: the head of
|
||||
// sm_GetEllipseTrimIntervals; the *_ellipse_seams_undefined functions: the paths of the two
|
||||
// callers that reach it). Status-checked steps that make upstream return before the call
|
||||
// return false here too.
|
||||
@@ -149,8 +149,8 @@ bool seam_lines_defined(const SmCone& crCone, const SmExtent2d& crUVDomain,
|
||||
dParam[1]) == SM_SUCCESS;
|
||||
}
|
||||
|
||||
// SmCone::IntersectWithCone (SmCone.cpp:2604) up to its elliptical branch for two
|
||||
// cylinders of the same radius whose axes intersect (:3010-3137): four
|
||||
// SmCone::IntersectWithCone (SmCone.cpp:2605) up to its elliptical branch for two
|
||||
// cylinders of the same radius whose axes intersect (:3011-3138): four
|
||||
// sm_AddEllipseIntervalsTrimmedToTwoCones calls, each trimming one ellipse to this cone
|
||||
// and then to the other one. True when one of those eight trims reads dParam.
|
||||
bool cone_ellipse_seams_undefined(const SmCone& crThis, const SmExtent2d& crUVDomain,
|
||||
@@ -232,9 +232,9 @@ bool cone_ellipse_seams_undefined(const SmCone& crThis, const SmExtent2d& crUVDo
|
||||
return trims_undefined();
|
||||
}
|
||||
|
||||
// SmCone::IntersectWithPlane (SmCone.cpp:3219) up to its elliptical branch (:3664-3834),
|
||||
// SmCone::IntersectWithPlane (SmCone.cpp:3220) up to its elliptical branch (:3665-3835),
|
||||
// which trims the ellipse to the cone's natural domain. True when that trim reads dParam.
|
||||
// No known input reaches it: the generator lines tested at :3716 and :3735 are as long
|
||||
// No known input reaches it: the generator lines tested at :3717 and :3736 are as long
|
||||
// as the seams and one of them is the least steep to the plane, so (up to rounding) they
|
||||
// fail first and return. Kept so the guard follows every path into
|
||||
// sm_GetEllipseTrimIntervals.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# topology.proto_topology_p3: the UB-241 stop in GlueCoincidentBrepVertices was broader than the defect (2026-10-29)
|
||||
|
||||
**Unit:** `topology.proto_topology_p3` (`ProtoTopologyManager::glue_coincident_brep_vertices`),
|
||||
found through `boolean.api_brep` case `crossing_creases_union_d1e-05`. Until the v1.0.1 pin the
|
||||
case was kept from the C++ side by the `boolean.api_brep` UB-175 guard.
|
||||
|
||||
**Values.** Both sides succeed; C++ returns 12 faces, Rust 13 (`values.measures.len`: cpp=15
|
||||
rust=16).
|
||||
|
||||
**Cause.** The UB-241 stop (SolidCraft fix, tangency-labs/smlib da8d05e) was broader than the
|
||||
defect: the defect is on the `GlueVertices` path; the delete branch is safe, so the port now
|
||||
stops only before `GlueVertices`. With the broader stop the port skipped edge deletes that C++
|
||||
makes, and one extra edge was related and kept, splitting a face. An ASan run of this case on
|
||||
v1.0.1 is clean.
|
||||
|
||||
**Fix.** Unit test `glue_deletes_a_second_short_common_edge_after_the_squeeze`;
|
||||
`glue_stops_once_the_second_vertex_is_glued_away` is unchanged.
|
||||
@@ -0,0 +1,19 @@
|
||||
Updates: UB-154
|
||||
|
||||
#### UB-154 (addendum). Still present in v1.0.1 (3fd5b95)
|
||||
- **Severity:** Crash / undefined behaviour
|
||||
- **Area:** Surfaces and volumes
|
||||
- **Location:** `source/SMLib/src/SmCone.cpp:5434`-`5439` and `:5505`-`5521` (v1.0.1),
|
||||
`sm_GetEllipseTrimIntervals` (def. `:5315`, file static).
|
||||
- **Class:** uninitialised read: the statuses of the two seam-line `smgu_LinePlaneIntersect`
|
||||
calls are still ignored, and `dParam[0]`, `dParam[1]` are still compared and used when a call
|
||||
fails.
|
||||
- **Change in v1.0.1:** the function now takes its trim circles from the trim domain's v limits
|
||||
and accepts seam-line crossings within a tolerance scaled by each seam's length
|
||||
(`dStartParamTol`, `dEndParamTol`) instead of `dScaledZero`; `GlobalSurfaceIntersect` also
|
||||
sends cylinders to `IntersectWithCone`. None of this checks the two statuses.
|
||||
- **How checked:** read from the v1.0.1 source. The `surface.cone_p3` shim's replay of the
|
||||
function's head (STEP domain, corners, seam vectors, the two intersection statuses) was compared
|
||||
line by line with v1.0.1 and is unchanged, so its guard still refuses exactly the inputs that
|
||||
reach the read.
|
||||
- **Status:** present in v1.0.1; the difftest guard stays.
|
||||
@@ -0,0 +1,22 @@
|
||||
Updates: UB-175
|
||||
|
||||
#### UB-175 (addendum). Not reproduced on v1.0.1 under AddressSanitizer
|
||||
- **Severity:** Crash / undefined behaviour
|
||||
- **Area:** Intersection
|
||||
- **Location:** `source/SMLib/src/SmProtoTopology.cpp`, `SmProtoTopologyManager::~SmProtoTopologyManager`
|
||||
(v1.0.1 clears every proto edge's end vertices and every proto vertex's edge list before the
|
||||
teardown deletes them).
|
||||
- **Change:** with the reference pinned to v1.0.1 (3fd5b95), the `boolean.api_brep` runner no longer
|
||||
keeps the inputs that reached this use-after-free on v1.0.0 from the C++ side; they now run the
|
||||
real SmApi Boolean and its proto topology teardown, and are compared like any other case. Its
|
||||
guard now rejects only the inputs that reach UB-242 (a failed imprint that may have edited a
|
||||
B-rep, where the port stops and upstream goes on); on v1.0.0 those also reached this teardown. The
|
||||
`boolean.topology_intersector_p1`/`_p2` shim no longer unlinks stale proto edge entries before
|
||||
deleting a proto topology manager, and its runner no longer answers `-2` for an
|
||||
`IntersectInsertRelate` the port records UB-175 for (the UB-242 stop is unchanged).
|
||||
- **How checked:** SMLib, SM_API and the difftest shims were rebuilt at 3fd5b95 with AddressSanitizer
|
||||
(the flags and defines of `ffi.env` otherwise unchanged). A default `boolean.api_brep` run and a
|
||||
4000-seed run, which include every input the earlier addenda found reaching the teardown, ran
|
||||
with no sanitizer report.
|
||||
- **Status:** not reproduced on v1.0.1; the difftest guard for it is removed. The record stays: it describes
|
||||
a defect of v1.0.0.
|
||||
Reference in New Issue
Block a user