python / python/cpython

Data race in list.sort() on no-gil build

Open
#154,756 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

interpreter-core topic-free-threading type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:
Summary

As reported in #153852 (TSAN0014), list.sort() writes each slot of the array with a plain, non-atomic store. When another thread concurrently accesses the same list, there is a data race between the in-place sort and the reader, which will be reported by TSAN.

How to reproduce

First we need to enable thread-sanitizer

  • ./configure --disable-gil --with-thread-sanitizer
  • TSAN_OPTIONS="halt_on_error=1 symbolize=1 history_size=4" ./python repro.py

Then run the following repro.py

import sys, threading

size, rounds, num_threads = 2000, 1500, 32
SCRAMBLED = sorted(range(size), key=lambda x: (x * 2654435761) & 0xFFFFFFFF)
global_list = list(SCRAMBLED)
enter, leave = threading.Barrier(num_threads + 1), threading.Barrier(num_threads + 1)

def reader():
    for _ in range(rounds):
        enter.wait()
        for _x in global_list:
            pass
        leave.wait()

def main_sorter():
    for _ in range(rounds):
        global_list[:] = SCRAMBLED
        enter.wait()
        global_list.sort()
        leave.wait()

ts = [threading.Thread(target=reader) for _ in range(num_threads)]
for t in ts: 
    t.start()
main_sorter()
for t in ts: 
    t.join()

Finally we could see the log from TSan

WARNING: ThreadSanitizer: data race (pid=3143552)
  Write of size 8 at 0xffffb6b3c018 by main thread:
    #0 binarysort Objects/listobject.c:1918 (python+0x1c64bc)
CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-154572

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running repro.py with the listed no-GIL and ThreadSanitizer configuration, then inspect Objects/listobject.c around binarysort at line 1918. Compare the race report with the linked gh-154572 work. Done means the reproduction no longer produces a ThreadSanitizer data-race report.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, python
Domain
operating-systems, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.