-
Notifications
You must be signed in to change notification settings - Fork 224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[breaking][core-graphics] Fix unsoundness in CGEventTap API #710
Conversation
20bd8dd
to
9b32d4a
Compare
9b32d4a
to
fb3708c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is a unfortunate limitation of the CGEventTapCreate
API (that it doesn't allow setting either CFMachPortContext
or "shouldFreeInfo
" when creating the mach port).
The solution here is completely correct, though I wonder if perhaps we could (ab)use CFMachPortSetInvalidationCallBack
to do the deallocation of the closure? This would make it so that we wouldn't have to keep the Box
around ourselves, but could instead release it in there - That way, we don't have to force the CFMachPortInvalidate
in new
(would still be required for with
), and we could allow the user to still access the mach_port
field directly.
@madsmtm Perhaps it could be future improvement? We had some publish requests recently. I want to see if we can update some crate versions this week. |
Not that much worth in an improvement in the future if we have already forced users to use |
Where? |
@waywardmonkeys I mean crates in whole repo. See comments in #706 |
You know I actually tried this first, but I don't think it can work. The invalidation callback only receives the The other possibility I see is to inject a custom allocator that frees the callback when all its allocated objects have been freed.
Actually, a simpler way of doing this might be to duplicate the mach port field and make one field private. We could invalidate the one in the private field, knowing that it hasn't been tampered with. |
Ah, I thought that was the case. Yeah, then there isn't any other way to do this. |
This should be ready to go in then. |
The user callback can be dropped while it is still referenced by the run loop. This PR fixes that by invaliding the event tap's mach port which prevents it from sending further events.
The
callback_ref
field was the wrong type. Of course it should never have been public, no one wants to access the callback to call it (and if they did they would probably segfault). In practice this just meant the "intermediate" box would leak on deallocation.Both fields have to be made private for the API to remain sound, because their lifetimes are tied together: the port holds a pointer to the callback. Otherwise the mach port can be swapped out and we would invalidate the wrong one. This led to the first actually unfortunate breakage, which is that everyone needs to call
.mach_port()
now.The second is that the callback passed to
CGEventTap::new()
must be'static
because otherwise the CGEventTap struct can be forgotten and the scope it references exited. An alternateCGEventTap::with()
API has been added that accepts a non-'static
callback.Below is a test case that segfaults with the existing API and does nothing with the new one.