MessagePack: `strict_map_key`検証失敗時のmsgpack C拡張におけるメモリリーク
### 影響
`msgpack-python`のC拡張 (`msgpack._cmsgpack`) は、`strict_map_key=True` (デフォルト) の設定でデシリアライゼーションがキーを拒否した場合、デコードされたマップの値をメモリリークします。 繰り返し不正な入力があると、プロセスで使用されるメモリが無限に増加し、攻撃者が制御するMessagePackデータをデシリアライズするアプリケーションでサービス拒否が発生する可能性があります。 `unpackb()`関数とストリーミングAPIの`Unpacker`の両方に影響があります。 純粋なPythonで実装された代替機能では、報告されたテストではこのメモリリークは確認されませんでした。
報告書によると、1回の解析失敗ごとに約4MiBのメモリが保持され、WindowsでPython 3.14とmsgpack 1.2.2を使用した場合、25回の解析失敗後には100MiBのメモリが保持されると測定されました。 これらの結果は、報告者から提供されたものです。
### 追加の影響: 古くなったパーサースタック参照
このメモリリークを修正する際に実施された参照管理の監査で、ストリーミング`Unpacker`に別のメモリ安全性の問題が見つかりました。
`unpack()`がネストされたコンテナの解析を完了した後、非アクティブなパーサースタックのスロットが、デコードされたオブジェクトへの所有権を持たないポインタを保持する可能性があります。 その後、`skip()`が呼び出されると、そのスロットが再アクティブ化されることがあります。 解析が失敗した場合、コンテキストのクリーンアップ処理は、この古くなったポインタを所有権を持つ参照として扱い、参照カウントをデクリメントします。
これにより、オブジェクトの参照カウントが破損し、後で実際の所有者が解放する際に、不正な参照アクセス (use-after-free) や `Py_DECREF` の重複呼び出しが発生する可能性があります。
このAPIを使用するユーザーは少ないですが、使用しているユーザーは、不正な参照アクセスにより、クラッシュや予期しないエラーが発生する可能性があります。
### 詳細
`msgpack/unpack.h`の`unpack_callback_map_item()`関数は、`str`または`bytes`ではないキーを拒否します。 この時点で、マップの値はすでに構築されています。 エラーを早期に返すことで、値への参照が解放されません。
`msgpack/unpack_template.h`において、`unpack_execute()`関数内で、値はローカル変数`obj`に保持されています。`CT_MAP_VALUE`のエラー発生時には、参照カウンタのデクリメントが行われずに`_failed`にジャンプします。パーサースタックのクリーンアップ処理では、この値がスタックに保存されていないため、復元されません。フォールバック処理では、値をデコードする前にキーがチェックされます。
### 影響を受けるバージョン
この問題は、**1.2.2**で再現され、開発版のコミット`475c1b7`で報告されています。影響を受ける製品の欄には、再現されたリリースのみが記載されており、以前のリリースが影響を受けていないという主張ではありません。
影響を受ける最も古いバージョンの確認が必要です。報告書の概要では1.1.xから1.2.2までのバージョンが対象とされていますが、根本原因の分析では、この問題がコミット`cd81370`(PR #673)で導入されたとされています。最終的な影響を受けるバージョンの範囲を公開する前に、この矛盾を解消する必要があります。
### 再現方法
C拡張がインストールされた、一時的なローカルプロセスで以下のコードを実行します。
```python
import msgpack
payload = msgpack.packb({42: b"A" * (4 * 1024 * 1024)}, use_bin_type=True)
msgpack.unpackb(payload) # ValueErrorが発生します。報告書では、4MiBのメモリリークが確認されています。
```
ストリーミングAPIのトリガー:
```python
import msgpack
payload = msgpack.packb({42: b"A" * (4 * 1024 * 1024)}, use_bin_type=True)
u = msgpack.Unpacker()
u.feed(payload)
list(u) # 同じstrict_map_keyのエラーが発生します。
```
### 対策
純粋なPythonによるフォールバック処理では、このパスでのメモリリークが発生しないと報告されており、アプリケーションの互換性とパフォーマンスの確認後、一時的な回避策として使用できる可能性があります。リクエストサイズやレート制限によって影響を軽減できますが、累積的なリークを完全に解消することはできません。
影響を受けるパッケージ:
- pip (Python) msgpack: 影響を受けるバージョンは1.2.3未満、1.2.3で修正済み。