copypasta
copypasta copied to clipboard
SIGSEGV on MacOS
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();
}
}
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.
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.
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
?
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.
I meant I was wondering that maybe drop
ping 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.
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.
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.