Skip to content

fix(수식): eqedit·renderer/equation 파서 DoS 하드닝 — 깊은 중첩 스택 오버플로 + 괄호 O(n^2) 행 (#4865) - #4866

Closed
kevin9327 wants to merge 1 commit into
edwardkim:develfrom
kevin9327:gym_equation_dos
Closed

fix(수식): eqedit·renderer/equation 파서 DoS 하드닝 — 깊은 중첩 스택 오버플로 + 괄호 O(n^2) 행 (#4865)#4866
kevin9327 wants to merge 1 commit into
edwardkim:develfrom
kevin9327:gym_equation_dos

Conversation

@kevin9327

Copy link
Copy Markdown
Contributor

무엇을 · 왜

수식 하위 시스템(src/doclang/eqedit/ LaTeX 변환기, src/renderer/equation/ 렌더러)의 재귀 하강 파서에 중첩 깊이 상한이 없어, 손상·악의적 수식 스크립트로 스택 오버플로(DoS, 프로세스 강제 종료) 가 가능했다. 수식은 HWP/HWPX 문서에 내장되어 export-svg/export-png/렌더(paragraph_layout·shape_layout)와 eqedit LaTeX 변환(doclang writer)로 처리되므로 문서 하나로 트리거된다.

퍼징(subprocess 격리)으로 발견한 개별 결함:

  1. renderer 파서 무한 재귀 — 중첩 그룹 {{{…}}}, 중첩 명령 sqrt sqrt …/LEFT ( …, 중첩 matrix/cases/pile/eqalign → 스택 오버플로(디버그 실측 임계: 중첩 sqrt ~144단계).
  2. renderer 반복 생성 깊은 트리1 over 1 …, OVER/ATOP 연쇄가 좌편향 깊은 EqNode 를 쌓아 이후 재귀적 Drop/layout/svg 에서 오버플로(~6300단계).
  3. eqedit 파서 무한 재귀 — 중첩 그룹/명령(sqrt·root·bar·matrix·left …) → 오버플로(중첩 brace ~153단계).
  4. eqedit 반복 생성 깊은 트리over/atop·^/_ 연쇄 → 재귀적 Drop/LaTeX emit 오버플로.
  5. renderer paren_then_script O(n^2) 행(hang) — LParen 마다 매칭까지 O(n) 전방 스캔 → 괄호 N개면 O(n^2). baseline(devel) 실측: (×1만 4.9s, ×2만 14.4s, ×4만 >40s.

형제 파서에는 이미 깊이 가드가 있으나(MAX_HWPX_SECTION_DEPTH=64, MAX_HWP5_SHAPE_DEPTH=256, MAX_DRAWING_OBJECT_DEPTH=256) 수식 파서에는 없었다.

변경

  • MAX_EQ_DEPTH = 64 중첩 깊이 상한을 두 파서에 추가(형제 가드 미러링, 실측 오버플로 임계 ~144보다 넉넉히 아래·실제 수식 중첩(수 단계)보다 훨씬 위).
    • renderer: 중첩 게이트웨이(parse_command/parse_group/parse_paren_group)에서 깊이 증감. 초과 시 그룹/괄호는 매칭까지 건너뛰고 명령은 인자 없이 Empty 반환 → 입력 진행 보장(비진행 루프·행 없음), 트리 깊이 제한.
    • eqedit: 재귀 funnel parse_atom 에서 깊이 가드, 초과 시 EqError::TooDeep 로 우아하게 실패(writer 가 placeholder fallback — 기존 오류 정책과 동일).
  • OVER/ATOP·첨자 연쇄 길이 캡 — 반복으로 쌓이는 깊은 트리를 같은 상한으로 제한(연쇄가 아닌 별개 분수는 리셋되어 영향 없음).
  • paren_then_script O(1) 화 — LParen→RParen 매칭을 스택으로 한 번(O(n)) 사전계산해 조회 → O(n^2) 제거.

cargo fmt 는 이 환경에서 실패(os error 206)하여 변경 leaf 파일에 rustfmt --edition 2021 적용, --check 무출력 확인.

검증

  • 퍼징 재현 46건 전부 graceful (패닉/행/오버플로 0). 극단 크기(50만 토큰)도 선형 처리(오버플로/행 아님).
  • paren_then_script: O(n^2)→선형(실측 ~100배, (×4만 0.41s).
  • 유효 수식 38건: origin/devel 대비 LaTeX+SVG+치수 바이트 동일(회귀 없음).
  • cargo test --lib 3708 통과(신규 회귀 테스트 6건 포함, 실패 0).
  • eqedit 72 + equation 178 기존 테스트 통과.

Closes #4865

…^2) 행 (edwardkim#4865)

수식 하위 시스템의 재귀 하강 파서에 중첩 깊이 상한이 없어 적대적으로 깊은
중첩(`{{{…}}}`, `sqrt sqrt …`, `LEFT ( …`, 중첩 matrix/cases)이나 긴 연쇄
(`1 over 1 …`, `x^x…`)에서 스택 오버플로가 발생했다. 반복으로 쌓인 깊은 AST 는
파싱 호출스택이 얕아도 이후 재귀적 Drop/layout/svg/LaTeX emit 에서 오버플로한다.
또한 renderer 의 `paren_then_script` 가 LParen 마다 O(n) 전방 스캔을 해 괄호가
많은 입력에서 O(n^2) 행(hang)을 유발했다.

- 두 파서에 `MAX_EQ_DEPTH=64` 중첩 깊이 상한 추가(형제 가드
  `MAX_HWPX_SECTION_DEPTH`·`MAX_HWP5_SHAPE_DEPTH` 미러링). 초과 시 renderer 는
  truncate(입력 진행 보장), eqedit 는 `EqError::TooDeep` 로 우아하게 실패.
- OVER/ATOP·첨자 연쇄 길이도 동일 상한으로 캡해 트리 깊이를 제한.
- `paren_then_script` 를 LParen→RParen 매칭 사전계산(O(n))으로 교체 → O(1) 조회.

검증: 퍼징 재현 46건 전부 graceful(패닉/행/오버플로 없음), 유효 수식 38건
LaTeX+SVG 바이트 동일, cargo test --lib 3708 통과, 회귀 테스트 추가.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@jangster77

Copy link
Copy Markdown
Collaborator

통합 PR #4883(4412546)로 병합 완료했습니다.

원 head와 CI를 다시 확인해 누적 반영했고, 상세 검토·메인터너 보정·검증 근거는 archive 검토 기록에 남겼습니다.

중복 병합을 막기 위해 이 원 PR을 닫습니다. 감사합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants