From d725baf2c83a7388fdcc87dd1d25be8563a3e908 Mon Sep 17 00:00:00 2001 From: Brett Lawson Date: Thu, 22 Oct 2026 12:09:10 -0700 Subject: [PATCH] topology.vertexuse FFI: refuse B-rep blocks above 1e150 (HANG-29) As the edgeuse_p2/p3, loopuse and face_p3 wrappers do: building such a face never returns upstream (CreateUVTrimCurve -> GlobalCoincidenceChecker). The Rust side imports the same export, so both sides now give the same rejection; the two fuzz corpus cases that skipped the hanging C++ now run it and expect that rejection on both sides. Co-Authored-By: Claude Opus 5.5 --- crates/difftest/tests/fuzz_corpus/topology.vertexuse.json | 4 ++-- crates/solidcore-ffi/src/units/topology_vertexuse.rs | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/difftest/tests/fuzz_corpus/topology.vertexuse.json b/crates/difftest/tests/fuzz_corpus/topology.vertexuse.json index 4964790232..9de4aa33a5 100644 --- a/crates/difftest/tests/fuzz_corpus/topology.vertexuse.json +++ b/crates/difftest/tests/fuzz_corpus/topology.vertexuse.json @@ -1,7 +1,7 @@ { "unit": "topology.vertexuse", "cases": [ - {"name": "fuzz01312_cpp_hang", "origin": "fuzz-out/n2000/topology.vertexuse/fuzz01312.json (timeout: cpp: no result after 20 s)", "issue": "HANG-29", "cpp_skip": "the C++ reference still hangs (no result in 20 s) on this input (main 4bb0f022, 2026-10-22)", "rust": "deferred", "inputs": {"brep": {"dom": [0, 0, 1, 1], "kind": "face", "surface": {"bspline": {"net": [[[0.0, 0.0, -400.0, -1.7976931348623157e+308], [0.0, 1e+300, 200.0, -1.7976931348623157e+308], [0.0, 0.0, -200.0, -1.7976931348623157e+308], [0.0, 0.0, 400.0, -1.7976931348623157e+308]], [[1000.0, 0.0, 0.0, -1.7976931348623157e+308], [1000.0, 1000.0, -400.0, -1.7976931348623157e+308], [1000.0, 2000.0, 200.0, -1.7976931348623157e+308], [1000.0, 3000.0, -200.0, -1.7976931348623157e+308]], [[2000.0, 0.0, 400.0, -1.7976931348623157e+308], [2000.0, 1000.0, 0.0, -1.7976931348623157e+308], [2000.0, 2000.0, -400.0, -1.7976931348623157e+308], [2000.0, 3000.0, 200.0, -1.7976931348623157e+308]], [[3000.0, 0.0, -200.0, -1.7976931348623157e+308], [3000.0, 1000.0, 400.0, -1.7976931348623157e+308], [3000.0, 2000.0, 0.0, -1.7976931348623157e+308], [3000.0, 3000.0, -400.0, -1.7976931348623157e+308]]], "p": 3, "q": 3, "u": [0, 0, 0, 0, 1, 1, 1, 1], "v": [0, 0, 0, 0, 1, 1, 1, 1]}}}, "init_gaps": true, "ok_to_make": true, "op": "uv_sector", "vu": 0}}, - {"name": "fuzz01688_cpp_hang", "origin": "fuzz-out/n2000/topology.vertexuse/fuzz01688.json (timeout: cpp: no result after 20 s)", "issue": "HANG-29", "cpp_skip": "the C++ reference still hangs (no result in 20 s) on this input (main 4bb0f022, 2026-10-22)", "rust": "deferred", "inputs": {"brep": {"dom": [0.5, 0.0, 0.5000001, 1.0], "kind": "face", "surface": {"bspline": {"form": 0, "net": [[[0.0, 0.0, 0.0, -1.7976931348623157e+308], [0.0, 1.0, 0.1, -1.7976931348623157e+308], [0.0, 2.0, 0.0, -1.7976931348623157e+308]], [[1.0, 0.0, 0.1, -1.7976931348623157e+308], [1.0, 1.0, 0.0, -1.7976931348623157e+308], [1.0, 2.0, 0.1, -1.7976931348623157e+308]], [[2.0, 0.0, 0.0, -1.7976931348623157e+308], [2.0, -1e+300, 0.1, -1.7976931348623157e+308], [2.0, 2.0, 0.0, -1.7976931348623157e+308]]], "oob": false, "p": 2, "q": 2, "u": [0, 0, 0, 1, 1, 1], "v": [0, 0, 0, 1, 1, 1]}}}, "init_gaps": false, "op": "missing_seam", "uv_ok": true, "vu": 2, "want_u": true, "want_v": false}} + {"name": "fuzz01312_cpp_hang", "origin": "fuzz-out/n2000/topology.vertexuse/fuzz01312.json (timeout: cpp: no result after 20 s)", "issue": "HANG-29", "cpp": "harness: c++ export: invalid argument: B-rep value above 1e150 (HANG-29)", "rust": "harness: c++ export: invalid argument: B-rep value above 1e150 (HANG-29)", "inputs": {"brep": {"dom": [0, 0, 1, 1], "kind": "face", "surface": {"bspline": {"net": [[[0.0, 0.0, -400.0, -1.7976931348623157e+308], [0.0, 1e+300, 200.0, -1.7976931348623157e+308], [0.0, 0.0, -200.0, -1.7976931348623157e+308], [0.0, 0.0, 400.0, -1.7976931348623157e+308]], [[1000.0, 0.0, 0.0, -1.7976931348623157e+308], [1000.0, 1000.0, -400.0, -1.7976931348623157e+308], [1000.0, 2000.0, 200.0, -1.7976931348623157e+308], [1000.0, 3000.0, -200.0, -1.7976931348623157e+308]], [[2000.0, 0.0, 400.0, -1.7976931348623157e+308], [2000.0, 1000.0, 0.0, -1.7976931348623157e+308], [2000.0, 2000.0, -400.0, -1.7976931348623157e+308], [2000.0, 3000.0, 200.0, -1.7976931348623157e+308]], [[3000.0, 0.0, -200.0, -1.7976931348623157e+308], [3000.0, 1000.0, 400.0, -1.7976931348623157e+308], [3000.0, 2000.0, 0.0, -1.7976931348623157e+308], [3000.0, 3000.0, -400.0, -1.7976931348623157e+308]]], "p": 3, "q": 3, "u": [0, 0, 0, 0, 1, 1, 1, 1], "v": [0, 0, 0, 0, 1, 1, 1, 1]}}}, "init_gaps": true, "ok_to_make": true, "op": "uv_sector", "vu": 0}}, + {"name": "fuzz01688_cpp_hang", "origin": "fuzz-out/n2000/topology.vertexuse/fuzz01688.json (timeout: cpp: no result after 20 s)", "issue": "HANG-29", "cpp": "harness: c++ export: invalid argument: B-rep value above 1e150 (HANG-29)", "rust": "harness: c++ export: invalid argument: B-rep value above 1e150 (HANG-29)", "inputs": {"brep": {"dom": [0.5, 0.0, 0.5000001, 1.0], "kind": "face", "surface": {"bspline": {"form": 0, "net": [[[0.0, 0.0, 0.0, -1.7976931348623157e+308], [0.0, 1.0, 0.1, -1.7976931348623157e+308], [0.0, 2.0, 0.0, -1.7976931348623157e+308]], [[1.0, 0.0, 0.1, -1.7976931348623157e+308], [1.0, 1.0, 0.0, -1.7976931348623157e+308], [1.0, 2.0, 0.1, -1.7976931348623157e+308]], [[2.0, 0.0, 0.0, -1.7976931348623157e+308], [2.0, -1e+300, 0.1, -1.7976931348623157e+308], [2.0, 2.0, 0.0, -1.7976931348623157e+308]]], "oob": false, "p": 2, "q": 2, "u": [0, 0, 0, 1, 1, 1], "v": [0, 0, 0, 1, 1, 1]}}}, "init_gaps": false, "op": "missing_seam", "uv_ok": true, "vu": 2, "want_u": true, "want_v": false}} ] } diff --git a/crates/solidcore-ffi/src/units/topology_vertexuse.rs b/crates/solidcore-ffi/src/units/topology_vertexuse.rs index 414dd5f2b3..9525772a22 100644 --- a/crates/solidcore-ffi/src/units/topology_vertexuse.rs +++ b/crates/solidcore-ffi/src/units/topology_vertexuse.rs @@ -40,9 +40,12 @@ //! [`eval`] accepts only what upstream handles without undefined behaviour: existing //! `.smb` files, well-formed clamped B-splines, planes with a non-zero normal, indices //! inside the B-rep's export (it builds the B-rep once more to count), finite inputs. +//! Blocks with a value above [`super::topology_edgeuse_p2::MAX_BLOCK_MAGNITUDE`] are +//! refused first (HANG-29: building the face never returns upstream). use std::path::Path; +use super::topology_edgeuse_p2::check_block_magnitudes; use crate::validate::{bad, check_knots, NOW, NOZ}; use crate::{call_with_buffer, FfiError}; @@ -438,6 +441,7 @@ pub fn eval(op: Op, path: &str, input: &[f64]) -> Result, FfiError> { let mut rd = Reader::new(input); brep_block(&mut rd, path)?; let block = input.get(..rd.at).ok_or_else(|| bad("input too short"))?; + check_block_magnitudes(block)?; let export = raw(Op::Export, path, block)?; if op == Op::Export { rd.done()?;