Skip to content

build(deps): bump urllib3 from 2.6.3 to 2.7.0#5

Open
dependabot[bot] wants to merge 1 commit into
masterfrom
dependabot/uv/urllib3-2.7.0
Open

build(deps): bump urllib3 from 2.6.3 to 2.7.0#5
dependabot[bot] wants to merge 1 commit into
masterfrom
dependabot/uv/urllib3-2.7.0

Conversation

@dependabot
Copy link
Copy Markdown

@dependabot dependabot Bot commented on behalf of github May 11, 2026

Bumps urllib3 from 2.6.3 to 2.7.0.

Release notes

Sourced from urllib3's releases.

2.7.0

🚀 urllib3 is fundraising for HTTP/2 support

urllib3 is raising ~$40,000 USD to release HTTP/2 support and ensure long-term sustainable maintenance of the project after a sharp decline in financial support. If your company or organization uses Python and would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and thousands of other projects please consider contributing financially to ensure HTTP/2 support is developed sustainably and maintained for the long-haul.

Thank you for your support.

Security

Addressed high-severity security issues. Impact was limited to specific use cases detailed in the accompanying advisories; overall user exposure was estimated to be marginal.

  • Decompression-bomb safeguards of the streaming API were bypassed:

    1. When HTTPResponse.drain_conn() was called after the response had been read and decompressed partially. (Reported by @​Cycloctane)
    2. During the second HTTPResponse.read(amt=N) or HTTPResponse.stream(amt=N) call when the response was decompressed using the official Brotli library. (Reported by @​kimkou2024)

    See GHSA-mf9v-mfxr-j63j for details.

  • HTTP pools created using ProxyManager.connection_from_url did not strip sensitive headers specified in Retry.remove_headers_on_redirect when redirecting to a different host. (GHSA-qccp-gfcp-xxvc reported by @​christos-spearbit)

Deprecations and Removals

  • Used FutureWarning instead of DeprecationWarning for better visibility of existing deprecation notices. Rescheduled the removal of deprecated features to version 3.0. (urllib3/urllib3#3763)
  • Removed support for end-of-life Python 3.9. (urllib3/urllib3#3720)
  • Removed support for end-of-life PyPy3.10. (urllib3/urllib3#4979)
  • Bumped the minimum supported pyOpenSSL version to 19.0.0. (urllib3/urllib3#3777)

Bugfixes

  • Fixed a bug where HTTPResponse.read(amt=None) was ignoring decompressed data buffered from previous partial reads. (urllib3/urllib3#3636)
  • Fixed a bug where HTTPResponse.read() could cache only part of the response after a partial read when cache_content=True. (urllib3/urllib3#4967)
  • Fixed HTTPResponse.stream() and HTTPResponse.read_chunked() to handle amt=0. (urllib3/urllib3#3793)
  • Updated _TYPE_BODY type alias to include missing Iterable[str], matching the documented and runtime behavior of chunked request bodies. (urllib3/urllib3#3798)
  • Fixed LocationParseError when paths resembling schemeless URIs were passed to HTTPConnectionPool.urlopen(). (urllib3/urllib3#3352)
  • Fixed BaseHTTPResponse.readinto() type annotation to accept memoryview in addition to bytearray, matching the io.RawIOBase.readinto contract and enabling use with io.BufferedReader without type errors. (urllib3/urllib3#3764)
Changelog

Sourced from urllib3's changelog.

2.7.0 (2026-05-07)

Security

Addressed high-severity security issues. Impact was limited to specific use cases detailed in the accompanying advisories; overall user exposure was estimated to be marginal.

  • Decompression-bomb safeguards of the streaming API were bypassed:

    1. When HTTPResponse.drain_conn() was called after the response had been read and decompressed partially.
    2. During the second HTTPResponse.read(amt=N) or HTTPResponse.stream(amt=N) call when the response was decompressed using the official Brotli <https://pypi.org/project/brotli/>__ library.

    See GHSA-mf9v-mfxr-j63j <https://github.com/urllib3/urllib3/security/advisories/GHSA-mf9v-mfxr-j63j>__ for details.

  • HTTP pools created using ProxyManager.connection_from_url did not strip sensitive headers specified in Retry.remove_headers_on_redirect when redirecting to a different host. (GHSA-qccp-gfcp-xxvc <https://github.com/urllib3/urllib3/security/advisories/GHSA-qccp-gfcp-xxvc>__)

Deprecations and Removals

  • Used FutureWarning instead of DeprecationWarning for better visibility of existing deprecation notices. Rescheduled the removal of deprecated features to version 3.0. ([#3763](https://github.com/urllib3/urllib3/issues/3763) <https://github.com/urllib3/urllib3/issues/3763>__)
  • Removed support for end-of-life Python 3.9. ([#3720](https://github.com/urllib3/urllib3/issues/3720) <https://github.com/urllib3/urllib3/issues/3720>__)
  • Removed support for end-of-life PyPy3.10. ([#4979](https://github.com/urllib3/urllib3/issues/4979) <https://github.com/urllib3/urllib3/issues/4979>__)
  • Bumped the minimum supported pyOpenSSL version to 19.0.0. ([#3777](https://github.com/urllib3/urllib3/issues/3777) <https://github.com/urllib3/urllib3/issues/3777>__)

Bugfixes

  • Fixed a bug where HTTPResponse.read(amt=None) was ignoring decompressed data buffered from previous partial reads. ([#3636](https://github.com/urllib3/urllib3/issues/3636) <https://github.com/urllib3/urllib3/issues/3636>__)
  • Fixed a bug where HTTPResponse.read() could cache only part of the response after a partial read when cache_content=True.

... (truncated)

Commits

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    You can disable automated security fix PRs for this repo from the Security Alerts page.

Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](urllib3/urllib3@2.6.3...2.7.0)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.7.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file python:uv Pull requests that update python:uv code labels May 11, 2026
dangyajun pushed a commit to dangyajun/lib-common that referenced this pull request May 13, 2026
Because our el_wake API didn't use explicit memory barriers between the
read/write synchronizations points TSAN reported data race such as the
one below:

WARNING: ThreadSanitizer: data race
  Write of size 8 by main thread:
    #0 close <null>
    Intersec#1 el_fd_unregister ./src/core/el-epoll.in.c:109:13
    Intersec#2 el_wake_unregister ./src/core/el.blk:1645:9
    Intersec#3 el_unregister ./src/core/el.blk:1894:36
    Intersec#4 dns_resolv_ctx_wipe ./src/net/addr.blk:239:5
    Intersec#5 dns_resolv_ctx_delete ./src/net/addr.blk:245:1
    Intersec#6 ____addr_info_async_block_invoke ./src/net/addr.blk:297:9
    #7 el_wake_on_event ./src/core/el.blk:1607:9
    #8 el_fd_fire ./src/core/el.blk:1307:13
    #9 el_fds_loop ./src/core/el.blk:1461:17
    #10 z_connect_ics_from_addr_and_wait ./tests/zchk-iop-rpc.c:316:13
    #11 __z_iop_rpc_block_invoke_6 ./tests/zchk-iop-rpc.c:608:9
    #12 z_iop_rpc ./tests/zchk-iop-rpc.c:640:7
    #13 z_run ./src/core/z.blk:1545:9
    #14 main ./tests/zchk.c:1206:12

  Previous read of size 8 by thread T8:
    #0 write <null>
    Intersec#1 el_wake_fire ./src/core/el.blk:1660:5
    Intersec#2 ____addr_info_async_block_invoke_2 ./src/net/addr.blk:331:9
    Intersec#3 job_run ./src/core/thr-job.blk:281:9
    Intersec#4 thr_run_deque_entry ./src/core/thr-job.blk:381:12
    Intersec#5 thr_job_try_steal ./src/core/thr-job.blk:473:20
    Intersec#6 thr_job_steal ./src/core/thr-job.blk:513:15
    #7 thr_job_main ./src/core/thr-job.blk:874:13
    #8 thr_hooks_wrapper ./src/core/thr.c:89:11

Indeed even if the eventfd() API ensures the thread safety of the
write/read sequence we still have to ensure no more usage of the fd
itself is possible before closing it.

Thus we add an explicit memory barrier in the form of an atomic counter.

Change-Id: I519147361d912e155341eec76f1fb4018699235c
Priv-Id: b1aef4b55b87f433f478950e7b79207846a0f623
dangyajun pushed a commit to dangyajun/lib-common that referenced this pull request May 13, 2026
thr_queue_drain() seemed to assume that no other thread is running the
queue when used and thus started with an `atomic_store(&q->running_on,
id)` without taking care of the current `running_on` value.

But when destroying a queue (`thr_queue_destroy`) TSAN reported this
race:

WARNING: ThreadSanitizer: data race
  Write of size 8  by main thread:
    #0 free <null>
    Intersec#1 libc_free ./src/core/mem.blk:140:9
    Intersec#2 mp_ifree ./src/core/mem.blk:351:5
    Intersec#3 thr_queue_delete ./src/core/thr-job.blk:574:1
    Intersec#4 thr_queue_drain ./src/core/thr-job.blk:618:9
    Intersec#5 thr_queue_sync ./src/core/thr-job.blk:705:13
    Intersec#6 thr_queue_destroy ./src/core/thr-job.blk:730:9
    #7 test_queue ./tests/zchk-thrjob.blk:381:9
    #8 __z_thrjobs_block_invoke_4 ./tests/zchk-thrjob.blk:647:9
    #9 z_thrjobs ./tests/zchk-thrjob.blk:648:7
    #10 z_run ./src/core/z.blk:1545:9
    #11 main ./tests/zchk.c:1206:12

  Previous atomic write of size 8 at 0x7210000001e0 by thread T28:
    #0 thr_queue_drain ./src/core/thr-job.blk:614:5
    Intersec#1 thr_queue_run ./src/core/thr-job.blk:624:5
    Intersec#2 job_run ./src/core/thr-job.blk:285:9
    Intersec#3 thr_run_deque_entry ./src/core/thr-job.blk:381:12
    Intersec#4 thr_job_try_steal ./src/core/thr-job.blk:473:20
    Intersec#5 thr_job_steal ./src/core/thr-job.blk:513:15
    Intersec#6 thr_job_main ./src/core/thr-job.blk:884:25
    #7 thr_hooks_wrapper ./src/core/thr.c:89:11

Indeed when finishing to drain the queue, another would finished by:

    do {
        […]
    } while (!mpsc_queue_drain_end(&it, &thr_qnode_destroy));
    atomic_compare_exchange_strong(&q->running_on, &id,
                                   THR_QUEUE_NOT_RUNNING);

So after removing the last element of the queue (`mpsc_queue_drain_end`)
the queue's `running_on` is reset to THR_QUEUE_NOT_RUNNING.

But when destroying, the sole condition to immediately drain the queue
is:

    if (mpsc_queue_push(&q->q, &n->qnode)) {
        thr_queue_drain(q);

So the race is obvious here, as soon as the queued is emptied, the
destroying thread could already be freeing the queue while the previous
thread could still be trying to reset `q->running_on`.

To fix we now actually wait for the queue to be release before draining
it again.

Change-Id: I159d6426ec7ace01d0e7aaf685a7e32a6bf31749
Priv-Id: 07021192cda3217760c2ba85fc0fa12af17ef20f
nicopauss added a commit that referenced this pull request Jun 4, 2026
ThreadSanitizer reported a data race on _G.all_pools between:

 - Writer: a worker thread exiting, via thr_detach ->
   log_shutdown_thread -> mem_stack_pool_wipe -> mem_pool_wipe, which
   calls dlist_remove on _G.all_pools while holding _G.all_pools_lock.

 - Reader: the main thread, running the post-fork child handler
   mem_stack_fix_all_pools_at_fork, which iterated _G.all_pools
   without acquiring _G.all_pools_lock.

Even though the worker thread had finished before fork() was called,
there was no happens-before edge between its last dlist_remove and
the main thread's atfork-child iteration (no join, and no matching
lock acquisition), which TSan correctly flagged.

Register prepare/parent handlers that take/release _G.all_pools_lock
around fork(). This establishes the happens-before edge through the
spinlock, following the standard pthread_atfork pattern. The child
handler now unlocks the spinlock after iterating.

```
  ==================
  WARNING: ThreadSanitizer: data race (pid=675506)
    Read of size 8 at 0x7ffff7b8f9c0 by main thread:
      #0 mem_stack_fix_all_pools_at_fork src/core/mem-stack.c:801:5 (zchk+0x11847af) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #1 __run_postfork_handlers posix/register-atfork.c:187:9 (libc.so.6+0x10e4aa) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
      #2 ifork src/core/unix.blk:970:11 (zchk+0x11df381) (BuildId: fff1872839743e3e2098a0b242dcad9a488e9434)
      #3 thr_job_fork ./lib-common/core/thr-job.h:379:17 (zchk+0x5dcbf4) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #4 _z_group_run src/core/z.blk:1043:11 (zchk+0x5d6abe) (BuildId: fff1872839743e3e2098a0b242dcad9a488e9434)
      #5 _z_group_process src/core/z.blk:1103:16 (zchk+0x5d6825) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #6 z_iop_http2 tests/zchk-iop-http.c:382:1 (zchk+0x932e5d) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #7 z_run src/core/z.blk:1545:9 (zchk+0x5d92c8) (BuildId: fff1872839743e3e2098a0b242dcad9a488e9434)
      #8 main tests/zchk.c:1206:12 (zchk+0x706599) (BuildId: fff1872839743e3e2098a0b242dcad9a488e9434)

    Previous write of size 8 at 0x7ffff7b8f9c0 by thread T23:
      #0 __dlist_remove ./lib-common/container-dlist.h:160:16 (zchk+0x11860f2) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #1 dlist_remove ./lib-common/container-dlist.h:205:5 (zchk+0x1182926) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #2 mem_pool_wipe src/core/mem-priv.h:67:9 (zchk+0x1183657) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #3 mem_stack_pool_wipe src/core/mem-stack.c:611:5 (zchk+0x1183446) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #4 log_shutdown_thread src/core/log.c:1157:9 (zchk+0x1173c74) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #5 thr_detach src/core/thr.c:50:9 (zchk+0x11db3f5) (BuildId: fff1872839743e3e2098a0b242dcad9a488e9434)
      #6 thr_hooks_wrapper src/core/thr.c:90:5 (zchk+0x11db90c) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)

    Location is TLS of main thread.

    Thread T23 (tid=675323, finished) created by main thread at:
      #0 pthread_create <null> (zchk+0x40a1ff) (BuildId: fff1872839743e3e2098a0b242dcad9a488e9434)
      #1 thr_create src/core/thr.c:111:11 (zchk+0x11db717) (BuildId: fff1872839743e3e2098a0b242dcad9a488e9434)
      #2 thr_fork_threads src/core/thr-job.blk:969:21 (zchk+0x11d6d24) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #3 thr_initialize src/core/thr-job.blk:1140:5 (zchk+0x11d75a5) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #4 module_require_internal src/core/module.c:323:9 (zchk+0x118e8ed) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #5 module_require src/core/module.c:335:5 (zchk+0x118d609) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #6 z_prometheus_client tests/zchk-prometheus.blk:211:5 (zchk+0x9b213e) (BuildId:
  fff1872839743e3e2098a0b242dcad9a488e9434)
      #7 z_run src/core/z.blk:1545:9 (zchk+0x5d92c8) (BuildId: fff1872839743e3e2098a0b242dcad9a488e9434)
      #8 main tests/zchk.c:1206:12 (zchk+0x706599) (BuildId: fff1872839743e3e2098a0b242dcad9a488e9434)

  SUMMARY: ThreadSanitizer: data race src/core/mem-stack.c:801:5 in mem_stack_fix_all_pools_at_fork
  ==================
```

Change-Id: Icb36262c282b990bfe6aca9f4a08bff8ac65b033
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Priv-Id: 22f173e9610d601220f1d3b17de5c417b512d1da
nicopauss added a commit that referenced this pull request Jun 4, 2026
Workers set `alive=false` inside thr_info_cleanup (from pthread_cleanup_pop)
before thr_hooks_wrapper calls thr_detach, which runs per-thread exit hooks
such as log_shutdown_thread -> mem_stack_pool_wipe -> mp_ifree (reads
mem_pool_libc.free). Main's busy-wait in thr_shutdown can therefore observe
alive=false and resume while the worker still accesses globals in thr_detach,
with no happens-before.

Move the alive store into a dedicated thr_mark_dead exit hook registered at
the tail of thr_hooks_g.exit_cbs so it runs after every other per-thread
exit hook. The atomic release store now happens-before the main thread's
acquire load, covering all tear-down work.

```
  ==================
  WARNING: ThreadSanitizer: data race (pid=738787)
    Write of size 8 at 0x555556b4c4e0 by main thread:
      #0 __z_str_block_invoke_2 tests/zchk-str.c:107:28 (zchk+0x9cf192) (BuildId:
  dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #1 z_str tests/zchk-str.c:116:7 (zchk+0x9cb5ca) (BuildId: dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #2 z_run src/core/z.blk:1545:9 (zchk+0x5d92c8) (BuildId: dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #3 main tests/zchk.c:1206:12 (zchk+0x706599) (BuildId: dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)

    Previous read of size 8 at 0x555556b4c4e0 by thread T4:
      #0 mp_ifree src/core/mem.blk:351:11 (zchk+0x1189890) (BuildId: dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #1 mem_pool_wipe src/core/mem-priv.h:70:5 (zchk+0x1182f03) (BuildId:
  dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #2 mem_stack_pool_wipe src/core/mem-stack.c:611:5 (zchk+0x1182cb6) (BuildId:
  dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #3 log_shutdown_thread src/core/log.c:1157:9 (zchk+0x11734e4) (BuildId:
  dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #4 thr_detach src/core/thr.c:50:9 (zchk+0x11dad05) (BuildId: dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #5 thr_hooks_wrapper src/core/thr.c:90:5 (zchk+0x11db21c) (BuildId:
  dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)

    Location is global 'mem_pool_libc' of size 64 at 0x555556b4c4c0 (zchk+0x15f84e0)

    Thread T4 (tid=738600, finished) created by main thread at:
      #0 pthread_create <null> (zchk+0x40a1ff) (BuildId: dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #1 thr_create src/core/thr.c:111:11 (zchk+0x11db027) (BuildId: dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #2 thr_fork_threads src/core/thr-job.blk:969:21 (zchk+0x11d6634) (BuildId:
  dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #3 thr_initialize src/core/thr-job.blk:1140:5 (zchk+0x11d6eb5) (BuildId:
  dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #4 module_require_internal src/core/module.c:323:9 (zchk+0x118e1fd) (BuildId:
  dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #5 module_require src/core/module.c:335:5 (zchk+0x118cf19) (BuildId:
  dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #6 z_thrjobs tests/zchk-thrjob.blk:598:5 (zchk+0xa7c0e2) (BuildId:
  dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #7 z_run src/core/z.blk:1545:9 (zchk+0x5d92c8) (BuildId: dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)
      #8 main tests/zchk.c:1206:12 (zchk+0x706599) (BuildId: dfdf1f6253fa8ff7dbda901d2ec5a372fc4e3092)

  SUMMARY: ThreadSanitizer: data race tests/zchk-str.c:107:28 in __z_str_block_invoke_2
  ==================
```

Change-Id: Ie58fc71343a5d09374d7e85e8651df4a39d790f0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Priv-Id: 06e9bf4d91b91b670766cab1818facf14a6c3fb4
nicopauss added a commit that referenced this pull request Jun 4, 2026
ThreadSanitizer reported a data race on memory shared between the
worker thread (writing via el_wake_fire) and the main thread (reading
in the el_wake_register_blk callback after el_wake_on_event).

The kernel's eventfd implementation is internally protected by a
spinlock (fs/eventfd.c): write() atomically increments the counter
under the lock, wakes up pollers, and releases the lock; read()
acquires the same lock to read the counter. This guarantees that
write() data is fully committed before read() returns — the ordering
is real, but invisible to ThreadSanitizer which has no visibility into
kernel-side synchronization.

The previous approach (atomic_fetch_add/release after write, paired
with atomic_fetch_sub/acquire after read) did not satisfy TSan: from
its point of view, the acquire RMW in el_wake_on_event could execute
before the release RMW in el_wake_fire (since the kernel eventfd
ordering between write() and read() is opaque), so the acquire might
observe the initial value and no release sequence is established.

Instead of adding a real spinlock around write() (which adds runtime
overhead for a false positive), use TSan's annotation API to model the
kernel-provided ordering directly:

  - __tsan_release(el) in el_wake_fire(), before write(): tells TSan
    that all data written by the caller up to this point is published.
    This must be placed before write() so that the annotation's vector
    clock is stored before the kernel wakes the reader.

  - __tsan_acquire(wake) in el_wake_on_event(), after read(): tells TSan
    to import the vector clock, establishing happens-before with the
    release — covering the caller's data.

These annotations compile to nothing when TSan is not enabled — zero
runtime cost in production.

A second race exists between el_wake_fire() accessing the ev_t struct
(reading event_fd, calling write(fd)) and el_wake_unregister()
destroying it (close(fd), el_destroy()). The tsan annotations above do
not cover this because fire's struct accesses happen after
tsan_release. This is handled by the wakeup_count atomic:

  - el_wake_fire(): atomic_fetch_add(release) after write() returns.
  - el_wake_unregister(): spin-waits with atomic_load(acquire) until
    count >= 0, meaning all in-flight fire() calls have completed.

The release ordering on fetch_add ensures that when the acquire-load
in unregister observes count >= 0, TSan sees a happens-before edge
covering fire's struct accesses and write(fd) call. The C11 release
sequence (through any intervening RMW by fetch_sub in on_event)
guarantees this works regardless of which thread wrote the value the
load observes.

Note: the tsan annotations cannot be replaced by making fetch_sub use
acquire ordering. TSan might schedule the sub before the add (since it
cannot see the kernel eventfd ordering), in which case the sub reads
the initial value and no release-acquire pair is formed.

Reproduced using:

  TSAN_OPTIONS=die_after_fork=0 CCACHE_DISABLE=1 \
    randbug ./tests/zchk --no-fork iop_rpc

```
  ==================
  WARNING: ThreadSanitizer: data race (pid=803128)
    Read of size 8 at 0x723c000014e8 by main thread:
      #0 __tsan_memcpy <null> (zchk+0x40612f) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)
      #1 ____ic_resolve_remote_addr_block_invoke src/iop/rpc-channel.blk:3402:27 (zchk+0x6bb3b3) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #2 ____addr_info_async_block_invoke src/net/addr.blk:292:17 (zchk+0x5ef75f) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #3 el_wake_on_event src/core/el.blk:1614:9 (zchk+0x115eddc) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #4 el_fd_fire src/core/el.blk:1314:13 (zchk+0x115d7e8) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)
      #5 el_fds_loop src/core/el.blk:1468:17 (zchk+0x115d0cf) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #6 z_connect_ics_from_addr_and_wait tests/zchk-iop-rpc.c:316:13 (zchk+0x925522) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #7 __z_iop_rpc_block_invoke_6 tests/zchk-iop-rpc.c:608:9 (zchk+0x9249d4) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #8 z_iop_rpc tests/zchk-iop-rpc.c:640:7 (zchk+0x918b71) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #9 z_run src/core/z.blk:1545:9 (zchk+0x5d92c8) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)
      #10 main tests/zchk.c:1206:12 (zchk+0x706599) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)

    Previous write of size 8 at 0x723c000014e8 by thread T10:
      #0 __tsan_memcpy <null> (zchk+0x40612f) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)
      #1 process_getaddrinfo_result src/net/addr.blk:269:9 (zchk+0x5ecfda) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #2 ____addr_info_async_block_invoke_2 src/net/addr.blk:313:22 (zchk+0x5efab7) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #3 job_run src/core/thr-job.blk:288:9 (zchk+0x11d5071) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)
  ──── (76 lines hidden) ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  #10 ic_connect src/iop/rpc-channel.blk:3493:12 (zchk+0x6a816c) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #11 z_connect_ics_from_addr_and_wait tests/zchk-iop-rpc.c:291:5 (zchk+0x92516c) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #12 __z_iop_rpc_block_invoke_6 tests/zchk-iop-rpc.c:608:9 (zchk+0x9249d4) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #13 z_iop_rpc tests/zchk-iop-rpc.c:640:7 (zchk+0x918b71) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #14 z_run src/core/z.blk:1545:9 (zchk+0x5d92c8) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)
      #15 main tests/zchk.c:1206:12 (zchk+0x706599) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)

    Thread T10 (tid=803198, running) created by main thread at:
      #0 pthread_create <null> (zchk+0x40a1ff) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)
      #1 thr_create src/core/thr.c:111:11 (zchk+0x11db1d7) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)
      #2 thr_fork_threads src/core/thr-job.blk:991:21 (zchk+0x11d66d4) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #3 thr_initialize src/core/thr-job.blk:1162:5 (zchk+0x11d6f55) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #4 module_require_internal src/core/module.c:323:9 (zchk+0x118e1fd) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #5 module_require src/core/module.c:335:5 (zchk+0x118cf19) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #6 __z_iop_rpc_block_invoke_6 tests/zchk-iop-rpc.c:592:9 (zchk+0x924889) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #7 z_iop_rpc tests/zchk-iop-rpc.c:640:7 (zchk+0x918b71) (BuildId:
  4beb3593909ab979e48553f492bf24092dff965c)
      #8 z_run src/core/z.blk:1545:9 (zchk+0x5d92c8) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)
      #9 main tests/zchk.c:1206:12 (zchk+0x706599) (BuildId: 4beb3593909ab979e48553f492bf24092dff965c)

  SUMMARY: ThreadSanitizer: data race src/core/str-l.c:35:9 in mp_lstr_copy_
  ==================
```

Change-Id: Ia719c6da893ea66949e821facb48ccb6dd3158c6
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Priv-Id: 7fe1c0363a9ef55a225a1cc22f44ce609fa8d220
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file python:uv Pull requests that update python:uv code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants