macosrustcocoafficore-foundation

Segfault when requesting accessibility permission via macOS Accessibility API


I'm trying to create a Rust program that will ask for accessibility permission on macOS.

If the permission is granted (through Settings -> Privacy & Security -> Accessibility), then it works and returns true from AXIsProcessTrustedWithOptions.

But if the permission is not allowed, instead of prompting to ask for the permission, the program crashes with a segfault.

It seems the settings dictionary is created incorrectly, but I'm not sure why.

I created it by following AXIsProcessTrustedWithOptions Apple's docs and tested it on macOS 14.4.1 (Apple silicon).

use accessibility_sys::AXIsProcessTrustedWithOptions;
use core_foundation_sys::base::{CFRelease, TCFTypeRef};
use core_foundation_sys::dictionary::{CFDictionaryAddValue, CFDictionaryCreateMutable};
use core_foundation_sys::number::kCFBooleanTrue;
use std::ffi::CString;
use std::ptr;

fn main() {
    let mut is_allowed = false;
    unsafe {
        let options =
            CFDictionaryCreateMutable(ptr::null_mut(), 1, std::ptr::null(), std::ptr::null());
        let key = CString::new("AXTrustedCheckOptionPrompt").unwrap();
        let value = kCFBooleanTrue;
        CFDictionaryAddValue(options, key.as_ptr().as_void_ptr(), value.as_void_ptr());
        is_allowed = AXIsProcessTrustedWithOptions(options);
        CFRelease(options as *const _);
    }
    println!("Accessibility permission enabled: {}", is_allowed);
}

Solution

  • Thanks for the help in the comments, I used accessibility_sys::kAXTrustedCheckOptionPrompt instead of my own CString and now it doesn't crash! also added error handling

    use std::{error::Error, ptr};
    use accessibility_sys::{kAXTrustedCheckOptionPrompt, AXIsProcessTrustedWithOptions};
    use core_foundation_sys::dictionary::{CFDictionaryAddValue, CFDictionaryCreateMutable};
    use core_foundation_sys::base::{CFRelease, TCFTypeRef};
    use core_foundation_sys::number::{kCFBooleanFalse, kCFBooleanTrue};
    
    
    fn check_accessibility(ask_if_not_allowed: bool) -> Result<bool, Box<dyn Error>> {
        let is_allowed;
        unsafe {
            let options =
                CFDictionaryCreateMutable(ptr::null_mut(), 0, std::ptr::null(), std::ptr::null());
            let key = kAXTrustedCheckOptionPrompt;
            let value = if ask_if_not_allowed {kCFBooleanTrue} else {kCFBooleanFalse};
            if !options.is_null() {
                CFDictionaryAddValue(
                    options,
                    key.as_void_ptr(),
                    value.as_void_ptr(),
                );
                is_allowed = AXIsProcessTrustedWithOptions(options);
                CFRelease(options as *const _);
            } else {
                return Err("options is null".into());
            }
        }
        Ok(is_allowed)
    }
    
    fn main() {
        let is_allowed = check_accessibility(true).unwrap();
        println!("Accessibility permission enabled: {}", is_allowed);
    }