#9627 Segmentation fault in ipapython.session_storage.store_data on macOS
Opened by gasinvein. Modified

Issue

I'm trying to use the FreeIPA Python API on macOS. With builtin Python (3.9 as of macOS 14), it seems to work normal. But with Python installed from MacPorts (even if it's the same major version), the interpreter terminates with SIGSEGV once the code execution reaches ipapython.session_storage.get_data and calls krb5_build_principal.

Steps to Reproduce

  1. Install any Python version from MacPorts, e.g. port install python39
  2. /opt/local/bin/python3.9 -c 'from ipapython import session_storage; session_storage.store_data("user@REALM", "key", "value")'

Actual behavior

What's interesting is that it doesn't matter if the libkrb5.dylib library is a system Heimdal kerberos library or a MIT one from MacPorts package kerberos5, the crash happens in either case.

The traceback with PYTHONFAULTHANDLER=1 is identical:

Fatal Python error: Segmentation fault
Current thread 0x00000001ff964c00 (most recent call first):
  File "/Users/gasinvein/Library/Python/3.9/lib/python/site-packages/ipapython/session_storage.py", line 297 in get_data
  File "/Users/gasinvein/Library/Python/3.9/lib/python/site-packages/ipapython/session_storage.py", line 237 in store_data
  File "<string>", line 1 in <module>
fish: Job 1, 'PYTHONFAULTHANDLER=1 python3.9 …' terminated by signal SIGSEGV (Address boundary error)

In the crash report with builtin Heimdal libkrb5.dylib:

0   libsystem_kernel.dylib                 0x197af2a60 __pthread_kill + 8
1   libsystem_pthread.dylib                0x197b2ac20 pthread_kill + 288
2   libsystem_c.dylib                      0x197a011e0 raise + 32
3   Python                                 0x101c5c7f4 faulthandler_fatal_error + 412
4   libsystem_platform.dylib               0x197b5b584 _sigtramp + 56
5   Heimdal                                0x1a62b4f54 va_princ + 120
6   Heimdal                                0x1a62b4eb8 build_principal + 232
7   Heimdal                                0x1a62b4dc4 krb5_build_principal_va + 80
8   Kerberos                               0x1a87f1cc8 krb5_build_principal + 116
9   libffi.8.dylib                         0x100c34050 ffi_call_SYSV + 80
10  libffi.8.dylib                         0x100c31548 ffi_call_int + 1432
11  _ctypes.cpython-312-darwin.so          0x100c0f9e8 _ctypes_callproc + 808
12  _ctypes.cpython-312-darwin.so          0x100c08a14 PyCFuncPtr_call + 216
13  Python                                 0x101aec428 _PyObject_MakeTpCall + 128
14  Python                                 0x101be0048 _PyEval_EvalFrameDefault + 44060
15  Python                                 0x101bd51bc PyEval_EvalCode + 184
16  Python                                 0x101c36590 run_eval_code_obj + 88
17  Python                                 0x101c34628 run_mod + 132
18  Python                                 0x101c33d0c PyRun_StringFlags + 124
19  Python                                 0x101c33c38 PyRun_SimpleStringFlags + 64
20  Python                                 0x101c56d94 Py_RunMain + 572
21  Python                                 0x101c57330 pymain_main + 304
22  Python                                 0x101c573d0 Py_BytesMain + 40
23  dyld                                   0x1977a20e0 start + 2360

In the crash report with MIT libkrb5.dylib from MacPorts (running python with DYLD_LIBRARY_PATH=/opt/local/lib:

0   libsystem_kernel.dylib                 0x197af2a60 __pthread_kill + 8
1   libsystem_pthread.dylib                0x197b2ac20 pthread_kill + 288
2   libsystem_c.dylib                      0x197a011e0 raise + 32
3   libsystem_platform.dylib               0x197b5b584 _sigtramp + 56
4   libkrb5.3.3.dylib                      0x103201970 build_principal_va + 204
5   libkrb5.3.3.dylib                      0x103201a9c krb5_build_principal_alloc_va + 68
6   libkrb5.3.3.dylib                      0x103201af0 krb5_build_principal + 28
7   libffi.8.dylib                         0x102c94050 ffi_call_SYSV + 80
8   libffi.8.dylib                         0x102c91548 ffi_call_int + 1432
9   _ctypes.cpython-39-darwin.so           0x102cc468c _ctypes_callproc + 860
10  _ctypes.cpython-39-darwin.so           0x102cbf450 PyCFuncPtr_call + 220
11  Python                                 0x10330ed34 _PyObject_MakeTpCall + 132
12  Python                                 0x1033e7cb4 call_function + 268
13  Python                                 0x1033e1b94 _PyEval_EvalFrameDefault + 15808
14  Python                                 0x10330f92c _PyFunction_Vectorcall + 184
15  Python                                 0x1033e7c28 call_function + 128
16  Python                                 0x1033e1b94 _PyEval_EvalFrameDefault + 15808
17  Python                                 0x10330f92c _PyFunction_Vectorcall + 184
18  Python                                 0x1033e7c28 call_function + 128
19  Python                                 0x1033e2e20 _PyEval_EvalFrameDefault + 20556
20  Python                                 0x1033dcf90 _PyEval_EvalCode + 416
21  Python                                 0x103428944 run_eval_code_obj + 136
22  Python                                 0x103428874 run_mod + 112
23  Python                                 0x103429384 PyRun_StringFlags + 148
24  Python                                 0x1034292a8 PyRun_SimpleStringFlags + 64
25  Python                                 0x10343fe88 Py_RunMain + 336
26  Python                                 0x103441188 Py_BytesMain + 40
27  dyld                                   0x1977a20e0 start + 2360

Expected behavior

The command shouldn't cause the interpreter to crash.

Version/Release/Distribution

FreeIPA python modules from 4.12.1


IPA code is not supposed to be used with Heimdal at all.

It seems in both cases it is failing in the krb5_build_principal().

        krb5_build_principal(context, ctypes.byref(srv_princ),
                             len(CONF_REALM), ctypes.c_char_p(CONF_REALM),
                             ctypes.c_char_p(CONF_NAME), ctypes.c_char_p(key),
                             pname, ctypes.c_char_p(None))

Are you able to run that under gdb and have bt full after a crash so that we can see actual context?

IPA code is not supposed to be used with Heimdal at all.

How is its client supposed to be used on macOS, then? Judging from the session_storage.py code, which has a special case for macOS, I've assumed it was at least considered at some point. MIT Kerberos isn't widely available on macOS.

It seems in both cases it is failing in the krb5_build_principal().

It's what I've figured, yes.

Are you able to run that under gdb and have bt full after a crash so that we can see actual context?

AFAIK GDB isn't available on Apple Silicon macs, and LLDB backtrace if essentially the same as what I've copy-pasted from crash reports. Anyway, here it is:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
  * frame #0: 0x0000000197b58904 libsystem_platform.dylib`_platform_strlen + 4
    frame #1: 0x0000000100705970 libkrb5.3.3.dylib`build_principal_va + 204
    frame #2: 0x0000000100705a9c libkrb5.3.3.dylib`krb5_build_principal_alloc_va + 68
    frame #3: 0x0000000100705af0 libkrb5.3.3.dylib`krb5_build_principal + 28
    frame #4: 0x0000000100198050 libffi.8.dylib`ffi_call_SYSV + 80
    frame #5: 0x0000000100195548 libffi.8.dylib`ffi_call_int + 1432
    frame #6: 0x00000001001c868c _ctypes.cpython-39-darwin.so`_ctypes_callproc + 860
    frame #7: 0x00000001001c3450 _ctypes.cpython-39-darwin.so`PyCFuncPtr_call + 220
    frame #8: 0x0000000100812d34 Python`_PyObject_MakeTpCall + 132
    frame #9: 0x00000001008ebcb4 Python`call_function + 268
    frame #10: 0x00000001008e5b94 Python`_PyEval_EvalFrameDefault + 15808
    frame #11: 0x000000010081392c Python`_PyFunction_Vectorcall + 184
    frame #12: 0x00000001008ebc28 Python`call_function + 128
    frame #13: 0x00000001008e5b94 Python`_PyEval_EvalFrameDefault + 15808
    frame #14: 0x000000010081392c Python`_PyFunction_Vectorcall + 184
    frame #15: 0x00000001008ebc28 Python`call_function + 128
    frame #16: 0x00000001008e6e20 Python`_PyEval_EvalFrameDefault + 20556
    frame #17: 0x00000001008e0f90 Python`_PyEval_EvalCode + 416
    frame #18: 0x000000010092c944 Python`run_eval_code_obj + 136
    frame #19: 0x000000010092c874 Python`run_mod + 112
    frame #20: 0x000000010092d384 Python`PyRun_StringFlags + 148
    frame #21: 0x000000010092d2a8 Python`PyRun_SimpleStringFlags + 64
    frame #22: 0x0000000100943e88 Python`Py_RunMain + 336
    frame #23: 0x0000000100945188 Python`Py_BytesMain + 40
    frame #24: 0x00000001977a20e0 dyld`start + 2360

I guess Python must be built with debug symbols to make this useful? In this case, I'll need to figure out how to do this with MacPorts.

IPA client code was not and is currently not planned to work on macOS. session_storage.py was developed as a generic wrapper using ctypes/CFFI, but never targeted to use macOS. May be one of developers had macOS access at that time and added it for convenience, at that time.

Debug symbols are needed, yes, to get context... It may be one of arguments is NULL and that causes crashes -- judging by strlen() in the build_principal_va it is probably

            data[count].length = strlen(component);

where component is one of elements obtained through va_arg() macro. There are no checks for NULL arguments in there so we definitely passed something NULL in the original list.

Here is the LLDB backtrace from python and kerberos both build with debug symbols:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
  * frame #0: 0x0000000197b58904 libsystem_platform.dylib`_platform_strlen + 4
    frame #1: 0x00000001002b2a3c libkrb5.3.3.dylib`build_principal_va(context=0x00000001126049f0, princ=0x0000600000740450, rlen=12, realm="X-CACHECONF:", ap="\U00000010\xaa\xdfo\U00000001") at bld_princ.c:76:34
    frame #2: 0x00000001002b2c00 libkrb5.3.3.dylib`krb5_build_principal_alloc_va(context=0x00000001126049f0, princ=0x0000000100126f08, rlen=12, realm="X-CACHECONF:", ap="\xc0Kp\"\U00000001") at bld_princ.c:128:14
    frame #3: 0x00000001002b2c94 libkrb5.3.3.dylib`krb5_build_principal(context=0x00000001126049f0, princ=0x0000000100126f08, rlen=12, realm="X-CACHECONF:") at bld_princ.c:148:14
    frame #4: 0x00000001001cc050 libffi.8.dylib`ffi_call_SYSV + 80
    frame #5: 0x00000001001ca78c libffi.8.dylib`ffi_call_int(cif=0x000000016fdfacb8, fn=(libkrb5.3.3.dylib`krb5_build_principal at bld_princ.c:143), orig_rvalue=0x000000016fdfadc0, avalue=0x000000016fdfad80, closure=0x0000000000000000) at ffi.c:829:3
    frame #6: 0x00000001001ca134 libffi.8.dylib`ffi_call(cif=0x000000016fdfacb8, fn=(libkrb5.3.3.dylib`krb5_build_principal at bld_princ.c:143), rvalue=0x000000016fdfadc0, avalue=0x000000016fdfad80) at ffi.c:838:3
    frame #7: 0x00000001002043d8 _ctypes.cpython-39-darwin.so`_call_function_pointer(flags=4353, pProc=(libkrb5.3.3.dylib`krb5_build_principal at bld_princ.c:143), avalues=0x000000016fdfad80, atypes=0x000000016fdfad40, restype=0x000000010018b2f8, resmem=0x000000016fdfadc0, argcount=8, argtypecount=8) at callproc.c:920:17
    frame #8: 0x0000000100203c18 _ctypes.cpython-39-darwin.so`_ctypes_callproc(pProc=(libkrb5.3.3.dylib`krb5_build_principal at bld_princ.c:143), argtuple=0x0000000100151e40, flags=4353, argtypes=0x0000000100151a50, restype=0x000000012260da20, checker=0x0000000000000000) at callproc.c:1263:15
    frame #9: 0x00000001001f8668 _ctypes.cpython-39-darwin.so`PyCFuncPtr_call(self=0x0000000100170c40, inargs=0x0000000100151e40, kwds=0x0000000000000000) at _ctypes.c:4201:14
    frame #10: 0x0000000100e487e4 Python`_PyObject_MakeTpCall + 392
    frame #11: 0x0000000100fa4a54 Python`_PyObject_VectorcallTstate + 100
    frame #12: 0x0000000100fa1a5c Python`PyObject_Vectorcall + 60
    frame #13: 0x0000000100fa1b70 Python`call_function + 264
    frame #14: 0x0000000100f9df10 Python`_PyEval_EvalFrameDefault + 42356
    frame #15: 0x0000000100e4a738 Python`_PyEval_EvalFrame + 52
    frame #16: 0x0000000100e49450 Python`function_code_fastcall + 204
    frame #17: 0x0000000100e491a4 Python`_PyFunction_Vectorcall + 288
    frame #18: 0x0000000100fa4a74 Python`_PyObject_VectorcallTstate + 132
    frame #19: 0x0000000100fa1a5c Python`PyObject_Vectorcall + 60
    frame #20: 0x0000000100fa1b70 Python`call_function + 264
    frame #21: 0x0000000100f9df10 Python`_PyEval_EvalFrameDefault + 42356
    frame #22: 0x0000000100e4a738 Python`_PyEval_EvalFrame + 52
    frame #23: 0x0000000100e49450 Python`function_code_fastcall + 204
    frame #24: 0x0000000100e491a4 Python`_PyFunction_Vectorcall + 288
    frame #25: 0x0000000100fa4a74 Python`_PyObject_VectorcallTstate + 132
    frame #26: 0x0000000100fa1a5c Python`PyObject_Vectorcall + 60
    frame #27: 0x0000000100fa1b70 Python`call_function + 264
    frame #28: 0x0000000100f9ddb0 Python`_PyEval_EvalFrameDefault + 42004
    frame #29: 0x0000000100f93958 Python`_PyEval_EvalFrame + 52
    frame #30: 0x0000000100fa2da0 Python`_PyEval_EvalCode + 3164
    frame #31: 0x0000000100fa36d0 Python`_PyEval_EvalCodeWithName + 216
    frame #32: 0x0000000100f938e4 Python`PyEval_EvalCodeEx + 252
    frame #33: 0x0000000100f937dc Python`PyEval_EvalCode + 76
    frame #34: 0x00000001010003c0 Python`run_eval_code_obj + 164
    frame #35: 0x0000000100ffe7e0 Python`run_mod + 188
    frame #36: 0x0000000100ffd9a0 Python`PyRun_StringFlags + 260
    frame #37: 0x0000000100ffd854 Python`PyRun_SimpleStringFlags + 104
    frame #38: 0x000000010102d0f0 Python`pymain_run_command + 180
    frame #39: 0x000000010102c974 Python`pymain_run_python + 388
    frame #40: 0x000000010102c794 Python`Py_RunMain + 24
    frame #41: 0x000000010102cb98 Python`pymain_main + 112
    frame #42: 0x000000010102cbe8 Python`Py_BytesMain + 56
    frame #43: 0x0000000100003f90 Python`___lldb_unnamed_symbol3 + 36
    frame #44: 0x00000001977a20e0 dyld`start + 2360
Metadata