Skip to content

Free threading: Data race on a socket's timeout #153935

Description

@deadlovelll

Bug report

Bug description:

Created from gh-153852 TSAN-0035

sock->sock_timeout is a plain PyTime_t and have no synchronization. On the free-threaded build, using the same socket from multiple threads causes a c-level data race , reported by TSAN.

Repro:

import socket
import threading

s = socket.socket()
barrier = threading.Barrier(2)
N = 1000


def reader():
    barrier.wait()
    for _ in range(N):
        s.gettimeout()


def writer():
    barrier.wait()
    for _ in range(N):
        s.settimeout(1.0)


threads = [threading.Thread(target=reader), threading.Thread(target=writer)]
for t in threads:
    t.start()
for t in threads:
    t.join()
s.close()
print("done")

Output:

python.exe(20351,0x204cf7840) malloc: nano zone abandoned due to inability to reserve vm space.
==================
WARNING: ThreadSanitizer: data race (pid=20351)
  Read of size 8 at 0x00010e600198 by thread T1:
    #0 sock_gettimeout_method socketmodule.c:3320 (_socket.cpython-316t-darwin.so:arm64+0x84b8)
    #1 method_vectorcall_NOARGS descrobject.c:448 (python.exe:arm64+0x1000a0c94)
    #2 PyObject_Vectorcall call.c:327 (python.exe:arm64+0x100088b88)
    #3 _Py_VectorCallInstrumentation_StackRefSteal ceval.c:768 (python.exe:arm64+0x100290b68)
    #4 _PyEval_EvalFrameDefault generated_cases.c.h:1906 (python.exe:arm64+0x100295bc8)
    #5 _PyEval_Vector ceval.c:2172 (python.exe:arm64+0x10029000c)
    #6 _PyFunction_Vectorcall call.c (python.exe:arm64+0x1000891e8)
    #7 _PyObject_VectorcallPrepend call.c:855 (python.exe:arm64+0x10008a984)
    #8 method_vectorcall classobject.c:55 (python.exe:arm64+0x10008de88)
    #9 context_run context.c:731 (python.exe:arm64+0x1002dff38)
    #10 method_vectorcall_FASTCALL_KEYWORDS descrobject.c:421 (python.exe:arm64+0x1000a0b7c)
    #11 PyObject_Vectorcall call.c:327 (python.exe:arm64+0x100088b88)
    #12 _Py_VectorCallInstrumentation_StackRefSteal ceval.c:768 (python.exe:arm64+0x100290b68)
    #13 _PyEval_EvalFrameDefault generated_cases.c.h:1906 (python.exe:arm64+0x100295bc8)
    #14 _PyEval_Vector ceval.c:2172 (python.exe:arm64+0x10029000c)
    #15 _PyFunction_Vectorcall call.c (python.exe:arm64+0x1000891e8)
    #16 _PyObject_VectorcallPrepend call.c:855 (python.exe:arm64+0x10008a984)
    #17 method_vectorcall classobject.c:55 (python.exe:arm64+0x10008de88)
    #18 _PyObject_Call call.c:348 (python.exe:arm64+0x100088e48)
    #19 PyObject_Call call.c:373 (python.exe:arm64+0x100088ec0)
    #20 thread_run _threadmodule.c:388 (python.exe:arm64+0x1004582e0)
    #21 pythread_wrapper thread_pthread.h:234 (python.exe:arm64+0x10038fe08)

  Previous write of size 8 at 0x00010e600198 by thread T2:
    #0 sock_settimeout socketmodule.c:3264 (_socket.cpython-316t-darwin.so:arm64+0x83fc)
    #1 method_vectorcall_O descrobject.c:476 (python.exe:arm64+0x1000a0e38)
    #2 PyObject_Vectorcall call.c:327 (python.exe:arm64+0x100088b88)
    #3 _Py_VectorCallInstrumentation_StackRefSteal ceval.c:768 (python.exe:arm64+0x100290b68)
    #4 _PyEval_EvalFrameDefault generated_cases.c.h:1906 (python.exe:arm64+0x100295bc8)
    #5 _PyEval_Vector ceval.c:2172 (python.exe:arm64+0x10029000c)
    #6 _PyFunction_Vectorcall call.c (python.exe:arm64+0x1000891e8)
    #7 _PyObject_VectorcallPrepend call.c:855 (python.exe:arm64+0x10008a984)
    #8 method_vectorcall classobject.c:55 (python.exe:arm64+0x10008de88)
    #9 context_run context.c:731 (python.exe:arm64+0x1002dff38)
    #10 method_vectorcall_FASTCALL_KEYWORDS descrobject.c:421 (python.exe:arm64+0x1000a0b7c)
    #11 PyObject_Vectorcall call.c:327 (python.exe:arm64+0x100088b88)
    #12 _Py_VectorCallInstrumentation_StackRefSteal ceval.c:768 (python.exe:arm64+0x100290b68)
    #13 _PyEval_EvalFrameDefault generated_cases.c.h:1906 (python.exe:arm64+0x100295bc8)
    #14 _PyEval_Vector ceval.c:2172 (python.exe:arm64+0x10029000c)
    #15 _PyFunction_Vectorcall call.c (python.exe:arm64+0x1000891e8)
    #16 _PyObject_VectorcallPrepend call.c:855 (python.exe:arm64+0x10008a984)
    #17 method_vectorcall classobject.c:55 (python.exe:arm64+0x10008de88)
    #18 _PyObject_Call call.c:348 (python.exe:arm64+0x100088e48)
    #19 PyObject_Call call.c:373 (python.exe:arm64+0x100088ec0)
    #20 thread_run _threadmodule.c:388 (python.exe:arm64+0x1004582e0)
    #21 pythread_wrapper thread_pthread.h:234 (python.exe:arm64+0x10038fe08)

  Thread T1 (tid=28859963, running) created by main thread at:
    #0 pthread_create <null>:126882288 (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x309d8)
    #1 do_start_joinable_thread thread_pthread.h:281 (python.exe:arm64+0x10038f000)
    #2 PyThread_start_joinable_thread thread_pthread.h:323 (python.exe:arm64+0x10038ee38)
    #3 ThreadHandle_start _threadmodule.c:475 (python.exe:arm64+0x10045809c)
    #4 do_start_new_thread _threadmodule.c:1919 (python.exe:arm64+0x100457b7c)
    #5 thread_PyThread_start_joinable_thread _threadmodule.c:2042 (python.exe:arm64+0x100456ae0)
    #6 cfunction_call methodobject.c:564 (python.exe:arm64+0x1001310f4)
    #7 _PyObject_MakeTpCall call.c:242 (python.exe:arm64+0x100087f70)
    #8 PyObject_Vectorcall call.c:327 (python.exe:arm64+0x100088c28)
    #9 _Py_VectorCallInstrumentation_StackRefSteal ceval.c:768 (python.exe:arm64+0x100290b68)
    #10 _PyEval_EvalFrameDefault generated_cases.c.h:3474 (python.exe:arm64+0x100298e58)
    #11 PyEval_EvalCode ceval.c:679 (python.exe:arm64+0x10028fb74)
    #12 run_eval_code_obj pythonrun.c:1406 (python.exe:arm64+0x10036aea4)
    #13 run_mod pythonrun.c:1509 (python.exe:arm64+0x10036abd4)
    #14 _PyRun_SimpleFile pythonrun.c:544 (python.exe:arm64+0x100365d88)
    #15 _PyRun_AnyFile pythonrun.c:92 (python.exe:arm64+0x100365534)
    #16 Py_RunMain main.c:891 (python.exe:arm64+0x1003a7aec)
    #17 pymain_main main.c:921 (python.exe:arm64+0x1003a7f3c)
    #18 Py_BytesMain main.c:945 (python.exe:arm64+0x1003a8010)
    #19 main python.c:15 (python.exe:arm64+0x1000032ac)

  Thread T2 (tid=28859964, running) created by main thread at:
    #0 pthread_create <null>:126884528 (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x309d8)
    #1 do_start_joinable_thread thread_pthread.h:281 (python.exe:arm64+0x10038f000)
    #2 PyThread_start_joinable_thread thread_pthread.h:323 (python.exe:arm64+0x10038ee38)
    #3 ThreadHandle_start _threadmodule.c:475 (python.exe:arm64+0x10045809c)
    #4 do_start_new_thread _threadmodule.c:1919 (python.exe:arm64+0x100457b7c)
    #5 thread_PyThread_start_joinable_thread _threadmodule.c:2042 (python.exe:arm64+0x100456ae0)
    #6 cfunction_call methodobject.c:564 (python.exe:arm64+0x1001310f4)
    #7 _PyObject_MakeTpCall call.c:242 (python.exe:arm64+0x100087f70)
    #8 PyObject_Vectorcall call.c:327 (python.exe:arm64+0x100088c28)
    #9 _Py_VectorCall_StackRefSteal ceval.c:726 (python.exe:arm64+0x1002903e4)
    #10 _PyEval_EvalFrameDefault generated_cases.c.h (python.exe:arm64+0x1002995c4)
    #11 PyEval_EvalCode ceval.c:679 (python.exe:arm64+0x10028fb74)
    #12 run_eval_code_obj pythonrun.c:1406 (python.exe:arm64+0x10036aea4)
    #13 run_mod pythonrun.c:1509 (python.exe:arm64+0x10036abd4)
    #14 _PyRun_SimpleFile pythonrun.c:544 (python.exe:arm64+0x100365d88)
    #15 _PyRun_AnyFile pythonrun.c:92 (python.exe:arm64+0x100365534)
    #16 Py_RunMain main.c:891 (python.exe:arm64+0x1003a7aec)
    #17 pymain_main main.c:921 (python.exe:arm64+0x1003a7f3c)
    #18 Py_BytesMain main.c:945 (python.exe:arm64+0x1003a8010)
    #19 main python.c:15 (python.exe:arm64+0x1000032ac)

SUMMARY: ThreadSanitizer: data race socketmodule.c:3320 in sock_gettimeout_method
==================
done
ThreadSanitizer: reported 1 warnings
[1]    20351 abort      ./python.exe repro_sock_timeout_race.py

Proposed fix:

Wrap s->sock_timeout with _Py_atomic_load_int64_relaxed

I have a fix ready

CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.14bugs and security fixes3.15pre-release feature fixes, bugs and security fixes3.16new features, bugs and security fixesextension-modulesC modules in the Modules dirtopic-free-threadingtype-bugAn unexpected behavior, bug, or error

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions