Skip to content

[bug] hwp3-sample16.hwp WASM 로드 실패 — RawVec capacity overflow (HWP3 picture record alignment) #877

@jangster77

Description

@jangster77

증상

samples/hwp3-sample16.hwp (2.9 MB, 64쪽 RFP 문서, 한국수자원공사 2004.11) 를 rhwp-studio 에서 열면 다음 panic 발생:

panicked at library/alloc/src/raw_vec/mod.rs:28:5: capacity overflow
[main] 파일 로드 실패: RuntimeError: unreachable

한컴오피스에서는 정상 오픈됨.

환경별 차이

환경 결과
rhwp-studio (WASM, 32-bit) Rust panic: RawVec capacity overflow → wasm unreachable trap
네이티브 CLI (rhwp dump, 64-bit) Graceful Err: HWP 파싱 실패 - 유효하지 않은 파일: HWP 3.0 오류: 입출력 오류가 발생했습니다: failed to fill whole buffer
다른 HWP3 샘플 (sample14 등) 정상

같은 코드가 32-bit/64-bit 메모리 가용성에 따라 panic / Err 로 갈림.

근본 원인 (probe binary 로 추적)

1차: 거대 length 할당

src/parser/hwp3/records.rs:413Hwp3AdditionalInfoBlock::read:

let length = reader.read_u32::<LittleEndian>()?;
let mut data = vec![0u8; length as usize];  // ← panic 지점

probe 실측: decompressed body offset 15131 위치에서

  • id u32 = 0x00000002
  • length u32 = 0xDC000000 = 3,690,987,520 (~3.69 GB)

32-bit WASM 메모리 한계 초과 → RawVec capacity overflow.

2차 (상위): 파라그래프 리스트 조기 종료

  • decompressed body 크기: 16,199,028 바이트 (16.2 MB, 64쪽 분량)
  • HWP3 파서 인식 paragraph 수: 1개 (cc=5, lc=1 — 표지 picture 포함)
  • 나머지 16.18 MB 가 통째로 additional_info_blocks 영역으로 잘못 진입
  • 첫 picture(ch=11) 처리 후 cursor alignment 가 misalign 되어, 정상 paragraph 헤더 자리에 발견된 00 00 00 ... 패턴이 char_count=0 end marker 로 오인됨

의심 코드: HWP3 picture(ch=11) 처리

src/parser/hwp3/mod.rs:929-933:

let n_ext_from_buf = (&info_buf[0..4]).read_u32::<LittleEndian>().unwrap_or(0);
let n_ext = n_ext_from_buf;
let mut ext_buf = vec![0u8; n_ext as usize];
if let Err(_) = body_cursor.read_exact(&mut ext_buf) { break; }
  • sample16 실측: info_buf[0..4] = 00 00 00 00 → n_ext = 0 (이 위치 자체는 sample16 에서 무해)
  • 그러나 ext_bufpic_type == 3 (drawing object) 일 때만 실제 사용됨에도 무조건 할당+read 함
  • 이후 mod.rs:978caption_paras = parse_paragraph_list(...) 재귀 호출의 byte alignment 가 정확하지 않을 가능성 (sample16 의 표지 picture 가 한컴 편집기에서 정상 표시되는 것으로 보아 file 자체는 valid HWP3)

probe 추적 detail

body_data.len() = 16199028
font_faces (7 lang) 정상 파싱: 끝 pos = 2694
nstyles = 51, 정상 파싱: 끝 pos = 14834
paragraph list start = 14834
  para[0] @ 14834: cc=5, lc=1, end_pos = 15088
  end marker @ 15088 (cc=0)  ← 조기 종료
after paragraphs pos = 15131 (43 bytes end marker 소비)
additional_info_block[0] @ 15131:
  id = 2
  length = 0xDC000000 = 3690987520  ← garbage, panic 유발
  bytes: [02 00 00 00] [00 00 00 dc] 0a f5 03 00 00 00 00 01 00 12 ff 94 fe ...

재현 방법

cargo run --release --bin rhwp -- dump samples/hwp3-sample16.hwp
# → "HWP 파싱 실패 - 유효하지 않은 파일: HWP 3.0 오류: 입출력 오류가 발생했습니다: failed to fill whole buffer"

# 또는 rhwp-studio 에서 samples/hwp3-sample16.hwp 파일 열기
# → "파일 로드 실패: RuntimeError: unreachable"

samples/hwp3-sample16.hwp 는 작업지시자가 추가한 새 샘플 (현재 미커밋). samples/hwp3-sample16-hwp5.hwp / samples/hwp3-sample16-hwp5.hwpx 는 동일 문서의 한컴 HWP5/HWPX 변환본이며 비교용.

수정 방향 (제안)

  1. 방어적 할당 가드 (즉시 효과): Hwp3AdditionalInfoBlock::readmod.rs:932 (picture ext_buf) 에서 length/n_ext 가 남은 stream 크기보다 크면 Err 반환. ch=29 (mod.rs:1119) 에는 이미 < 1000000 검증 존재 — 동일 패턴 적용.
  2. WASM panic hook 강화: console_error_panic_hook 등록 (이미 panic 시 stack 일부 노출되므로 부분적으로 동작 중인 것으로 보임), Vec::try_with_capacity 또는 try_reserve 사용으로 panic → Err 변환.
  3. 상위 원인 해결: HWP3 picture(ch=11) 처리에서 ext_buf 읽기를 pic_type == 3 일 때만 수행하도록 변경. 그리고 첫 paragraph 의 byte consumption 검증 (실제 sample16 body 가 어디까지 사용되어야 정합한지 spec 재확인).

관련 파일

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions