fix(parser/hwp5): 문단↔표↔셀 상호재귀 깊이 상한 — export-structure 스택 오버플로 DoS (#4827) - #4830
Closed
kevin9327 wants to merge 1 commit into
Closed
fix(parser/hwp5): 문단↔표↔셀 상호재귀 깊이 상한 — export-structure 스택 오버플로 DoS (#4827)#4830kevin9327 wants to merge 1 commit into
kevin9327 wants to merge 1 commit into
Conversation
…dwardkim#4827) 손상된 .hwp 를 `export-structure`(및 본문을 파싱하는 모든 명령)로 처리하면 HWP5 본문 파서의 상호재귀에 깊이 상한이 없어 스택 오버플로(SIGSEGV, 패닉과 달리 catch_unwind 로 못 잡음)로 크래시한다. 재귀 경로: parse_paragraph → parse_ctrl_header → parse_control → parse_table_control → parse_cell → parse_paragraph_list → parse_paragraph (↩) 레코드 레벨은 10비트(≤1023)라 표 중첩이 최대 ~341겹까지 파일로 도달 가능하고, 그 깊이가 스레드 기본 스택 한계 근처라 크래시/완주가 비결정적으로 갈린다 (edwardkim#4822 §2 관측과 일치). 수정: 이 재귀 계열이 전부 경유하는 `parse_paragraph` 진입점에 스레드-로컬 RAII 깊이 가드를 둔다(상한 64, HWPX edwardkim#4759·HWP3 edwardkim#4285·HWP5 묶음개체 edwardkim#4761 형제 가드와 동일 값). 상한 초과 시 BodyTextError 로 거부하면 상위 `parse_paragraph_list` 의 `if let Ok(..)` 가 해당 하위 트리만 절단하고 나머지는 정상 파싱한다. 정상(얕은 중첩) 문서 동작은 불변. 검증: - 결정론적 재현: 표 250겹 파싱이 상한 없이 250 그대로 내려감(수정 전); 341겹 + 작은 스택 → STATUS_STACK_OVERFLOW(0xC00000FD). - 회귀 테스트 2건 추가(상한 초과 절단 / 정상 깊이 보존). - 정상 샘플 30개 × 3모드 = 90개 export-structure 출력이 수정 전후 완전 동일. - cargo test --lib 3704 통과·0 실패, rustfmt·clippy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Collaborator
|
통합 PR #4883(4412546)로 병합 완료했습니다. 원 head와 CI를 다시 확인해 누적 반영했고, 상세 검토·메인터너 보정·검증 근거는 archive 검토 기록에 남겼습니다. 중복 병합을 막기 위해 이 원 PR을 닫습니다. 감사합니다. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
무엇을 / 왜
손상된
.hwp를rhwp export-structure(및 본문을 파싱하는 모든 명령:info·export-text등)로처리하면 스택 오버플로로 크래시한다(SIGSEGV — 패닉과 달리
catch_unwind로 못 잡는 프로세스종료). HWP5 본문 파서의 문단↔표↔셀 상호재귀에 깊이 상한이 없는 것이 근본 원인이다.
#4822§2 가 코퍼스 퍼징으로 관측한export-structure스택 오버플로의 정밀 원인 규명 겸 수정이다(#4822 는 "구조 분류(clause/outline) 재귀"로 추정했으나, 실제 무한 재귀는 그 이전 단계인 본문
파싱에 있다). Fixes #4827.
재귀 경로 (무한 하강)
셀 안의 문단이 다시 표를 품는 사이클이며, 머리말/꼬리말·각주/미주·글상자(GSO)·캡션의 내부 문단도
모두
parse_paragraph_list를 경유하므로 같은 사이클에 속한다. 레코드 헤더 레벨은 10비트(≤1023)(
record.rs)라 표 한 겹이 레벨을 3 파므로 손상 문서로 최대 ~341겹까지 도달 가능하다. 이 깊이가스레드 기본 스택 한계 근처라 스택 크기·레이아웃에 따라 크래시하거나 과대 트리를 방출하며 완주한다
—
#4822의 "비결정적 크래시 / 3000+ 줄 중첩 JSON" 관측과 일치한다.수정
이 재귀 계열이 전부 경유하는
parse_paragraph진입점에 스레드-로컬 RAII 깊이 가드(
SectionDepthGuard) 를 둔다(상한MAX_HWP5_SECTION_DEPTH = 64). 파라미터를 여러 호출부에관통시키지 않고 한 곳에서 전 경로를 봉인한다. 상한 초과 시
BodyTextError로 거부하면, 상위parse_paragraph_list의if let Ok(..)가 해당 하위 트리만 우아하게 절단(truncate) 하고 나머지는정상 파싱한다.
이미 형제 파서들이 같은 종류의 상호재귀를 상한으로 봉인해 두었고, HWP5 본문 경로만 빠져 있었다:
HWPX 섹션
MAX_HWPX_SECTION_DEPTH=64(#4759) · HWP3 그리기 개체 (#4285) · HWP5 묶음 개체parse_container_children(#4761) · HMLmax_depth=256. 값 64 는 가장 가까운 형제인 HWPX 섹션가드와 동일하다.
검증
parse_body_text_section에 먹여재현. 표 250겹 → 파싱 깊이 250(상한 없음 확정); 341겹 + 작은 스택 스레드 →
thread has overflowed its stack, 종료 코드0xC00000FD (STATUS_STACK_OVERFLOW).src/parser/body_text/tests.rs):nested_table_recursion_is_depth_capped(상한 초과 → 절단·무크래시),shallow_table_nesting_is_preserved(정상 5겹 → 깊이 보존, 과잉 차단 없음).export-structure출력이수정 전/후 바이너리에서 바이트 단위로 완전 동일.
cargo test --lib3704 통과 / 0 실패,rustfmt --check·cargo clippy --libclean.범위
HWP5 본문(
body_text/control)의 표 상호재귀만 다룬다.#4822§1(파서 무한루프)은 근본 원인이달라(커서 미전진) 별도 작업 대상이다.