--- a/src/io/dwg/dwg_stream_readers/object_reader/entities.rs +++ b/src/io/dwg/dwg_stream_readers/object_reader/entities.rs @@ -1682,20 +1682,49 @@ MLineData { scale_factor, justification, start_point, normal, openclosed, lines_in_style, vertex_count, vertices, style_handle } } +/// Cap an array count against the bits actually left in the object's main +/// stream instead of the blanket [`MAX_ARRAY_COUNT`]. +/// +/// MESH is the one entity whose arrays legitimately run past 100 000 items: a +/// road-surface mesh in a civil drawing carries hundreds of thousands of +/// vertices. Clamping those to the blanket cap does not merely truncate the +/// mesh — the reader then continues from the wrong bit offset, so the face, +/// edge and crease lists that follow decode as garbage (observed: a 100 000 +/// vertex clamp left a 14-face mesh with face sizes `[64, 0, 64, 23, 0, …]`). +/// Bounding by the remaining bits keeps the corrupt-data protection — no +/// unbounded allocation is possible — without mis-parsing a valid drawing. +/// +/// `min_bits_per_item` is the shortest legal encoding of one element: a +/// BitDouble is 2 bits at minimum (the 0.0 / 1.0 forms), so a 3BD vertex is 6 +/// and a BitLong is 2. +fn stream_bounded_count(reader: &DwgMergedReader, raw: i32, min_bits_per_item: i64) -> i32 { + let raw = raw.max(0) as i64; + let total_bits = (reader.main().data_len() as i64).saturating_mul(8); + // The handle stream follows the main stream; anything past its start is not + // array data. handle_start() is 0 when the framing did not supply it. + let handle_start = reader.handle_start(); + let end_bits = if handle_start > 0 { handle_start.min(total_bits) } else { total_bits }; + let remaining = (end_bits - reader.position_in_bits()).max(0); + raw.min(remaining / min_bits_per_item.max(1)).min(i32::MAX as i64) as i32 +} + pub fn read_mesh(reader: &mut DwgMergedReader) -> MeshData { let version = reader.read_bit_short(); let blend_crease = reader.read_bit(); let subdivision_level = reader.read_bit_long(); - let num_verts = safe_count(reader.read_bit_long()); + let raw_verts = reader.read_bit_long(); + let num_verts = stream_bounded_count(reader, raw_verts, 6); let mut vertices = Vec::with_capacity(num_verts as usize); for _ in 0..num_verts { vertices.push(reader.read_3bit_double()); } - let total_face_data = safe_count(reader.read_bit_long()); + let raw_face_data = reader.read_bit_long(); + let total_face_data = stream_bounded_count(reader, raw_face_data, 2); let mut faces = Vec::new(); let mut i = 0; while i < total_face_data { - let n = safe_count(reader.read_bit_long()); + let raw_n = reader.read_bit_long(); + let n = stream_bounded_count(reader, raw_n, 2); i += 1; let mut face = Vec::new(); for _ in 0..n { @@ -1705,7 +1734,8 @@ faces.push(face); } - let num_edges = safe_count(reader.read_bit_long()); + let raw_edges = reader.read_bit_long(); + let num_edges = stream_bounded_count(reader, raw_edges, 4); let mut edges = Vec::with_capacity(num_edges as usize); for _ in 0..num_edges { let s = reader.read_bit_long(); @@ -1713,7 +1743,8 @@ edges.push((s, e)); } - let num_creases = safe_count(reader.read_bit_long()); + let raw_creases = reader.read_bit_long(); + let num_creases = stream_bounded_count(reader, raw_creases, 2); let mut crease_values = Vec::with_capacity(num_creases as usize); for _ in 0..num_creases { crease_values.push(reader.read_bit_double()); } @@ -2350,14 +2381,31 @@ pub fn read_ole2frame(reader: &mut DwgMergedReader, version: DwgVersion) -> Ole2FrameData { let ver = reader.read_bit_short(); let mode = if version.r2000_plus() { reader.read_bit_short() } else { 0 }; - // OLE binary data can be very large (embedded images/documents), - // so don't use safe_count (100 KB cap). Use a generous 10 MB cap instead. let data_len = (reader.read_bit_long().max(0) as usize).min(10_000_000); let data = reader.read_bytes(data_len); let trailing_byte = if version.r2000_plus() { reader.read_byte() } else { 3 }; - let upper_left = reader.read_3bit_double(); - let lower_right = reader.read_3bit_double(); + + let mut upper_left = Vector3::new(0.0, 0.0, 0.0); + let mut lower_right = Vector3::new(0.0, 0.0, 0.0); + + // Extract 4-corner coordinates stored in the OLE binary data header starting at offset 2 + if data.len() >= 66 { + let ul_x = f64::from_le_bytes(data[2..10].try_into().unwrap()); + let ul_y = f64::from_le_bytes(data[10..18].try_into().unwrap()); + let ul_z = f64::from_le_bytes(data[18..26].try_into().unwrap()); + let lr_x = f64::from_le_bytes(data[50..58].try_into().unwrap()); + let lr_y = f64::from_le_bytes(data[58..66].try_into().unwrap()); + let lr_z = if data.len() >= 74 { f64::from_le_bytes(data[66..74].try_into().unwrap()) } else { 0.0 }; + + if ul_x.is_finite() && ul_y.is_finite() && lr_x.is_finite() && lr_y.is_finite() + && (ul_x != 0.0 || ul_y != 0.0 || lr_x != 0.0 || lr_y != 0.0) { + upper_left = Vector3::new(ul_x, ul_y, if ul_z.is_finite() { ul_z } else { 0.0 }); + lower_right = Vector3::new(lr_x, lr_y, if lr_z.is_finite() { lr_z } else { 0.0 }); + } + } + let ole_type = if version.r2000_plus() { reader.read_byte() } else { 2 }; + Ole2FrameData { version: ver, mode, data, trailing_byte, upper_left, lower_right, ole_type } }