손상 입력의 과다 line_seg O(n²) 무한정지 DoS 방어 (#4813) - #4842
Closed
kevin9327 wants to merge 2 commits into
Closed
Conversation
파싱 직후, 저장된 line_seg 수가 문단 문자 수를 크게 초과하는(물리적으로 불가능한) 문단의 line_seg 배열을 비운다. line_seg 하나는 화면상 한 줄이고 한 줄은 문자를 최소 1개 담으므로 정상 문서는 언제나 line_seg 수 ≤ 문자 수 + 1 이다. 손상된 HWP/HWPX 는 길이·개수 필드 훼손으로 이 배열을 수만 개까지 부풀릴 수 있고(퍼징 실측 samples/hwp3-sample14.hwp 10% 바이트 플립본: 한 문단 line_seg 25,856 개 > 문자 21,454 개), 그러면 compose_lines·layout 이 line_seg 마다 문단 전체 텍스트를 다시 슬라이싱·배치해 O(line_seg 수 × 문단 길이)로 폭주한다 — info·export-text 가 유한 시간에 끝나지 않는 서비스 거부(DoS)다. - drop_corrupt_oversized_linesegs(): 상한을 문자 수 + 64 로 넉넉히 잡아 정상 문단(줄바꿈만 있는 문단 포함)은 절대 걸리지 않으므로 동작이 동일하다. 포맷 무관 가드다. 비운 뒤 기존 리플로우/합성 폴백이 문단을 정상 재구성한다. - 회귀 테스트 2건 추가(손상 배열 제거 / 정상 문단 보존). - 실측: 손상본 info·export-text 무한정지 → 0.2s·1.8s 종료. 정상 문서 10종 (HWP3·HWP5·HWPX)의 info·export-text 출력 바이트 동일(전/후 SHA-256 비교). 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. |
CI Lint 의 "Check workspace members (FFI bindings, tools)" 가 실패했다 (PR edwardkim#4842). Build & Test 실패는 그 롤업이다. src/document_core/commands/document.rs 의 경계 테스트가 `std::iter::repeat('\n').take(300).collect()` 로 문자열을 만드는데, clippy manual_str_repeat 이 -D warnings 에 걸린다. `"\n".repeat(300)` 은 같은 값을 내면서 의도도 더 분명하다. 검증: cargo clippy --workspace --all-targets -- -D warnings 통과, drop_corrupt_oversized_linesegs 테스트 2/2 통과, rustfmt 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jangster77
self-requested a review
August 15, 2026 13:29
jangster77
pushed a commit
that referenced
this pull request
Aug 15, 2026
CI Lint 의 "Check workspace members (FFI bindings, tools)" 가 실패했다 (PR #4842). Build & Test 실패는 그 롤업이다. src/document_core/commands/document.rs 의 경계 테스트가 `std::iter::repeat('\n').take(300).collect()` 로 문자열을 만드는데, clippy manual_str_repeat 이 -D warnings 에 걸린다. `"\n".repeat(300)` 은 같은 값을 내면서 의도도 더 분명하다. 검증: cargo clippy --workspace --all-targets -- -D warnings 통과, drop_corrupt_oversized_linesegs 테스트 2/2 통과, rustfmt 통과. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
문제 (#4813)
퍼징(gym 손상-강건성 감사)이 rhwp 가 손상된 문서에서 유한 시간에 끝나지 않는(무한정지급) DoS 를 실측했다. 결정적 재현:
원인 — 계측으로 확정 (파서 아님, 렌더 경로)
이슈의 원인 추정은 HWP3 파서 순회 루프였으나, 임시 반복 카운터와 단계 마커로 계측한 결과 실제 폭주 지점은 파서가 아니라 렌더 경로였다:
parse는 정상 종료한다.info는 compose(compose_lines) 단계에서,export-text는 layout(build_page_tree) 단계에서 멈춘다 (둘 다from_bytes→ paginate/build_page_tree 경로).text_start=[0, 9203, 0, 0, …] 비단조)를 갖는다. line_seg 하나는 화면상 한 줄이고 한 줄은 문자를 최소 1개 담으므로, 정상 문서는 언제나line_seg 수 ≤ 문자 수 + 1이다.compose_lines·layout 은 line_seg 마다para.text를 다시 슬라이싱·배치하므로, 이 배열이 부풀면 O(line_seg 수 × 문단 길이) ≈ 5.5억 회 로 폭주한다 — 사실상 종료하지 않는 DoS.수정
from_bytes파싱 직후(compose/pagination/layout 이 저장 line_seg 를 소비하기 전) 물리적으로 불가능한 과다 line_seg 배열을 비우는 포맷 무관 가드drop_corrupt_oversized_linesegs()를 추가한다. 비운 문단은 기존 리플로우/합성 폴백(compose_lines의 line_seg 부재 경로 등)이 텍스트로부터 정상 재구성한다. 상한을문자 수 + 64로 넉넉히 잡아 정상 문서(줄바꿈만 있는 문단 포함)는 절대 걸리지 않으므로 동작이 완전히 동일하다.이슈는 HWP3 파서로 지목했으나, 근본 원인은 특정 포맷이 아니라 렌더러가 소비하는 손상 line_seg 배열이므로 로드 직후 공통 지점에 가드를 둔다(HWP3 전용 분기 없음).
검증
info무한정지 → 0.23s,export-text→ 1.78s 종료(정상 오류/성공, 패닉 없음).hwp3-sample14·hwp3-sample10, HWP5, HWPX)의info·export-text출력이 수정 전/후 바이트 동일(SHA-256 비교).cargo test --lib전량 통과,rustfmt --edition 2021 --check·cargo clippy --lib무경고.비고
같은 퍼징 세트의 다른 표본(
issue2063_huge_cellbreak_table.hwp,hwp3-sample10-hwp5.hwp)은 90초 계측 결과 유한 시간에 종료(디버그 빌드에서 느릴 뿐)했다 — 과다 line_seg 가 아닌 별개 원인이라 본 PR 범위 밖이다.Closes #4813