diff --git a/src/parser/hwpx/header.rs b/src/parser/hwpx/header.rs index b83b733331..2a55359817 100644 --- a/src/parser/hwpx/header.rs +++ b/src/parser/hwpx/header.rs @@ -1285,7 +1285,9 @@ fn parse_para_shape_switch( // HWP3 암호 원본의 별도 spacing 계약은 HWP3 parser // 안에서만 처리한다. HWPX 전체에 반감 적용하면 // 일반 HWPX 문단 흐름과 기준 HWP3 변환본이 함께 밀린다. - let val2x = val * 2; + // 손상 문서의 극단 value 는 i32 곱셈 오버플로 패닉을 + // 유발하므로 saturating 으로 막는다(정상값은 무영향). + let val2x = val.saturating_mul(2); match tag_name { b"left" => { ps.margin_left = val2x; @@ -1354,7 +1356,8 @@ fn parse_para_shape_switch( let effective_type = ls_type.unwrap_or(ps.line_spacing_type); ps.line_spacing = match effective_type { LineSpacingType::Percent => v, - _ => v * 2, + // 손상 value 의 i32 곱셈 오버플로 패닉 차단. + _ => v.saturating_mul(2), }; case_line_spacing = Some(v); } @@ -1903,8 +1906,9 @@ fn parse_tab_def( let mut item = parse_tab_item(ce); if in_hwpunitchar_case { // HwpUnitChar 값은 실제 HWPUNIT(1× 스케일)이므로 - // HWP 바이너리와 동일한 2× 스케일로 변환 - item.position *= 2; + // HWP 바이너리와 동일한 2× 스케일로 변환. + // 손상 pos 의 u32 곱셈 오버플로 패닉 차단(정상값은 무영향). + item.position = item.position.saturating_mul(2); td.tabs.push(item); found_case = true; } else if in_default { @@ -2690,6 +2694,54 @@ mod tests { assert_eq!(ps.spacing_after, 1136); } + /// 손상 HWPX 의 HwpUnitChar `` 값이 2× IR 스케일 변환에서 정수 + /// 곱셈 오버플로 패닉(DoS)을 일으키지 않고 saturating 으로 안전 처리되는지 + /// 검증한다. 종전에는 `value`/`pos` 극단값이 `parse_hwpx_header` 를 패닉시켜 + /// info/export-text/export-structure/convert 전부가 손상 문서 한 건에 + /// 죽었다(header.rs:1288/1357/1907, `attempt to multiply with overflow`). + #[test] + fn hwpunitchar_oversized_value_saturates_without_panic() { + let xml = r##" + + + + + + + + + + + + + + + + + + + + + + + + + +"##; + + // 핵심: 패닉하지 않고 Ok 를 돌려준다. + let (doc_info, _) = parse_hwpx_header(xml).expect("손상 HwpUnitChar 값은 패닉 없이 파싱"); + let ps = &doc_info.para_shapes[1]; + // 2_000_000_000 × 2 는 i32 를 넘으므로 i32::MAX 로 포화된다. + assert_eq!(ps.margin_left, i32::MAX); + assert_eq!(ps.margin_right, i32::MAX); + assert_eq!(ps.line_spacing, i32::MAX); + // 2_147_483_648(u32) × 2 는 u32 를 넘으므로 u32::MAX 로 포화된다. + assert_eq!(doc_info.tab_defs[0].tabs[0].position, u32::MAX); + } + #[test] fn odd_para_margin_survives_hwpx_serialize_parse_roundtrip() { // [#3368] 홀수 여백(ml=101)은 (HwpUnitChar) 의 정수 나눗셈으로