forked from 1b-agent/opencadstudio-upstream
Fix unbounded MINSERT allocation and scale-multiplied spacing
Two independent MINSERT (array INSERT) bugs found while auditing render_graph.rs: 1. array_offsets() reserved and filled one Vec element per array instance, with row_count/column_count parsed as unchecked u16s. A corrupt or adversarial 65535x65535 pair requests 4,294,836,225 instances (~96 GiB) before any GPU-side chunking limit can help, and before the render graph even reaches the GPU. Fixed with a documented instance budget (20,000) enforced at both the load-time corrupt-entity filter (is_entity_corrupt, matching the existing polyline/spline vertex-count guards) and a failsafe clamp in array_offsets itself, for an insert built or edited some other way. 2. insert_instance_transform composed the per-instance row/column offset translation *before* the INSERT's own transform, so the block's x/y scale multiplied the spacing along with the block contents — an INSERT with x-scale 2 and column spacing 10 rendered 20 units between columns instead of 10. The pinned CAD model's own Insert::array_points() confirms the correct behavior: spacing rotates with the insert but is never scaled. Fixed by orienting the offset with rotation + OCS only, then translating the already- composed (scaled) instance transform by that world-space vector. 7 new tests cover both: pathological/reasonable instance counts at the load-time filter and the render-graph failsafe, and instance transforms for scaled and rotated arrays. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
4ddd19f6e8
commit
8bc9e83c2e
2 files changed
+160
-4
No files matched your search
@@ -2035,7 +2035,17 @@ pub(crate) fn is_entity_corrupt(e: &EntityType) -> bool {
|
||||
// rarely use this many — and parser desync produces exactly-100_000-vertex
|
||||
// junk records.
|
||||
const MAX_VERTS: usize = 100_000;
|
||||
// A MINSERT's row/column counts are parsed `u16`s whose unchecked product
|
||||
// (`Insert::instance_count`) drives a per-instance allocation in
|
||||
// `render_graph::array_offsets` and a full per-instance render-graph walk
|
||||
// for every one — a corrupt or adversarial 65535x65535 pair requests over
|
||||
// four billion instances (tens of GiB) long before any GPU-side chunking
|
||||
// limit could help. Kept in sync with the identical failsafe clamp in
|
||||
// `render_graph::array_offsets`, which guards inserts that reach the
|
||||
// render graph some other way (e.g. built at runtime, not loaded).
|
||||
const MAX_MINSERT_INSTANCES: usize = 20_000;
|
||||
match e {
|
||||
E::Insert(i) => i.instance_count() > MAX_MINSERT_INSTANCES,
|
||||
E::LwPolyline(p) => {
|
||||
!finite_unit_normal(&p.normal)
|
||||
|| p.vertices.len() >= MAX_VERTS
|
||||
@@ -2410,4 +2420,33 @@ mod corrupt_guard_tests {
|
||||
let s = Spline::from_control_points(3, pts);
|
||||
assert!(!is_entity_corrupt(&EntityType::Spline(s)));
|
||||
}
|
||||
|
||||
// A corrupt or adversarial MINSERT row/column pair (u16, so its unchecked
|
||||
// product can reach into the billions) must be rejected before it can
|
||||
// drive the render graph's per-instance allocation and expansion.
|
||||
#[test]
|
||||
fn rejects_pathological_minsert_counts() {
|
||||
let mut i = acadrust::entities::Insert::new("BLOCK", Vector3::ZERO);
|
||||
i.row_count = u16::MAX;
|
||||
i.column_count = u16::MAX;
|
||||
assert!(is_entity_corrupt(&EntityType::Insert(i)));
|
||||
}
|
||||
|
||||
// An ordinary array insert, well under the budget, is valid source data.
|
||||
#[test]
|
||||
fn keeps_a_reasonable_minsert() {
|
||||
let mut i = acadrust::entities::Insert::new("BLOCK", Vector3::ZERO);
|
||||
i.row_count = 10;
|
||||
i.column_count = 10;
|
||||
i.row_spacing = 5.0;
|
||||
i.column_spacing = 5.0;
|
||||
assert!(!is_entity_corrupt(&EntityType::Insert(i)));
|
||||
}
|
||||
|
||||
// A plain (non-array) INSERT is never treated as a MINSERT-count problem.
|
||||
#[test]
|
||||
fn keeps_a_plain_insert() {
|
||||
let i = acadrust::entities::Insert::new("BLOCK", Vector3::ZERO);
|
||||
assert!(!is_entity_corrupt(&EntityType::Insert(i)));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user