copypasta icon indicating copy to clipboard operation
copypasta copied to clipboard

SIGSEGV on MacOS

Open sagacity opened this issue 4 years ago • 7 comments

I'm seeing crashes on MacOS Catalina (10.15.3) with Rust 1.43.1. This happens in the original clipboard crate as well, but since that seems unmaintained I'm reporting it here.

You can reproduce the crash by running cargo test in my fork.

You may need to run it a few times, but you will get different errors such as:

failing-517cc89e9cb2a459(35714,0x70000ebe5000) malloc: *** error for object 0x7f8eb170a6e0: pointer being freed was not allocated
failing-517cc89e9cb2a459(35714,0x70000ebe5000) malloc: *** set a breakpoint in malloc_error_break to debug

Or:

failing-517cc89e9cb2a459(35969,0x700000ae7000) malloc: Double free of object 0x7fe935c10a50
failing-517cc89e9cb2a459(35969,0x700000ae7000) malloc: *** set a breakpoint in malloc_error_break to debug

I'm unfortunately no MacOS developer so I have no idea what causes this, but it only seems to occur when I'm accessing the clipboard from both my test code and my regular (under test) code, like in some_other_fn in the code below. Moving the ClipboardContext to a shared static using something like lazy_static! does not help.

The test is fairly simple:

use copypasta::{ClipboardContext, ClipboardProvider};

fn some_other_fn() {
    let mut ctx = ClipboardContext::new().unwrap();
    ctx.get_contents().unwrap();
}

#[cfg(test)]
mod tests {
    use super::*;

    fn clear_clipboard() {
        let mut clipboard: ClipboardContext = ClipboardContext::new().unwrap();
        clipboard.set_contents("".into()).unwrap();
    }

    #[test]
    fn foo() {
        clear_clipboard();

        let mut ctx = ClipboardContext::new().unwrap();
        ctx.set_contents("Dummy".into()).unwrap();
        ctx.get_contents().unwrap();

        some_other_fn();
    }

    #[test]
    fn bar() {
        clear_clipboard();

        let mut ctx = ClipboardContext::new().unwrap();
        ctx.set_contents("Dummy".into()).unwrap();
        ctx.get_contents().unwrap();

        some_other_fn();
    }
}

sagacity avatar May 15 '20 10:05 sagacity

Ah, I've tested it now with cargo test -- --test-threads=1 and that works so it's probably a concurrency issue. I guess the MacOS code needs a RwLock. I'll see if I can contribute.

sagacity avatar May 15 '20 10:05 sagacity

I'll see if I can contribute.

That would be great, since I'm not much of a macOS developer either. It's probably also a good idea to check why exactly this is crashing, rather than just putting an RwLock on it without having a clear understanding of what exactly is going wrong.

chrisduerr avatar May 15 '20 13:05 chrisduerr

Fair enough, the PR was a bit premature :)

According to https://developer.apple.com/documentation/appkit/nspasteboard there is a general pasteboard that is shared by all applications. I'm not sure how the release works (when the Id goes out of scope), but the docs mention that releaseGlobally() on the pasteboard Releases the receiver’s resources in the pasteboard server. Could it be that the contents that I set in one instance of ClipboardContext are released when I try to retrieve it from another ClipboardContext?

sagacity avatar May 15 '20 13:05 sagacity

Could it be that the contents that I set in one instance of ClipboardContext are released when I try to retrieve it from another ClipboardContext?

I don't know how it works, sure it could be. However this needs to work cross-application too, so that would mean that if I free something in my application while you try to read in your application, it would segfault, which seems unlikely? Unless I understood you incorrectly.

chrisduerr avatar May 15 '20 13:05 chrisduerr

I meant I was wondering that maybe dropping the OSXClipboardContext struct is releasing the pasteboard for my entire running application instead of just for that OSXClipboardContext instance. But, again, that's mostly speculation on my end.

sagacity avatar May 15 '20 14:05 sagacity

According to this SO thread and Apple documentation on the topic, in Cocoa, "mutable objects are generally not thread-safe". I think this justifies putting the NSPasteboard in an RwLock at least?

I don't think releaseGlobally matters here—copypasta only uses the general pasteboard, and the Apple doc says

Although you must call this method to release a temporary, privately named pasteboard to avoid leaks, you should never call it on a standard pasteboard.

MattX avatar Jul 11 '21 17:07 MattX

I suspect the proper solution here is to only access the generalPasteboard from the main thread, as that's where all other macOS code would access it from as well.

madsmtm avatar Apr 30 '24 16:04 madsmtm