diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2025-08-15 09:51:40 +0200 |
|---|---|---|
| committer | Markus Pettersson <markus.pettersson@mullvad.net> | 2025-08-28 14:34:02 +0200 |
| commit | 8b7e2dba790e90a0ac885adfeded15eb97fb99f0 (patch) | |
| tree | 320dd43be7f2681a0b70a1fe71ff06fb89b8e29c | |
| parent | 57e41952f8d0b02d0d433c12879e7e537807cb3f (diff) | |
| download | mullvadvpn-8b7e2dba790e90a0ac885adfeded15eb97fb99f0.tar.xz mullvadvpn-8b7e2dba790e90a0ac885adfeded15eb97fb99f0.zip | |
Use objc2_service_management to get rid of a lot of unsafe
| -rw-r--r-- | Cargo.lock | 29 | ||||
| -rw-r--r-- | mullvad-daemon/Cargo.toml | 4 | ||||
| -rw-r--r-- | mullvad-daemon/src/macos_launch_daemon.rs | 81 |
3 files changed, 55 insertions, 59 deletions
diff --git a/Cargo.lock b/Cargo.lock index 0acd63ee00..dded76c819 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2986,8 +2986,8 @@ dependencies = [ "mullvad-version", "nix 0.30.1", "notify 8.0.0", - "objc2 0.5.2", "objc2-foundation 0.3.1", + "objc2-service-management", "rand 0.8.5", "regex", "serde", @@ -3610,9 +3610,6 @@ name = "objc-sys" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" -dependencies = [ - "cc", -] [[package]] name = "objc2" @@ -3741,6 +3738,30 @@ dependencies = [ ] [[package]] +name = "objc2-security" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1f8e0ef3ab66b08c42644dcb34dba6ec0a574bbd8adbb8bdbdc7a2779731a44" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-service-management" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ca650c581044d08572b3c21d9ce8c730f2eefdc1683f033849f305ea8212be" +dependencies = [ + "block2 0.6.1", + "objc2 0.6.2", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "objc2-security", +] + +[[package]] name = "objc_id" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index 4844400257..7abf9bf7cc 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -71,8 +71,8 @@ simple-signal = "1.1" talpid-dbus = { path = "../talpid-dbus" } [target.'cfg(target_os="macos")'.dependencies] -objc2 = { version = "0.5.2", features = ["exception"] } -objc2-foundation = { version = "0.3.1", features = ["NSProcessInfo"] } +objc2-foundation = { version = "0.3.1", features = ["NSProcessInfo", "NSString", "NSURL"] } +objc2-service-management = { version = "0.3.1", features = [ "SMAppService", "objc2" ] } notify = "8.0.0" talpid-macos = { path = "../talpid-macos" } diff --git a/mullvad-daemon/src/macos_launch_daemon.rs b/mullvad-daemon/src/macos_launch_daemon.rs index 53f9ba2af4..080e771565 100644 --- a/mullvad-daemon/src/macos_launch_daemon.rs +++ b/mullvad-daemon/src/macos_launch_daemon.rs @@ -5,19 +5,13 @@ //! must be checked so that the user can be directed to approve the launch //! daemon in the system settings. -#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare. +use objc2_foundation::{NSOperatingSystemVersion, NSProcessInfo, NSURL, ns_string}; +use objc2_service_management::{SMAppService, SMAppServiceStatus}; -use std::ffi::CStr; - -use objc2::{class, msg_send, runtime::AnyObject}; -use objc2_foundation::{NSOperatingSystemVersion, NSProcessInfo}; - -type Id = *mut AnyObject; - -// TODO: Replace with obcj2-service-management -// Framework that contains `SMAppService`. -#[link(name = "ServiceManagement", kind = "framework")] -unsafe extern "C" {} +/// Path to the plist that defines the Mullvad launch daemon. +/// It must be kept in sync with the path defined in +/// `dist-assets/pkg-scripts/postinstall`. +const DAEMON_PLIST_PATH: &str = "/Library/LaunchDaemons/net.mullvad.daemon.plist"; /// Authorization status of the Mullvad daemon. #[repr(i32)] @@ -36,20 +30,31 @@ pub fn get_status() -> LaunchDaemonStatus { if os_version.majorVersion < 13 { return LaunchDaemonStatus::Ok; } - get_status_for_url(&daemon_plist_url()) + // SAFETY: daemon_plist_path is not an empty path & it is a valid system path. + // https://developer.apple.com/documentation/foundation/nsurl/fileurl(withpath:)#parameters + let daemon_plist_url = unsafe { NSURL::fileURLWithPath(ns_string!(DAEMON_PLIST_PATH)) }; + get_status_for_url(&daemon_plist_url) } -fn get_status_for_url(url: &Object) -> LaunchDaemonStatus { - let status: libc::c_long = - unsafe { msg_send![class!(SMAppService), statusForLegacyURL: url.0] }; - +fn get_status_for_url(url: &NSURL) -> LaunchDaemonStatus { + // SAFETY: Apple does not state *anything* regarding safety requirements of this function: + // https://developer.apple.com/documentation/servicemanagement/smappservice/statusforlegacyplist(at:) + // But using a bit of reasoning & the [guidelines of objc2](https://github.com/madsmtm/objc2/blob/master/crates/header-translator/README.md#what-is-required-for-a-method-to-be-safe): + // """ + // What is required for a method to be safe? + // 1. The method must not take a raw pointer; one could trivially pass ptr::invalid() and cause UB with that. + // 2. Any extra requirements that the method states in its documentation must be upheld. + // """ + // we can conclude that: + // (1.) is upheld by the virtue of url being a reference, since references are always valid. + // (2.) is trivially upheld since Apple does not state safety requirements. + let status = unsafe { SMAppService::statusForLegacyURL(url) }; match status { - // SMAppServiceStatusNotRegistered | SMAppServiceStatusNotFound - 0 | 3 => LaunchDaemonStatus::NotFound, - // SMAppServiceStatusEnabled - 1 => LaunchDaemonStatus::Ok, - // SMAppServiceStatusRequiresApproval - 2 => LaunchDaemonStatus::NotAuthorized, + SMAppServiceStatus::NotRegistered | SMAppServiceStatus::NotFound => { + LaunchDaemonStatus::NotFound + } + SMAppServiceStatus::Enabled => LaunchDaemonStatus::Ok, + SMAppServiceStatus::RequiresApproval => LaunchDaemonStatus::NotAuthorized, // Unknown status _ => LaunchDaemonStatus::Unknown, } @@ -59,33 +64,3 @@ fn get_os_version() -> NSOperatingSystemVersion { let process_info = NSProcessInfo::processInfo(); process_info.operatingSystemVersion() } - -/// Returns an `NSURL` instance for `DAEMON_PLIST_PATH`. -fn daemon_plist_url() -> Object { - /// Path to the plist that defines the Mullvad launch daemon. - /// It must be kept in sync with the path defined in - /// `dist-assets/pkg-scripts/postinstall`. - const DAEMON_PLIST_PATH: &CStr = c"/Library/LaunchDaemons/net.mullvad.daemon.plist"; - - let nsstr_inst: Id = unsafe { msg_send![class!(NSString), alloc] }; - let nsstr_inst: Id = - unsafe { msg_send![nsstr_inst, initWithUTF8String: DAEMON_PLIST_PATH.as_ptr()] }; - - let nsurl_inst: Id = unsafe { msg_send![class!(NSURL), alloc] }; - let nsurl_inst: Id = unsafe { msg_send![nsurl_inst, initWithString: nsstr_inst] }; - - let _: () = unsafe { msg_send![nsstr_inst, release] }; - - assert!(!nsurl_inst.is_null()); - - Object(nsurl_inst) -} - -/// Calls `[self.0 release]` when the wrapped instance is dropped. -struct Object(Id); - -impl Drop for Object { - fn drop(&mut self) { - let _: () = unsafe { msg_send![self.0, release] }; - } -} |
