CI: UBSan runs GIL-only and never against the JIT; two configurations already shipped by distros are unsanitized
Nessuno ha ancora preso questa issue.
- Lingua principale
- Python
- Stelle
- 77.2k
- Fork
- 35.9k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
Feature or enhancement
Proposal
The UBSan job in .github/workflows/build.yml runs against exactly one configuration: GIL-enabled, no JIT. Two configurations that CI already builds — and that distributions already ship — are never sanitized. Both are cheap to add, and I have measured what each would have caught.
1. UBSan × free-threading: true
The sanitizer matrix pairs TSan with free-threading: {false, true} but UBSan only with false:
free-threading:
- false
- true
sanitizer:
- TSan
include:
- check-name: Undefined behavior
sanitizer: UBSan
free-threading: false
So files that only compile under Py_GIL_DISABLED are never built under UBSan. That is not a hypothetical gap: Python/uniqueid.c:185 shifted a negative per-thread refcount delta left, which is UB, and it fired from interpreter startup and from every GC cycle. Over the full suite with all suppressions disabled:
| UB reports | test failures | failing files | |
|---|---|---|---|
| before | 1762 | 529 | 61 |
| after #154915 | 0 | 0 | 0 |
Once #154915 lands, this configuration is green, so adding it is a regression guard rather than a source of new work.
2. UBSan × --enable-experimental-jit
jit.yml builds the JIT in several configurations and tail-call.yml builds --with-tail-call-interp, but neither is ever combined with a sanitizer. The JIT's own C code — the stencil patcher in Python/jit.c, and the tier-2 optimiser in Python/optimizer*.c which only compiles under -D_Py_TIER2=1 — is therefore never sanitized in CI.
This one has already proven productive when done by hand: gh-139269 (an unaligned uint64_t store in Python/jit.c's patch_* functions, which segfaulted release builds) was found by building --enable-experimental-jit with -fsanitize=address,undefined, and gh-142476 was an ASan-found leak in allocate_executor.
I ran the full test suite against a machine-code JIT build under UBSan, with every entry in Tools/ubsan/suppressions.txt disabled: clean, zero reports, 51,459 tests across 493 files, 4m50s. Verified non-vacuous — _testinternalcapi.get_jit_backend() returns jit and a hot loop produces real machine-code ranges, so this was the copy-and-patch JIT rather than a silent fallback to the uop interpreter.
Since it is already green, this is also a pure regression guard.
Two smaller findings from the same sweep
local-boundsis enabled nowhere.--with-undefined-behavior-sanitizerinjects plain-fsanitize=undefined, andlocal-boundsis not in that group even though it detects genuine UB. I built with it on and ran the full suite: zero findings, including no false positives from the trailing variable-length array idiom, which was the obvious risk. It looks free to add.float-divide-by-zero(also not in the group) yields exactly one hit,Modules/expat/xmlparse.c:869, in vendored libexpat, which deliberately relies on IEEE-754+infand documents that in a comment. Not worth enabling without excluding expat.
On cost
The usual objection to widening the matrix is build time. For reference, a full PGO + ThinLTO + UBSan build — the heaviest combination I tried, including the instrumented build, the profile run, the rebuild and the ThinLTO link — took 9 min 43 s at --jobs=8. A plain UBSan build is far cheaper, and the JIT suite run above took under five minutes.
(That PGO/LTO configuration found nothing new, incidentally. It is worth noting only because --enable-optimizations and --with-lto appear in no workflow at all, while Arch, Debian and python-build-standalone all ship them. UBSan's checks are source-level, so PGO/LTO mostly changes which UB is reached; miscompilation from UB would show up as test failures rather than sanitizer reports, which is a different exercise.)
Linked PRs
- #154915 — fixes the free-threaded UB, prerequisite for adding that matrix entry
Has this already been discussed elsewhere?
This is a follow-up to gh-148286.
Links to previous discussion of this feature:
- gh-148286
- gh-139269
- gh-142476
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia da .github/workflows/build.yml e confronta la relativa matrice UBSan con le configurazioni dei workflow per free-threading, JIT e tail-call. Verifica il prerequisito in #154915, quindi aggiorna la configurazione CI pertinente in modo che UBSan copra le build proposte e la considerazione di local-bounds. L'attività è completata quando i nuovi job vengono eseguiti correttamente senza segnalazioni del sanitizer e mantengono la copertura CI esistente.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- github-actions, python
- Ambito
- build-system, ci-cd, testing-qa
- Tipo di issue
- Funzionalità
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Stato di attività
- Tranquilla
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 62/100