thread safety issues in unicodeobject.c
まだ誰も着手していません。
- 主要言語
- Python
- スター
- 77.2k
- フォーク
- 35.9k
- PR マージ指標
- PR 指標を取得中
説明
Bug report
Bug description:
Running a TSAN analyser exposed some issues in unicodeobject.c
it->it_index++ is accessed and incremented raw in unicode_ascii_iter_next.
This can result in a crash with the debug build with a small test harness of multiple threads iterating on the same string.
import os
import sys
import threading
ROUNDS = int(sys.argv[1]) if len(sys.argv) > 1 else 4000
LENGTH = 2000
NTHREADS = 8
CHAR = "A" # the ONLY character that a correct iterator may ever return
# NOEXHAUST>0: each thread stops after that many items, so the iterator is
# never drained to the end -> isolates the it_index race from the it_seq
# double-DECREF-on-exhaustion bug.
NOEXHAUST = int(os.environ.get("NOEXHAUST", "0"))
def drain(it, out, anomalies, barrier, cap):
barrier.wait()
n = 0
while True:
try:
c = next(it)
except StopIteration:
return
except Exception as e: # a corrupted object can raise on use
anomalies.append(("exception", repr(e)))
return
# The corruption checks -- impossible for a correct implementation:
if type(c) is not str or len(c) != 1 or c != CHAR:
anomalies.append(("bad-char", repr(c)))
out.append(1)
n += 1
if cap and n >= cap: # stop BEFORE exhaustion to isolate the it_seq bug
return
def main():
total_seen = 0
total_expected = 0
dup_skip_rounds = 0
anomalies = []
for r in range(ROUNDS):
s = CHAR * LENGTH
it = iter(s)
barrier = threading.Barrier(NTHREADS)
outs = [[] for _ in range(NTHREADS)]
threads = [
threading.Thread(target=drain,
args=(it, outs[i], anomalies, barrier, NOEXHAUST))
for i in range(NTHREADS)
]
for t in threads:
t.start()
for t in threads:
t.join()
seen = sum(len(o) for o in outs)
total_seen += seen
total_expected += LENGTH
if seen != LENGTH:
dup_skip_rounds += 1
if anomalies:
print(f"[round {r}] CORRUPTION: {anomalies[:5]}")
break
print("=" * 60)
print(f"rounds run : {r + 1}")
print(f"chars expected total : {total_expected}")
print(f"chars actually seen : {total_seen} "
f"(delta {total_seen - total_expected:+d} = dup/skip)")
print(f"rounds with dup/skip : {dup_skip_rounds}")
print(f"OOB corruptions found : {len(anomalies)}")
if anomalies:
print(f" samples: {anomalies[:10]}")
print("--> proves the out-of-bounds read: a char not in the source.")
else:
print("--> no OOB char observed this run (still UB; TSan flags it "
"deterministically).")
return 1 if anomalies else 0
if __name__ == "__main__":
sys.exit(main())
https://github.com/python/cpython/blob/main/Objects/unicodeobject.c#L14979-L14983
With the small test harness above
Stack (most recent call first):
File "/Users/johng/repos/cpython/str_race_impact.py", line 68 in main
File "/Users/johng/repos/cpython/str_race_impact.py", line 109 in
[1] 59403 abort PYTHON_GIL=0 ./python.exe str_race_impact.py
CPython versions tested on:
CPython main branch
Operating systems tested on:
macOS
Linked PRs
- gh-155873
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
Objects/unicodeobject.c の 14979-14983 行付近から始め、提供されたマルチスレッドハーネス str_race_impact.py を debug または TSAN ビルドで使用して報告を再現してください。共有文字列イテレーターで報告された race、クラッシュ、文字の重複またはスキップ、破損した値が発生しなくなれば完了です。gh-155873 はすでに issue にリンクされています。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- c, python
- 領域
- backend
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 停滞
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 25/100