* Claim #15862: interference and booleans of a loft against a mirrored box
Co-Authored-By: Claude Code <noreply@anthropic.com>
* solidcore-ops: test a box mirrored into an opening of a spline loft (#15862)
A box extruded from a rectangle, read back from its record and mirrored
across x = 0 sits 0.2 mm clear of a loft through two cubic splines. On main
the loft minus the box is nothing, the loft and the box intersect in the
whole loft, and their union is refused, because the mirror turns every
straight edge into the degree 1 B-spline of the whole line.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* solidcore: a mirrored line stays a line (#15862)
Mirroring a body (SmBrep::Mirror, and SmBrep::Transform with a left-handed
matrix) turned each straight edge into the plain degree 1 B-spline of the
line's NURB. A line read from a record is infinite, so that B-spline ran over
knots -1234567..1234567 with control points some 1.8e7 mm out. A boolean of
such a body with a spline face it does not touch took the wrong side: a box
mirrored into an opening of a spline loft, 0.2 mm clear, cut the whole loft
away, intersected it in the whole loft, and its union was refused (1001).
Line::create_mirror_curve mirrors the line's point and unit vector and keeps
its domain, scale and sides; an existing NURB is mirrored with it, so each
parameter maps to the mirror of the same point and edge intervals stay valid.
Recorded as WR-784.
Golden topology.brep_mirror blessed: 147 of 341 cases move, none in status,
topology (graph) or the geometry digest.
- 133 mirror and mirror-matrix cases: each straight edge's curve is now a line;
every record block drops by a whole multiple of 23 (the B-spline type code
minus the line's), e.g. Solids_box_smb_* by 12 lines, the cylinder's seam by 1
(gen0017/0030/0033/0036/0077, mat0027/0045 within 1e-9 besides: the old
samples came from control points near 1e7).
- 14 matrix cases with a reflection and an unequal scale (box, cylinder and cone
mirror_rotated_scaled; mat0003/0004/0009/0017/0029/0036/0049/0050/0056/0057/
0090): the scale turns the edges into NURBS as before, now over the edge's own
interval instead of the whole line, so the record is shorter and the uses keep
no stored UV trims, exactly as main gives for an unequal scale without a
reflection (Solids_box_smb_non_uniform_2_1_1). Measured on box, cylinder, cone, extrusion, l_shape_with_boss
and OffsetBoxCylOnCorner with that matrix: valid at CheckLevel::All and the same
volume to the last bit on main and with this change.
Co-Authored-By: Claude Code <noreply@anthropic.com>
---------
Co-authored-by: Claude Code <noreply@anthropic.com>
One file per upstream C++ defect, so parallel PRs never conflict. Never append to a
shared file. The rules, fields, severity order and numbering are below (from the former
archive). The site (site/build.py) renders every file here, in name order.
Name:<ID>--<short-slug>.md, e.g. WR-262--extrusion-drop-point-fast.md. The ID
takes the next free number in its series (UB-, HANG-, ND-, WR-, DOC-) across
this directory. If two PRs pick the same number, whoever merges second renumbers theirs
(the site flags duplicates).
Content:
#### WR-262. One-line title [PSIRT if file input reaches memory corruption]
- **Severity:** Wrong result
- **Area:** Surfaces and volumes
- **Location:** `source/SMLib/src/File.cpp:123`, `Function` (def. `:100`).
- **What happens:** ...
- **Trigger:** ...
- **How found:** generated difftest case
- **Confirmed:** yes, case `gen0042` of `unit.id`
Severity is one of Crash / undefined behaviour, Hang, Nondeterminism,
Wrong result, Docs. Area is free text, ideally one of the areas already used
here (grep -rh '^- \*\*Area:' plan/upstream-issues/ | sort | uniq -c).
Updates (now confirmed, fixed upstream, renumbered, withdrawn) are addenda, not
edits: a new file <ID>--addendum-<YYYY-MM-DD>-<slug>.md starting with
Updates: <ID> and what changed. Records are never edited after they merge.
Older references to plan/upstream-issues.md IDs (and plan/findings.md: <heading>): grep the heading or ID under this directory
(grep -rl '^#### UB-35\.' plan/upstream-issues/).
History
Until 2026-10-25 the issues were #### entries of one shared file,
plan/upstream-issues.md, grouped under ## <severity> and ### <area> headings. On
2026-10-25 scripts/split_archives.py split it into one file per entry here and deleted
it; python3 -I scripts/split_archives.py check proves the split lossless. Each file
keeps the #### line verbatim, adds - **Severity:** and - **Area:** from the
headings it stood under, then the entry verbatim. Entries filed after the ## Docs
section, under the two headings that were not severities, take the severity of their ID
series and an empty area (or the ### heading under them). The second entry for an ID
already used has a --dup2 name suffix; its heading is unchanged. Entries without a
numeric ID are named UB-new--, UB-unnumbered--, ISSUE-489-- and UNNUMBERED--. A
hang report that had a ## heading and no #### entry is
HANG-unnumbered--createlinearsweep-negative-full-circle-distance-does-not.md (not
rendered on the site, as before). The archive's text outside the entries follows
verbatim, except its "Frozen archive" notice; "here" in it means the archive.
Defects and hazards in the upstream C++ (source/SMLib, source/SM_API) found while
porting it to Rust. Problems only: porting notes, harness details and Rust-side choices
stay in plan/findings.md. Behaviour that is odd but plausibly intended is left out.
Upstream commit:c9c5979 (NVIDIA usd-brep). Paths are relative to the upstream
root. Line numbers were checked against that commit.
Security. Entries marked [PSIRT] are crafted-file memory or stack exhaustion,
or out-of-bounds or uninitialised reads that file input can reach. Per upstream
SECURITY.md, report these to NVIDIA PSIRT (psirt@nvidia.com or the web form), not as
public GitHub issues. The other out-of-bounds entries need a bad argument from the
calling code. Whoever files them should still decide whether they count as security
issues.
Fields.
How found: "read during porting" means source reading only. The other values are
"generated difftest case", "adversarial / edge-case fixture", or "crash in the C++
reference run" (the difftest harness linking the upstream build).
Confirmed:yes only when a C++ run showed the behaviour: a crash, or a named
difftest case or fixture that compares against it. Otherwise no. In most "no"
cases the harness deliberately keeps that input away from the C++ side.
Build. The reference build is the upstream release configuration (-O2, no
SM_DEBUG_CODE, SM_USE_OLDTOL) with -ffp-contract=off, on x86-64 Linux / LP64.
Severity order: crash/UB, hang, nondeterminism, wrong result, docs. Entries are grouped
by area within each severity, so IDs are in order within each area but not across a
whole severity.
Numbering. A new entry takes the next free number in its series (UB-, HANG-,
ND-, WR-, DOC-) at the moment its PR merges. If another PR took that number in the
meantime, whoever merges renumbers the new entry and every reference to it (here, in
plan/findings.md, plan/modules.toml and code comments). Numbers are not reused.
Note: duplicate IDs (2026-10-23)
Parallel PRs gave six IDs to more than one entry. They are not renumbered, because code
comments, shims, unit notes, findings and PRs already cite them; when citing one of these
IDs, add the function name so the entry is unambiguous. Each entry, by its section and
title (other references in the repo in brackets):
UB-100: (a) Surfaces and volumes, SmDerivSurfDefinition::GetSampleValues missing
tangent reference [solidcore-ffisurface_surface_p6.rs, surface.surface_p6
notes, findings]; (b) Intersection, SmPolyBrep::CalculateBoundingBox null point array
for a pseudo box.
UB-185: (a) Topology, SmFilletVertex::CalcMateGeom unbounded recursion through a
cycle of mates [blend.fillet_geom_p3 notes, MAX_MATE_DEPTH]; (b) Numerical
integration (near the end of the file), fillet executive m_bIsInitialized
indeterminate before parameter setup [blend_fillet_executive_p2/p3 shims and FFI,
findings].
UB-204: (a) NLib: matrices and polynomials, SmMatrix(rows, cols) uninitialised
inline storage used by SmLocalSolveNd [difftest curve_curve_p9.rs, findings
(#94)]; (b) Surfaces and volumes, SmFilletSolver::GetSurface unbounded recursion
through the geometry set [findings, blend.fillet_solver_p1 pseudo B-rep entry].
UB-210: (a) Topology, SmBrep::DeleteEdge reads pSurvF outside the merge branch
[topology/brep/p4.rs, topology.brep_p4 notes]; (b) Topology,
SmFilletEdge::CalcFilletIntFillet reads the context of a missing fillet corner
[blend_fillet_geom_p3 shim, runner and fixtures, blend.fillet_geom_p3 notes].
UB-238 (four entries): (a) NLib: knots and basis functions,
SmBSplineCurve::CreateCanonical heap corruption for a NaN knot [solidcore-ffiprimitive_api_curves.rs, fuzz corpus]; (b) Spatial structures and small geometry,
vertex/vertex and edge/edge topology solvers dereference NULL in a release
SM_ASSERT [solidcore-ffiintersect_topology_solver_p1.rs]; (c) Curves,
SetIntersectionCurveThroughPoint builds UV trim curves from the address of a pointer
slot; (d) Topology, SmFilletGeom::MakeRailEdgeuse leaves a deleted vertex use linked
in the fillet vertex [blend_fillet_geom_p5 shim and FFI, blend.fillet_geom_p5
notes, findings].
WR-190: (a) Topology, attributes, solvers, SmFilletSolver::RecordFilletError
reports rail 0 for rail 1 [blend/fillet_solver/p2.rs]; (b) Curves,
SmFilletEdge::CalcBlendingRail builds its Hermite fallback as a degree-2 curve
[blend/fillet_geom/p6.rs].
New entries take the next free number after the highest in their series, as the
Numbering paragraph at the top says.