Files
MarkusRe 6a3cc682fa Mirror: a mirrored body's straight edges stay lines, so booleans with it no longer take the wrong side (#15862) (#16104)
* 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>
2026-11-13 18:24:03 +00:00
..

Upstream SMLib issues (one file each)

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-ffi surface_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-ffi primitive_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-ffi intersect_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.