[31601] in CVS-changelog-for-Kerberos-V5

home help back first fref pref prev next nref lref last post

krb5 commit: Avoid the macOS API ccache in forked children

daemon@ATHENA.MIT.EDU (ghudson@mit.edu)
Mon Nov 2 21:13:39 2026

From: ghudson@mit.edu
To: cvs-krb5@mit.edu
Message-Id: <20261103021332.89BAC101CCE@krbdev.mit.edu>
Date: Mon,  2 Nov 2026 21:13:32 -0500 (EST)
MIME-Version: 1.0
Reply-To: krbdev@mit.edu
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Errors-To: cvs-krb5-bounces@mit.edu

https://github.com/krb5/krb5/commit/e7a22fc32da7e5aafb2aba005b621081a898109c
commit e7a22fc32da7e5aafb2aba005b621081a898109c
Author: Douglas Eichelberger <d@eic.email>
Date:   Tue Oct 27 18:55:48 2026 -0700

    Avoid the macOS API ccache in forked children
    
    The macOS API ccache module communicates with the credential cache
    service through the CCAPI framework and XPC.  In a child process
    forked from a parent that already used them, those calls can crash the
    process.  Record the pid of the first API ccache use; in a child
    forked after that, report the API collection as empty, treat caches as
    not found (KRB5_FCC_NOFILE), and leave CCAPI objects inherited from
    the parent unreleased, so the child never calls the CCAPI framework or
    XPC.
    
    [ghudson@mit.edu: edited commit message and comment]
    
    ticket: 9276
    tags: pullup
    target_version: 1.22-next

 src/lib/krb5/ccache/cc_api_macos.c | 62 ++++++++++++++++++++++++++++++++------
 1 file changed, 52 insertions(+), 10 deletions(-)

diff --git a/src/lib/krb5/ccache/cc_api_macos.c b/src/lib/krb5/ccache/cc_api_macos.c
index 34b1c350f..ba06f9e91 100644
--- a/src/lib/krb5/ccache/cc_api_macos.c
+++ b/src/lib/krb5/ccache/cc_api_macos.c
@@ -64,6 +64,29 @@ struct api_macos_ptcursor {
     cc_ccache_iterator_t iter;
 };
 
+/*
+ * The macOS CCAPI framework and XPC are not safe to use in a child process
+ * forked from a multithreaded parent.  Record the pid of the first use.  In a
+ * child forked after that, report the collection as empty and caches as not
+ * found, and leave CCAPI objects inherited from the parent unreleased.
+ */
+static k5_once_t first_use_once = K5_ONCE_INIT;
+static pid_t first_use_pid;
+
+static void
+record_first_use(void)
+{
+    first_use_pid = getpid();
+}
+
+static krb5_boolean
+forked_after_first_use(void)
+{
+    if (k5_once(&first_use_once, record_first_use) != 0)
+        return FALSE;
+    return first_use_pid != getpid();
+}
+
 /* Map a CCAPI error code to a com_err code. */
 static krb5_error_code
 ccerr2mit(uint32_t err)
@@ -96,6 +119,8 @@ make_cache(const char *residual, cc_context_t cc_context,
     *ccache_out = NULL;
 
     if (cc_context == NULL) {
+        if (forked_after_first_use())
+            return KRB5_FCC_NOFILE;
         err = cc_initialize(&cc_context, ccapi_version_max, NULL, NULL);
         if (err != ccNoError)
             return KRB5_FCC_INTERNAL;
@@ -133,6 +158,8 @@ oom:
 static uint32_t
 open_cache(struct api_macos_cache_data *data)
 {
+    if (forked_after_first_use())
+        return ccErrCCacheNotFound;
     if (data->cache != NULL)
         return ccNoError;
     return cc_context_open_ccache(data->cc_context, data->residual,
@@ -171,6 +198,9 @@ get_primary_name(krb5_context context, char **name_out)
 
     *name_out = NULL;
 
+    if (forked_after_first_use())
+        return KRB5_FCC_NOFILE;
+
     if (uname(&un) == 0) {
         release = strtol(un.release, &end, 10);
         if (end != un.release && release < 20) {
@@ -253,6 +283,9 @@ api_macos_gen_new(krb5_context context, krb5_ccache *cache_out)
 
     *cache_out = NULL;
 
+    if (forked_after_first_use())
+        return KRB5_FCC_NOFILE;
+
     err = cc_initialize(&cc_context, ccapi_version_max, NULL, NULL);
     if (err)
         goto cleanup;
@@ -291,6 +324,9 @@ api_macos_initialize(krb5_context context, krb5_ccache cache,
     uint32_t err;
     char *princstr = NULL, *prefix_name = NULL;
 
+    if (forked_after_first_use())
+        return KRB5_FCC_NOFILE;
+
     /* Apple's cc_context_create_ccache() requires a name with type prefix. */
     if (asprintf(&prefix_name, "API:%s", data->residual) < 0)
         return ENOMEM;
@@ -319,9 +355,11 @@ api_macos_close(krb5_context context, krb5_ccache cache)
 {
     struct api_macos_cache_data *data = cache->data;
 
-    if (data->cache != NULL)
-        cc_ccache_release(data->cache);
-    cc_context_release(data->cc_context);
+    if (!forked_after_first_use()) {
+        if (data->cache != NULL)
+            cc_ccache_release(data->cache);
+        cc_context_release(data->cc_context);
+    }
     free(data->residual);
     free(data);
     free(cache);
@@ -333,8 +371,7 @@ api_macos_destroy(krb5_context context, krb5_ccache cache)
 {
     struct api_macos_cache_data *data = cache->data;
 
-    open_cache(data);
-    if (data->cache != NULL) {
+    if (open_cache(data) == ccNoError) {
         cc_ccache_destroy(data->cache);
         data->cache = NULL;
     }
@@ -441,7 +478,8 @@ api_macos_end_seq_get(krb5_context context, krb5_ccache cache,
 {
     cc_credentials_iterator_t iter = *cursor;
 
-    cc_credentials_iterator_release(iter);
+    if (!forked_after_first_use())
+        cc_credentials_iterator_release(iter);
     *cursor = NULL;
     return 0;
 }
@@ -576,6 +614,8 @@ api_macos_ptcursor_next(krb5_context context, krb5_cc_ptcursor ptcursor,
     defname = krb5_cc_default_name(context);
     if (defname == NULL || strncmp(defname, "API:", 4) != 0)
         return 0;
+    if (forked_after_first_use())
+        return 0;
     defresidual = defname + 4;
 
     /* If the default cache name is a subsidiary cache, yield that cache if it
@@ -640,10 +680,12 @@ api_macos_ptcursor_free(krb5_context context, krb5_cc_ptcursor *ptcursor)
     struct api_macos_ptcursor *apt = (*ptcursor)->data;
 
     if (apt != NULL) {
-        if (apt->iter != NULL)
-            cc_ccache_iterator_release(apt->iter);
-        if (apt->cc_context != NULL)
-            cc_context_release(apt->cc_context);
+        if (!forked_after_first_use()) {
+            if (apt->iter != NULL)
+                cc_ccache_iterator_release(apt->iter);
+            if (apt->cc_context != NULL)
+                cc_context_release(apt->cc_context);
+        }
         free(apt->primary);
         free(apt);
     }
_______________________________________________
cvs-krb5 mailing list
cvs-krb5@mit.edu
https://mailman.mit.edu/mailman/listinfo/cvs-krb5

home help back first fref pref prev next nref lref last post