mailbox.MH.get_sequences() unnecessarily materializes large sequence ranges
還沒有人認領這個 Issue。
- 主要語言
- Python
- 星號
- 77.2k
- 分支
- 35.9k
- PR 合併指標
- PR 指標待擷取
描述
Feature or enhancement
Proposal:
Hello Python Security Response Team,
I am reporting a resource-exhaustion issue in CPython's mailbox.MH implementation. MH.get_sequences() materializes every integer in a range from the .mh_sequences metadata file before filtering the result against the message keys that actually exist. A normal MH.get_message() call invokes get_sequences(), so a tiny metadata file can cause a large allocation while loading an otherwise ordinary message.
Reproduction:
Please see the attached plain-text proof-of-concept, cpython-mh-sequence-repro.py. It creates a temporary MH mailbox containing one message and a small .mh_sequences file, then calls MH.get_message(1).
On macOS with Python 3.14.6:
$ python3 cpython-mh-sequence-repro.py --stop 5000000
python=3.14.6
stop=5000000 metadata_bytes=18
elapsed=0.259s maxrss=451297280 outcome=message_loaded
Negative control:
$ python3 cpython-mh-sequence-repro.py --stop 5
python=3.14.6
stop=5 metadata_bytes=12
elapsed=0.001s maxrss=23674880 outcome=message_loaded
The latest main ASAN build also reproduced the behavior with a one-million range: 18 bytes of metadata reached approximately 223 MB maximum RSS while loading the single message. No ASAN diagnostic occurred; this report is not claiming a memory-safety issue.
Root cause:
In Lib/mailbox.py, MH.get_sequences() effectively executes keys.update(range(start, stop + 1)) and only afterwards filters the result against all_keys. The final result only contains existing mailbox keys, so the declared interval does not need to be enumerated.
Suggested mitigation:
Intersect each declared range with all_keys instead of enumerating the interval, for example:
if start <= stop:
keys.update(key for key in all_keys if start <= key <= stop)
The fix should preserve existing malformed-input behavior, ordering, and empty-sequence removal, and should add a regression test using a very large range with a small mailbox.
I searched the CPython issue and pull-request tracker for get_sequences, .mh_sequences, keys.update(range, and MH mailbox DoS. I did not find a matching report. Existing MH issues concerning Claws Mail sequence-file formats, missing .mh_sequences files, and replacement data loss are different behaviors.
POC is here.
#!/usr/bin/env python3
"""Measure MH sequence-range materialization from a tiny mailbox file."""
from __future__ import annotations
import argparse
from pathlib import Path
import mailbox
import resource
import sys
import tempfile
import time
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--stop", type=int, default=1_000_000)
args = parser.parse_args()
with tempfile.TemporaryDirectory(prefix="cpython-mh-") as tmp:
root = Path(tmp)
(root / "1").write_bytes(b"Subject: canary\n\nbody\n")
(root / ".mh_sequences").write_text(
f"unseen: 1-{args.stop}\n", encoding="ascii"
)
box = mailbox.MH(root)
start = time.monotonic()
try:
box.get_message(1)
outcome = "message_loaded"
except BaseException as exc:
outcome = f"{type(exc).__name__}: {exc}"
elapsed = time.monotonic() - start
maxrss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
print(f"python={sys.version.split()[0]}")
print(f"stop={args.stop} metadata_bytes={(root / '.mh_sequences').stat().st_size}")
print(f"elapsed={elapsed:.3f}s maxrss={maxrss} outcome={outcome}")
return 1 if args.stop >= 1_000_000 else 0
if __name__ == "__main__":
raise SystemExit(main())
Has this already been discussed elsewhere?
No response given
Links to previous discussion of this feature:
No response
Linked PRs
- gh-156442
貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
研究方向
受影響的實作位於 Lib/mailbox.py,具體是 MH.get_sequences();從這裡開始,檢查現有的 mailbox 測試。新增一個使用超大範圍和小型 mailbox 的回歸測試,同時保留對格式錯誤輸入的處理行為、順序以及空序列移除行為。
由索引模型根據 Issue 內容生成。
評估
- 技術堆疊
- python
- 領域
- backend, security
- Issue 類型
- 缺陷
- 難度
- 3/5
- 預估耗時
- 1-2 天
- 活躍度
- 停滯
- 描述清晰度
- 描述清楚
- 新手友好度
- 35/100