Skip to content

fix(hwpx): OLE/차트·도형 오프셋 파싱에서 음수값 유실 2건 - #3758

Closed
kevin9327 wants to merge 3 commits into
edwardkim:develfrom
kevin9327:pr/task-bughunt-3
Closed

kevin9327 wants to merge 3 commits into
edwardkim:develfrom
kevin9327:pr/task-bughunt-3

Conversation

@kevin9327

@kevin9327 kevin9327 commented Aug 1, 2026 •

Copy link
Copy Markdown
Contributor

요약

HWPX 파서에서 음수 HWPUNIT 오프셋을 다루는 필드 중, 형제 필드(hp:tbl/hp:pic/hp:line 의 vertOffset/horzOffset)는 이미 parse_i32_wrapping을 쓰는데 아직 parse_u32를 쓰는 경로 2곳을 대조 발견해 고쳤습니다. 각각 별도 커밋 + 회귀 테스트.

1. OLE/차트 hp:pos vertOffset·horzOffset (bf9c4e67b)

parse_common_shape_children(hp:ole, hp:chart 공용)의 <hp:pos vertOffset="..." horzOffset="...">가 parse_u32를 쓰고 있었습니다. HWPX는 음수 HWPUNIT을 unsigned 32-bit decimal 문자열로 저장하는 경우가 있는데("4294965296" = i32 -2000), parse_u32는 그대로 거대한 양수로 저장해 위/왼쪽으로 벗어난 OLE·차트 개체 좌표가 뭉개졌습니다.

  • 회귀 테스트: bugfind_ole_pos_negative_offset_is_not_wrapped_to_huge_u32

2. 도형 hp:offset x/y (d5f520c18)

parse_picture와 parse_object_layout_child(선/사각형/타원 등 공용)의 <hp:offset x="..." y="..."> (개체 내부 shape-transform 오프셋)도 parse_u32를 쓰고 있었습니다. 이 경우 "-500"처럼 순수 음수 문자열은 u32 파싱 자체가 실패해 unwrap_or(0)으로 조용히 0이 되고, shape_attr.offset_x/y 뿐 아니라 (hp:pos가 없을 때 폴백되는) common.horizontal_offset/vertical_offset까지 음수 오프셋이 유실됐습니다.

  • 회귀 테스트: bugfind_shape_offset_negative_x_y_not_dropped_to_zero

검증

  • cargo test --lib parser::hwpx::section::tests::bugfind_ole_pos_negative_offset_is_not_wrapped_to_huge_u32 — pass
  • cargo test --lib parser::hwpx::section::tests::bugfind_shape_offset_negative_x_y_not_dropped_to_zero — pass
  • cargo test --lib parser::hwpx::section::tests:: -- offset (관련 offset 기존 테스트 120개 포함) — 전부 pass, 회귀 없음
  • cargo fmt --all -- --check — clean

디스크 제약 안내: 작업 중 C: 드라이브 여유공간이 4.3GB → 1.8GB까지 떨어져(빌드 캐시 성장), 이후 전체 빌드/전체 테스트 스위트는 실행하지 못했습니다. 위 narrow test 실행 결과만으로 검증했습니다. 원래 3-5건을 찾는 게 목표였으나 디스크 여유가 임계치에 가까워 2건에서 커밋+푸시하고 마감합니다.

Test plan

  • bugfind_ole_pos_negative_offset_is_not_wrapped_to_huge_u32 (narrow, pass)
  • bugfind_shape_offset_negative_x_y_not_dropped_to_zero (narrow, pass)
  • 관련 기존 offset 테스트 120개 회귀 없음 확인
  • cargo fmt --all -- --check
  • 전체 cargo test는 디스크 제약으로 미실행

🤖 Generated with Claude Code


📌 추가 커밋 안내 — #3763 플레이키 수정을 함께 실었습니다

이 PR 의 변경과 무관하게 CI 가 계속 빨갰습니다. devel(cc38291) 자체가 #3742 머지 이후
tests/batch_axes_contract.rs 의 stdin 헬퍼 때문에 깨져 있고, 그 커밋을 기반으로 하는 모든 PR 이
같은 지점에서 막힙니다.

thread 'batch_global_auth_options_are_rejected_before_consuming_path_stdin' panicked at tests/batch_axes_contract.rs:34:10:
stdin 쓰기 실패: Os { code: 32, kind: BrokenPipe, message: "Broken pipe" }

재실행으로는 빠져나가지 못했습니다 — 이 PR 도 재실행했지만 같은 지점에서 다시 실패했습니다.
그래서 이 브랜치의 CI 를 읽을 수 있게 하려고 #3766 의 테스트 하네스 수정
(tests/ 3개 파일, 제품 코드 무변경)을 그대로 실었습니다.

원인 분석은 #3763 에 있습니다. #3766 이 먼저 머지되면 이 커밋은 빈 diff 가 되어 리베이스 때
자연히 떨어져 나갑니다.
리뷰 시 마지막 커밋
test(cli): stdin 계약 테스트의 BrokenPipe 플레이키를 함께 싣는다 (#3763) 는 이 PR 의 본 주제가
아니니 분리해서 보시면 됩니다.

parse_common_shape_children(OLE/차트가 공유)의 hp:pos vertOffset/horzOffset은
parse_u32를 썼다. HWPX는 음수 HWPUNIT을 unsigned 32-bit decimal 문자열로
저장하는데("4294965296" = i32 -2000), parse_u32는 이를 그대로 거대한 양수로
저장해 위/왼쪽으로 벗어난 OLE·차트 개체 좌표가 뭉개졌다.

표(hp:tbl)·그림(hp:pic)·선(hp:line)은 이미 parse_i32_wrapping을 쓰고 있어
동일 필드의 형제 경로와 비교해 발견했다. OLE/차트 공용 경로만 아직
parse_u32를 쓰고 있었던 것.

Regression test: bugfind_ole_pos_negative_offset_is_not_wrapped_to_huge_u32
parse_picture와 parse_object_layout_child(선/사각형/타원 등 공용)의
<hp:offset x="..." y="..."> 는 개체 내부 shape-transform 오프셋인데
parse_u32로 읽고 있었다. "-500" 같은 음수 문자열은 u32 파싱에 실패해
unwrap_or(0)으로 조용히 0이 되어, shape_attr.offset_x/y 뿐 아니라
(hp:pos가 없을 때 폴백되는) common.horizontal_offset/vertical_offset 까지
음수 오프셋이 유실됐다.

형제 필드인 hp:pos의 vertOffset/horzOffset은 이미 parse_i32_wrapping을
쓰고 있어 이를 대조해 발견했다.

Regression test: bugfind_shape_offset_negative_x_y_not_dropped_to_zero
이 PR 의 변경과 무관하게 CI 가 계속 빨갛다. devel(cc38291) 자체가 edwardkim#3742 머지
이후 tests/batch_axes_contract.rs 의 stdin 헬퍼 때문에 깨져 있고, 그 커밋을
기반으로 하는 모든 PR 이 같은 지점에서 막힌다.

    thread 'batch_global_auth_options_are_rejected_before_consuming_path_stdin'
    panicked at tests/batch_axes_contract.rs:34:10:
    stdin 쓰기 실패: Os { code: 32, kind: BrokenPipe, message: "Broken pipe" }

재실행으로는 빠져나가지 못한다 — 이 PR 도 재실행했지만 같은 지점에서 다시
실패했다. 이 브랜치의 CI 를 읽을 수 있게 하려고 edwardkim#3766 의 테스트 하네스 수정을
그대로 싣는다.

근인: 이 헬퍼를 쓰는 테스트들의 계약이 "자식이 stdin 을 읽기 전에 거부하고
종료한다" 이다. 자식이 인자 검증에서 즉시 죽으면 파이프의 읽기 끝이 닫히고
부모의 write_all 이 EPIPE 를 받는다. 기능이 의도대로 일찍 거부할수록 더 잘
깨진다. EPIPE 는 오류가 아니라 검증 대상 동작의 정상적인 부산물이므로
ErrorKind::BrokenPipe 만 넘어가고 그 밖의 오류는 그대로 패닉한다.

제품 코드 변경 없음. 테스트 하네스만 고친다.

edwardkim#3766 이 먼저 머지되면 이 커밋은 빈 diff 가 되므로 리베이스 때 떨어져 나간다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@edwardkim edwardkim added this to the v1.0.0 milestone Aug 2, 2026
@edwardkim edwardkim added bug Something isn't working hwpx HWPX 포맷 처리: 파싱, 직렬화, roundtrip, XML/ZIP 패키지 보존, 호환성 개선 labels Aug 2, 2026
@jangster77
jangster77 self-requested a review August 2, 2026 10:48
@jangster77

Copy link
Copy Markdown
Collaborator

통합 검토 PR #3801이 devel에 merge되었습니다.

이 PR의 contributor 기능 commit은 cherry-pick -x로 통합했으며, 누적 과정에서 확인된 binding
계약과 회귀 테스트 보정도 같은 통합 PR에 포함했습니다. 원 source branch는 수정하거나 삭제하지
않았습니다.

원 PR별 검토 기록과 적용 SHA는
mydocs/pr/archives/pr_3747_3779_kevin_review_impl.md 및 해당 pr_<번호>_review.md에 남겼습니다.
기여해 주셔서 감사합니다.

@jangster77 jangster77 closed this Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working hwpx HWPX 포맷 처리: 파싱, 직렬화, roundtrip, XML/ZIP 패키지 보존, 호환성 개선

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants