sheeplog

Checking (macOS) Mach Service Availability with Swift

Testing for mach ports is useful if you have an app that talks to an XPC service, agent or daemon and you’d like to check for availability of the endpoint before pulling out an entire NSXPCConnection. This would, for example, allow you to show a progress indicator while the app waits to pull data over XPC into the UI.

Looking for a way to check if a mach service is reachable (or rather, launchd knows about it), you’ll find lots of C or Objective-C code.

Swift lets you call C functions but the trouble with Swift is that “complex macros” are not available but task_get_bootstrap_port and mach_task_self() are defined as such:

1
2
#define task_get_bootstrap_port(task, port)     \
          (task_get_special_port((task), TASK_BOOTSTRAP_PORT, (port)))

source

1
#define mach_task_self() mach_task_self_

source

Both are “complex macros” and therefore not callable from Swift. Yikes.

Option 1

Write your own C helpers. Beware that they cannot have the name of the originals because there already are macros with the same name. You’ll also need a bridging header (which Xcode takes care of for you when you add a C file to your project).

1
2
3
4
5
6
7
8
// mach_compat.h

#include <mach/mach.h>
mach_port_t
mach_task_current(void);

kern_return_t
task_get_bs_port(task_inspect_t task, mach_port_t *special_port);
1
2
3
4
5
6
7
8
9
10
11
12
13
// mach_compat.c

#include "mach_compat.h"

mach_port_t
mach_task_current() {
    return mach_task_self();
}

kern_return_t
task_get_bs_port(task_inspect_t task, mach_port_t *special_port) {
    return task_get_special_port(task, TASK_BOOTSTRAP_PORT, special_port);
}

And use them like this:

1
2
mach_port_t service_port = MACH_PORT_NULL;
task_get_bs_port(mach_task_current(), &service_port)

Option 2

Simply do what the macros do.

The slightly trickier part was mach_task_self(). XNU’s version gives you mach_task_self_ which mach initializes with the return value of task_self_trap(). Using mach_task_self_ directly is possible but ugly.

So I came up with the following:

1
2
3
4
5
6
7
func machServiceAvailable(_ endpoint: String) -> Bool {
    var srv_port = mach_port_t(MACH_PORT_NULL)
    var bs_port = mach_port_t(MACH_PORT_NULL)
    task_get_special_port(task_self_trap(), TASK_BOOTSTRAP_PORT, &bs_port)
    let result = bootstrap_look_up(bs_port, endpoint, &srv_port)
    return KERN_SUCCESS == result
}

Option 3

Do none of the above.

XNU actually stores the current tasks bootstrap port in bootstrap_port:

1
2
3
4
5
func machServiceAvailable(_ endpoint: String) -> Bool {
    var srv_port = mach_port_t(MACH_PORT_NULL)
    let result = bootstrap_look_up(bootstrap_port, endpoint, &srv_port)
    return KERN_SUCCESS == result
}

See the PoC for CVE-2018-4280 for a neat implementaion of almost the same.

For a deeper understanding of those things I recommend Technical Note 2083.

Tagged with macos, swift, xcode

Creative Commons License