cpython's CI should run Undefined Behavior Sanitizer (UBSAN), like it already does for Address Sanitizer (ASAN).
It already has support in configure for this with ./configure --with-undefined-behavior-sanitizer.
This is important for portability, future proofing (keeping CPython working with a range of compilers), and avoiding confusing bug reports later when UB manifests.
Note that --with-undefined-behavior-sanitizer does not set export UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 right now.
By default, UBSAN will not abort on errors, which might mean errors are being missed by the fuzzer right now which does seem to use UBSAN? Not sure how the configuration works for that job:
Overall in favor. Doing this in CI is predicated on if we actually pass UBSAN today and which things we need to categorize and disable in order to do so.
Note that there have been some UB reports in the past that may be technically correct from a C++ compiler spec perspective (read: clang, and note that C and C++ have different defined behavior definitions) but not actually undefined from a "giant swath of C code in the world has depended on the implemented behavior for 30+ years" perspective so the implemented behavior won't actually ever be changed. (see us passing -fwrapv to the compiler all over the place? defining that behavior in our build is one reason why)
Work to be done there (if any) is mostly around categorization and how to tell UBSAN which things we know and are okay with, the goal being not to introduce more.
For example, clang 17 ubsan gives triggers a build failure trying to run _freeze_module:
../cpython/Objects/object.c:2858:5: runtime error: call to function list_dealloc through pointer to incorrect function type 'void (*)(struct _o
bject *)'
../cpython/Objects/listobject.c:347: note: list_dealloc defined here
(*)(PyObject *) vs (*)(PyListObject *) is "undefined"? CPython casts things to PyObject* all over the place as a standard practice by design as a common C idiom for structure extension.
Activity
thesamesam commented on Nov 5, 2023
Note that --with-undefined-behavior-sanitizer does not set export UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 right now.
By default, UBSAN will not abort on errors, which might mean errors are being missed by the fuzzer right now which does seem to use UBSAN? Not sure how the configuration works for that job:
thesamesam commented on Nov 5, 2023
cc @gpshead as we've discussed build issues before and this feels like it's in your wheelhouse
gpshead commented on Nov 5, 2023
Overall in favor. Doing this in CI is predicated on if we actually pass UBSAN today and which things we need to categorize and disable in order to do so.
gpshead commented on Nov 5, 2023
Note that there have been some UB reports in the past that may be technically correct from a C++ compiler spec perspective (read: clang, and note that C and C++ have different defined behavior definitions) but not actually undefined from a "giant swath of C code in the world has depended on the implemented behavior for 30+ years" perspective so the implemented behavior won't actually ever be changed. (see us passing -fwrapv to the compiler all over the place? defining that behavior in our build is one reason why)
Work to be done there (if any) is mostly around categorization and how to tell UBSAN which things we know and are okay with, the goal being not to introduce more.
gpshead commented on Nov 5, 2023
For example, clang 17 ubsan gives triggers a build failure trying to run _freeze_module:
(*)(PyObject *) vs (*)(PyListObject *) is "undefined"? CPython casts things to PyObject* all over the place as a standard practice by design as a common C idiom for structure extension.
thesamesam commented on Nov 5, 2023
Yeah, see #111178 for that one. I'm not sure that this use as it stands is compatible with CFI which is a useful exploit mitigation.
But yeah, categorising existing errors sounds fine, sorting them into "things we should fix" and "things we want to make defined in CPython".
encukou commented on Jun 16, 2025
Undefined behavior is now gone (#111178, #133157), thanks to @picnixz and many others.
gh-111758: Run UBSan in GitHub Actions (GH-135578)
encukou commented on Jun 19, 2025
The CI run is in. But, as Hugo pointed out, the workflow definitions are now somewhat repetitive.
I'll watch the job for about a week before consolidating.
pythongh-111758: Run UBSan in GitHub Actions (pythonGH-135578)
pythongh-111758: Run UBSan in GitHub Actions (pythonGH-135578)
webknjaz commented on Jul 20, 2025
Now that it's passed, #136820 does this thanks to the EuroPython collab ;)
thesamesam commented on Jul 20, 2025
Thanks for the efforts on this, all.
GH-111758: Merge TSan and UBSan reusable GHA workflows (#136820)
pythonGH-111758: Merge TSan and UBSan reusable GHA workflows (pythonG…
[3.14] GH-111758: Merge TSan and UBSan reusable GHA workflows (GH-136820
webknjaz commented on Jul 22, 2025
@thesamesam I think this issue can be considered resolved.
thesamesam commented on Jul 22, 2025
Indeed.
[3.13] pythonGH-111758: Merge TSan and UBSan reusable GHA workflows (p…
[3.13] pythonGH-111758: Merge TSan and UBSan reusable GHA workflows (p…
[3.13] GH-111758: Merge TSan and UBSan reusable GHA workflows (#136820)…
pythongh-111758: Run UBSan in GitHub Actions (pythonGH-135578)
pythonGH-111758: Merge TSan and UBSan reusable GHA workflows (python#…
pythongh-111758: Run UBSan in GitHub Actions (pythonGH-135578)
pythonGH-111758: Merge TSan and UBSan reusable GHA workflows (python#…
[3.14] pythonGH-111758: Merge TSan and UBSan reusable GHA workflows (p…