diff --git a/data/contributions/8536386.md b/data/contributions/8536386.md new file mode 100644 index 000000000..c7fddf2b4 --- /dev/null +++ b/data/contributions/8536386.md @@ -0,0 +1,129 @@ +--- +title: "[textdecoder] Clear JIS0212 state on EUC-JP errors" +date: 2026-10-08 +resolvedDate: 2026-10-13 +author: Yelihi +contribution_url: https://crrev.com/c/8536386 +module: blink +kind: fix +keywords: ["TextDecoder", "TextCodecCJK", "EUC-JP", "JIS0212", "encoding", "wpt"] +status: merged +crbug: 467624168 +related: [8503856] +externalLinks: + - title: "WPT PR #63590 · EUC-JP 오류 이후 JIS0212 상태 회귀 테스트" + url: https://github.com/web-platform-tests/wpt/pull/63590 +--- + +EUC-JP 디코딩 중 잘못된 JIS0212 바이트열을 만난 뒤에도 문자표 선택 상태가 남아, +같은 세션의 후속 문자를 잘못 해석하던 문제를 수정했습니다. 오류 경로에서 상태를 +초기화하는 한 줄을 추가하고, 일반 입력·스트리밍·fatal 오류 이후 재개를 확인하는 +WPT 회귀 테스트를 추가했습니다. + +## 문제 설명 + +EUC-JP 디코더는 `0x8F`로 시작하는 JIS0212 시퀀스를 읽을 때 `jis0212_`로 +문자표 선택 상태를 관리합니다. 세 번째 바이트가 유효하지 않으면 오류를 반환하지만, +기존 오류 경로에서는 이 상태를 지우지 않았습니다. + +그 결과 같은 디코딩 세션에서 뒤이어 정상적인 JIS0208 바이트 쌍이 들어와도 +JIS0212 문자표로 해석해 잘못된 결과를 만들 수 있었습니다. 예를 들어 +`0x8F 0xA2 0x20` 뒤에 `0xA1 0xA1`을 전달하면, 마지막 바이트 쌍은 +전각 공백 `U+3000`으로 해석되어야 합니다. + +선행 [CL 8503856](https://crrev.com/c/8503856)은 입력 종료 시 상태를 초기화해 +다음 세션으로 넘어가는 문제를 해결했습니다. 이번 사례는 입력이 끝나기 전, +같은 세션 안에서 오류 이후의 디코딩을 계속하는 경로에 남아 있었습니다. + +## 해결 내용 + +`third_party/blink/renderer/platform/wtf/text/text_codec_cjk.cc`의 +`EucJpDecoder::ParseByte()` 오류 경로에 `jis0212_ = false;`를 추가했습니다. +다음 바이트를 처리하기 전에 문자표 선택을 해제하도록 한 변경입니다. + +아래는 변경 위치를 발췌한 코드입니다. 첫 줄이 이번 CL에서 추가한 부분입니다. + +```cpp +jis0212_ = false; +if (IsAscii(byte)) { + prepended_byte_ = byte; +} +``` + +오류를 일으킨 바이트가 ASCII이면 재처리할 수 있도록 보관하는 기존 동작은 +유지합니다. 따라서 문자표 상태는 지우면서도 해당 ASCII 문자를 잃지 않습니다. +기존 코덱 재사용과 입력 종료 시의 초기화 역시 유지했습니다. + +## 테스트 방법 + +`encoding/textdecoder-mistakes.any.js`에 네 종류의 입력 벡터와 fatal 스트리밍 +오류 이후 재개 테스트를 추가했습니다. + +### 일반 입력과 스트리밍 경계 + +다음 입력을 한 번에 디코딩한 결과와, 가능한 모든 두 청크 분할 위치에서 +디코딩한 결과가 같은지 검사합니다. 첫 청크는 `{stream: true}`로 처리합니다. + +| 입력 바이트 | 기대 결과 | 검증 내용 | +| --- | --- | --- | +| `8F A2 20 A1 A1` | `U+FFFD`, ASCII 공백, `U+3000` | 오류 뒤 ASCII 재처리와 JIS0208 복구 | +| `8F A2 A0 A1 A1` | `U+FFFD`, `U+3000` | 유효 범위 아래의 세 번째 바이트 | +| `8F A2 FF B0 A1` | `U+FFFD`, `U+4E9C` | 유효 범위 위의 세 번째 바이트 | +| `8F A2 AF A1 A1` | `U+02D8`, `U+3000` | 정상 JIS0212 처리 뒤 JIS0208 해석 유지 | + +각 벡터는 바이트 다섯 개로 구성되어, 단일 입력 한 경우와 분할 위치 네 경우를 +검사합니다. 여기에 fatal 오류 테스트를 더해 총 21개의 테스트 케이스를 추가했습니다. + +### fatal 스트리밍 오류 이후 재개 + +`fatal: true`에서는 잘못된 시퀀스가 `TypeError`를 발생시켜야 합니다. +이후 같은 디코더로 입력을 이어 가면, 재처리 대기 중인 ASCII 공백과 정상적인 +JIS0208 문자가 함께 출력되는지 확인합니다. + +아래는 추가한 테스트의 핵심 부분입니다. `u()`는 해당 테스트 파일의 바이트 배열 +생성 헬퍼입니다. + +```js +const d = new TextDecoder('euc-jp', {fatal: true}); +assert_throws_js(TypeError, + () => d.decode(u(0x8f, 0xa2, 0x20), {stream: true})); +assert_equals(d.decode(u(0xa1, 0xa1)), ' \u3000'); +``` + +### 리뷰와 CQ 결과 + +Gerrit 리뷰 요청에서 Adam Rice가 CL 8503856에서 지적한 EUC-JP 문제의 +후속 수정임을 명시했습니다. 2026년 10월 8일 Patch Set 1의 CQ dry run이 +통과했고, Adam Rice와 Kent Tamura의 승인을 거쳐 10월 13일 리베이스된 +Patch Set 2가 머지됐습니다. + +추가한 회귀 테스트는 같은 날 WPT PR #63590으로 upstream에 머지됐습니다. +Gerrit export 댓글과 upstream의 `encoding/textdecoder-mistakes.any.js` 변경을 +대조했습니다. 이 문서 작성 과정에서 Chromium 테스트를 별도로 실행하지는 않았습니다. + +## 연관 CL과 외부 PR + +- [CL 8503856 — 입력 종료 후 CJK 디코더 상태 초기화](https://crrev.com/c/8503856): + ISO-2022-JP와 EUC-JP의 상태가 다음 디코딩 세션으로 넘어가는 문제를 해결한 + 선행 변경입니다. 해당 리뷰에서 이번 오류 처리 경로의 문제가 제기됐습니다. +- [WPT PR #63590 — EUC-JP 상태 회귀 테스트](https://github.com/web-platform-tests/wpt/pull/63590): + 현재 CL에서 자동 export된 테스트 PR입니다. 별도 기여로 중복 집계하지 않고 + `externalLinks`로 연결했습니다. +- [Chromium 이슈 467624168](https://crbug.com/467624168): + 선행 CL과 현재 CL이 참조하는 디코더 상태 관련 이슈입니다. + +## 배운 점 + +- 상태를 가진 파서는 입력 종료뿐 아니라 오류 이후에도 다음 입력을 해석할 수 있는 + 상태로 복구해야 합니다. +- 오류 상태를 지우는 것과 재처리할 입력을 보존하는 것은 별개의 책임입니다. + ASCII 재처리 동작을 유지하면서 문자표 선택만 초기화해야 합니다. +- 바이트열을 한 번에 전달하는 테스트 외에 청크 경계를 바꾼 스트리밍 테스트와 + 예외 이후 재개 테스트를 함께 두면 상태 잔류를 더 정확하게 검증할 수 있습니다. + +## 참고 자료 + +- [현재 CL 8536386 — 설명·변경 코드·리뷰 기록](https://chromium-review.googlesource.com/c/chromium/src/+/8536386) +- [Patch Set 2 — EUC-JP 오류 경로 수정](https://chromium-review.googlesource.com/c/chromium/src/+/8536386/2/third_party/blink/renderer/platform/wtf/text/text_codec_cjk.cc) +- [Patch Set 2 — WPT 회귀 테스트](https://chromium-review.googlesource.com/c/chromium/src/+/8536386/2/third_party/blink/web_tests/external/wpt/encoding/textdecoder-mistakes.any.js) +- [병합된 커밋 951c29c2ac34](https://chromium.googlesource.com/chromium/src/+/951c29c2ac34c231b6e038c296d131104df8391f)