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 <noreply@anthropic.com>
This commit is contained in:
Brett Lawson
2026-10-22 12:09:10 -07:00
parent d1bc8204cf
commit d725baf2c8
2 changed files with 6 additions and 2 deletions
@@ -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}}
]
}
@@ -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<Vec<f64>, 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()?;