diff --git a/Misc/NEWS.d/next/C_API/2026-06-17-08-12-29.gh-issue-151515.0rJRKv.rst b/Misc/NEWS.d/next/C_API/2026-06-17-08-12-29.gh-issue-151515.0rJRKv.rst new file mode 100644 index 000000000000000..16bbf07af7e2b98 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-06-17-08-12-29.gh-issue-151515.0rJRKv.rst @@ -0,0 +1,12 @@ +Summary + +This PR fixes an undefined behavior bug in the C code generated by Parser/asdl_c.py for get_ast_state(). +The Problem + +In the generated code, _PyOnceFlag_CallOnce is invoked like this: +_PyOnceFlag_CallOnce(&state->once, (_Py_once_fn_t *)&init_types, state) + +Using &init_types passes a pointer to a function pointer (effectively a double pointer), rather than the function pointer itself. by forcing this with an explicit cast (_Py_once_fn_t *) silences the compiler but triggers a strict aliasing violation and undefined behavior at runtime when the pointer is dereferenced. This can lead to compiler-optimization-driven segmentation faults, particularly in free-threaded/GIL-disabled builds where one-time initialization flags are heavily relied upon. +The Fix + +Updated Parser/asdl_c.py to pass init_types directly with a standard function pointer cast (_Py_once_fn_t)init_types (removing the extra & and *). Ran make regen-ast to cleanly update the generated files. diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index e2a57177d20afb7..14fe70000b829ab 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -2274,7 +2274,7 @@ def generate_module_def(mod, metadata, f, internal_h): PyInterpreterState *interp = _PyInterpreterState_GET(); struct ast_state *state = &interp->ast; assert(!state->finalized); - if (_PyOnceFlag_CallOnce(&state->once, (_Py_once_fn_t *)&init_types, state) < 0) { + if (_PyOnceFlag_CallOnce(&state->once, &init_types, state) < 0) { return NULL; } return state; diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 49b6bf1d12b6fab..a8cc3847b3154ff 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -23,7 +23,7 @@ get_ast_state(void) PyInterpreterState *interp = _PyInterpreterState_GET(); struct ast_state *state = &interp->ast; assert(!state->finalized); - if (_PyOnceFlag_CallOnce(&state->once, (_Py_once_fn_t *)&init_types, state) < 0) { + if (_PyOnceFlag_CallOnce(&state->once, &init_types, state) < 0) { return NULL; } return state;