Author: Milad Fa Date: 2026-09-24T10:01:11-04:00 New Revision: f05ae447f56404dce7b43145676242d63e0734f4
URL: https://github.com/llvm/llvm-project/commit/f05ae447f56404dce7b43145676242d63e0734f4 DIFF: https://github.com/llvm/llvm-project/commit/f05ae447f56404dce7b43145676242d63e0734f4.diff LOG: [libclang/python] Fix visitor callback return data type (#221024) Declare the visitor callbacks return as c_long, i.e. a full register word rather than c_int. This is currently causing an issue on s390x, a 64-bit big-endian target. The V8 JavaScript engine has recently started using libclang as a dependency to parse header files. It is currently crashing on s390x with a SIGFPE due to this issue. Both the s390x ELF ABI and libffi require the value to be sign/zero extended to register size, and leaving it as `c_int` leaves garbage on the high side. cindex.py calls into libclang through ctypes, which builds the callback closures using libffi. With a `c_int` return, ctypes writes only the low 32 bits and skips the extension that both the ABI and libffi's closure contract require, so libffi hands libclang a register whose high bits are garbage. Using c_long makes the value fill the register at its native width on all platforms. Added: Modified: clang/bindings/python/clang/cindex.py clang/bindings/python/tests/cindex/test_cursor.py clang/docs/ReleaseNotes.md Removed: ################################################################################ diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 7414691f3ae2a2..d425d3c069ae1b 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -71,6 +71,7 @@ byref, c_char_p, c_int, + c_long, c_longlong, c_uint, c_ulong, @@ -82,6 +83,7 @@ ) import os +import platform import sys from enum import Enum import warnings @@ -4121,8 +4123,16 @@ def set_property(self, property, value): translation_unit_includes_callback = CFUNCTYPE( None, c_object_p, POINTER(SourceLocation), c_uint, py_object ) -cursor_visit_callback = CFUNCTYPE(c_int, Cursor, Cursor, py_object) -fields_visit_callback = CFUNCTYPE(c_int, Cursor, py_object) +# On s390x the visitor callbacks must return a full register word (c_long) +# rather than c_int. ctypes does not sign/zero-extend a narrow closure return +# to the full 64-bit return register the s390x ELF ABI requires, leaving +# garbage in the high bytes. libclang reads the full register and faults with +# a SIGFPE. +# TODO: Remove once the ctypes fix (https://github.com/python/cpython/issues/156933) +# has propagated. +_visitor_result = c_long if platform.machine() == "s390x" else c_int +cursor_visit_callback = CFUNCTYPE(_visitor_result, Cursor, Cursor, py_object) +fields_visit_callback = CFUNCTYPE(_visitor_result, Cursor, py_object) # Functions strictly alphabetical order. FUNCTION_LIST: list[LibFunc] = [ diff --git a/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py index c8ffa15c2e84b9..ada62fcdb1b51c 100644 --- a/clang/bindings/python/tests/cindex/test_cursor.py +++ b/clang/bindings/python/tests/cindex/test_cursor.py @@ -10,11 +10,15 @@ TranslationUnit, TypeKind, conf, + cursor_visit_callback, + fields_visit_callback, ) import gc +import platform import unittest +from ctypes import c_int, c_long from .util import get_cursor, get_cursors, get_tu @@ -114,6 +118,15 @@ class C { class TestCursor(unittest.TestCase): + def test_visitor_callback_return_type(self): + # On s390x the visitor callbacks must return a full register word so + # ctypes writes a fully extended return register; a narrow c_int leaves + # the high bytes uninitialized and libclang faults with SIGFPE. + # Works around https://github.com/python/cpython/issues/156933. + expected = c_long if platform.machine() == "s390x" else c_int + self.assertEqual(cursor_visit_callback._restype_, expected) + self.assertEqual(fields_visit_callback._restype_, expected) + def test_get_children(self): tu = get_tu(CHILDREN_TEST) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 9e01980263f9f1..4349f342b3b89f 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -972,6 +972,12 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use ### Python Binding Changes +- Fixed a crash (`SIGFPE`) when traversing an AST via the visitor callbacks + (e.g. `Cursor.get_children`) on s390x. The callbacks now return a full + register word so the return value is correctly extended, working around a + `ctypes` bug (https://github.com/python/cpython/issues/156933) that left the + high bytes of the return register uninitialized. + ### OpenMP Support - Added the OpenMP 6.1 `#pragma omp flatten` loop transformation and the _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
