Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,28 @@ fn sel_register(name: &CStr) -> *mut c_void {
}
}

/// Whether `cls` actually implements `sel`, via `class_respondsToSelector`.
///
/// macOS 14 (Sonoma) compatibility guard: `SLSEventAuthenticationMessage`
/// exists on macOS 14, but `messageWithEventRecord:pid:version:` was only
/// added in macOS 15 (Sequoia). `sel_registerName` always succeeds (it just
/// interns the string), so a `!sel.is_null()` check is not enough — we must
/// confirm the class responds before calling `objc_msgSend`, or the runtime
/// raises `NSInvalidArgumentException: unrecognized selector`. See #1503.
fn class_responds_to_selector(cls: *mut c_void, sel: *mut c_void) -> bool {
if cls.is_null() || sel.is_null() {
return false;
}
type RespondsToFn = unsafe extern "C" fn(*mut c_void, *mut c_void) -> bool;
static SYM: OnceLock<Option<RespondsToFn>> = OnceLock::new();
let f = *SYM
.get_or_init(|| find_sym(b"class_respondsToSelector\0").map(|p| unsafe { as_fn(p) }));
match f {
Some(f) => unsafe { f(cls, sel) },
Comment on lines +221 to +226

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Apple Objective-C runtime headers: what is the declared return type of class_respondsToSelector, and is Objective-C BOOLABI-compatible with Rustboolinextern "C" FFI?

💡 Result:

In the Apple Objective-C runtime headers, the function class_respondsToSelector is declared to return the Objective-C BOOL type [1][2]. Whether Objective-C BOOL is ABI-compatible with Rust's bool depends on the platform and architecture, as BOOL is not a fixed type but a typedef [3][4]. In the Objective-C runtime headers, BOOL is defined as either bool (a C99 _Bool) or signed char, depending on the target [3][4]. 1. Compatibility: Rust's bool is typically represented as a single byte [5]. When BOOL is defined as bool (common on modern 64-bit platforms like Apple Silicon), it is generally ABI-compatible with Rust's bool [6][3][4]. However, when BOOL is defined as signed char (common on older architectures or legacy macOS targets), it is not strictly ABI-compatible with Rust's bool because the underlying representations and type-encoding expectations differ [3][4][7]. 2. Risk: Because BOOL's definition is platform-dependent, assuming ABI compatibility with Rust's bool across all targets is unsafe [6][8]. For robust FFI, it is recommended to use an abstraction (such as those found in crates like objc2 or abibool) that accounts for the specific target's BOOL definition, or to explicitly handle the conversion based on the target architecture [6][8][9].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant region in skylight.rs
sed -n '180,270p' libs/cua-driver/rust/crates/platform-macos/src/input/skylight.rs

# Find definitions/usages of find_sym / as_fn in the same file
rg -n "fn find_sym|find_sym\\(|fn as_fn|as_fn\\(" libs/cua-driver/rust/crates/platform-macos/src/input/skylight.rs

# Look for other Objective-C runtime symbol bindings in this repo that use BOOL/bool patterns
rg -n "RespondsToSelector|class_respondsToSelector|respondsToSelector" libs/cua-driver/rust/crates/platform-macos -S
rg -n "extern \"C\" fn\\(.*\\) -> bool|-> bool;|c_schar|BOOL" libs/cua-driver/rust/crates/platform-macos/src -S

Repository: trycua/cua

Length of output: 6803


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant region in skylight.rs
sed -n '180,270p' libs/cua-driver/rust/crates/platform-macos/src/input/skylight.rs

# Find definitions/usages of find_sym / as_fn in the same file
rg -n "fn find_sym|find_sym\\(|fn as_fn|as_fn\\(" libs/cua-driver/rust/crates/platform-macos/src/input/skylight.rs

# Look for other Objective-C runtime symbol bindings in this repo that use BOOL/bool patterns
rg -n "RespondsToSelector|class_respondsToSelector|respondsToSelector" libs/cua-driver/rust/crates/platform-macos -S
rg -n "extern \"C\" fn\\(.*\\) -> bool|-> bool;|c_schar|BOOL" libs/cua-driver/rust/crates/platform-macos/src -S

Repository: trycua/cua

Length of output: 6803


🌐 Web query:

Rust ABI extern "C" return type bool mapping to C _Bool/_Bool ABI rules (bool validity 0/1)

💡 Result:

In Rust, the bool type is guaranteed to have the same representation as the C99 _Bool type, and it is safe to use in extern "C" FFI declarations [1][2][3]. Key technical details regarding this mapping include: Representation and Validity: - Rust defines bool to have a size of 1 byte, with false represented by the bit pattern 0x00 and true by 0x01 [4][5]. - It is considered undefined behavior (UB) for a Rust bool to possess any bit pattern other than 0x00 or 0x01 [4]. - When interfacing with C, Rust's bool aligns with the C99 _Bool type, which is also implementation-defined but behaves identically on all platforms currently supported by Rust (where _Bool is typically 1 byte, with 0 for false and 1 for true) [2][5]. FFI Considerations: - While bool is FFI-safe, it is crucial that the C side also adheres to the expectation that only 0 and 1 are used [5]. If a C function returns a value other than 0 or 1 into a Rust bool, it will trigger undefined behavior in Rust [4]. - Because _Bool size and alignment can be implementation-defined in the C standard, reliance on bool for FFI assumes that the C compiler’s _Bool matches the platform’s ABI used by the Rust compiler [2][5]. In practice, this is universally true for Rust's supported targets [5]. - If you are dealing with C types that are not _Bool (such as Windows BOOL, which is typically a 4-byte int), you should not use Rust's bool directly, as the size mismatch will cause errors [6][7]. In such cases, use an explicit integer type (e.g., i32) on the Rust side and convert manually to preserve safety [6]. Summary: For standard C99 _Bool interoperability, using bool in extern "C" functions is stable, supported, and the recommended practice [8][1][2]. Ensure the C code treats its _Bool values as strictly 0 or 1 [5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant region in skylight.rs
sed -n '200,255p' libs/cua-driver/rust/crates/platform-macos/src/input/skylight.rs

# Find definitions/usages of find_sym / as_fn in the same file
rg -n "find_sym\\(|as_fn\\(" libs/cua-driver/rust/crates/platform-macos/src/input/skylight.rs

# Look for other Objective-C runtime symbol bindings in this repo that use BOOL/bool patterns
rg -n "class_respondsToSelector|respondsToSelector" libs/cua-driver/rust/crates/platform-macos -S
rg -n "extern \"C\" fn\\([^\\)]*\\)\\s*->\\s*bool" libs/cua-driver/rust/crates/platform-macos/src -S
rg -n "c_schar|signed char|\\bBOOL\\b|YES\\b|NO\\b" libs/cua-driver/rust/crates/platform-macos/src -S

Repository: trycua/cua

Length of output: 4891


🌐 Web query:

Apple headers typedef for BOOL (MacTypes.h / objc/objc.h) and signature/return type of class_respondsToSelector

💡 Result:

The BOOL type in Apple's Objective-C runtime (defined in <objc/objc.h>) is platform-dependent [1][2]. On 64-bit iOS and all watchOS platforms, it is a typedef for the C99 bool type [1][2]. On other platforms (including macOS), it is a typedef for signed char [1][2]. The definition is explicitly signed to ensure that the Objective-C type encoding for BOOL is always 'c' (rather than 'C'), regardless of whether -funsigned-char is used [1][3]. In contrast, the Boolean type found in <MacTypes.h> is a legacy type defined as an unsigned char (typedef unsigned char Boolean) [4]. The function class_respondsToSelector is a runtime function with the following signature [5][6]: BOOL class_respondsToSelector(Class cls, SEL sel); It accepts a class (cls) and a selector (sel) as arguments and returns a BOOL value: YES if instances of the class respond to the specified selector, and NO otherwise [5]. Apple documentation generally recommends using NSObject's instance method responds(to:) or the class method instancesRespond(to:) instead of this low-level runtime function [5][7].

Citations:


Fix FFI return type for class_respondsToSelector to match BOOL
In libs/cua-driver/rust/crates/platform-macos/src/input/skylight.rs (lines 221-226), RespondsToFn returns Rust bool, but Apple’s class_respondsToSelector returns Objective‑C BOOL, which is signed char on macOS. Use c_schar/i8 (or a BOOL wrapper) as the extern "C" return type and convert with != 0.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/cua-driver/rust/crates/platform-macos/src/input/skylight.rs` around
lines 221 - 226, The FFI typedef RespondsToFn currently returns Rust bool but
must match Objective‑C BOOL (signed char); change RespondsToFn to return
libc::c_schar or i8, adjust SYM initialization that uses find_sym and as_fn
accordingly, and when calling the function (the match arm that invokes f with
cls, sel) convert the returned c_schar to a Rust bool via != 0; update any
places assuming a direct bool return to use this explicit conversion.

None => false,
}
}

// ── SLSEventRecord extraction ──────────────────────────────────────────────

/// Extract the embedded `SLSEventRecord *` from a `CGEvent`.
Expand Down Expand Up @@ -244,11 +266,19 @@ pub fn post_to_pid(pid: pid_t, event_ptr: *mut c_void, attach_auth_message: bool

if attach_auth_message {
// Build and attach SLSEventAuthenticationMessage.
//
// macOS 14 (Sonoma) compatibility: the class exists on macOS 14 but
// `messageWithEventRecord:pid:version:` was added in macOS 15. Guard
// with `class_respondsToSelector` (a `!sel.is_null()` check is not
// enough — `sel_registerName` interns any name); when the selector is
// absent we skip the auth envelope and fall through to the plain
// `SLEventPostToPid` below. Chromium-class targets may not receive the
// event on macOS 14, but the daemon no longer crashes. See #1503.
let cls = objc_class(c"SLSEventAuthenticationMessage");
let sel = sel_register(c"messageWithEventRecord:pid:version:");
let factory = factory_msg_send_fn();

if !cls.is_null() && !sel.is_null() {
if class_responds_to_selector(cls, sel) {
if let Some(factory_fn) = factory {
let record = unsafe { extract_event_record(event_ptr) };
if !record.is_null() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,22 @@ public enum SkyLightEventPost {
"SLSEventAuthenticationMessage")
else { return nil }

// macOS 14 (Sonoma) compatibility: the class exists on macOS 14 but
// `messageWithEventRecord:pid:version:` was added in macOS 15.
// `NSSelectorFromString` always succeeds (it interns the string), so
// verify the class responds before storing it — otherwise `objc_msgSend`
// raises NSInvalidArgumentException at runtime and crashes the daemon.
// When absent, return nil so callers skip the auth envelope. See #1503.
let factorySelector = NSSelectorFromString(
"messageWithEventRecord:pid:version:")
guard messageClass.responds(to: factorySelector) else { return nil }

return Resolved(
postToPid: postToPid,
setAuthMessage: setAuth,
msgSendFactory: msgSend,
messageClass: messageClass,
factorySelector: NSSelectorFromString(
"messageWithEventRecord:pid:version:")
factorySelector: factorySelector
)
}()

Expand Down
Loading