gtk-rs / gtk-rs-core

Rust bindings for GNOME libraries
https://gtk-rs.org/gtk-rs-core
MIT License
272 stars 103 forks source link

gio::Application::get_default() is unsound #44

Open sdroege opened 5 years ago

sdroege commented 5 years ago

It allows retrieving the application from any thread, not just the main thread (or more general the thread where the application was created). gio::Application is not thread-safe, so this is a problem.

alatiera commented 5 years ago

Would it be possible to add a check that make it so it only returns if called from the thread gtk was initialized from?

I mean, would this be proper workaround/fix?

sdroege commented 5 years ago

We don't have any gtk in gio :) a fix, half at least, would be to check if the default main context is owned by this thread. There's a function for that

alatiera commented 5 years ago

We don't have any gtk in gio

ah, of course, meant to say GMainContext

sdroege commented 5 years ago

MainContext::is_owner() is what I was thinking of. This will return false while application.run() is not running though.

sdroege commented 3 years ago

We can probably add into qdata of the Application the current thread id whenever we see it first in Rust, and later always check it and if it differs we panic or return None or similar.

jf2048 commented 2 years ago

It seems this can never be completely safe because g_application_set_default is not thread safe, and that gets called internally by GApplication implementations. I guess what that means is, a GApplication can only be referenced in the main thread, but we don't have any way of knowing what the main thread really is, getting the current thread id is just a guess.

It could be made a little more robust by checking if we can acquire the main context before storing the qdata, but that still isn't completely safe in all cases because it only checks for a running application... I wonder if set_default and get_default should just be unsafe functions, but that also won't completely solve it because another GApplication could be created from another thread which is also unsafe

sdroege commented 2 years ago

Yeah the problem with making get_default() unsafe is that it's a very useful API to have in GTK applications and requiring those to use unsafe code is not ideal (set_default() is fine IMHO, who needs that anyway...)

felinira commented 1 year ago

I think I have a way forward:

jf2048 commented 1 year ago

That will not fully make it sound but will probably correct most of the issues people have. The wrapper macro will need some modifications here to do the thread checking so we may need to see https://github.com/gtk-rs/gtk-rs-core/issues/916 for that

sdroege commented 1 year ago

I think the easiest would be to implement default() manually for now and implement the same assertion that is also used in e.g. glib::MainContext::spawn_local(). That should cover the easiest mistakes at least.

More correct would be to forbid construction of a gio::Application from another thread but that requires glib::wrapper! macro changes as @jf2048 says, and not very easy ones.

Does someone want to do the easy part for now?

jf2048 commented 1 year ago

I think the easiest would be to implement default() manually for now and implement the same assertion that is also used in e.g. glib::MainContext::spawn_local(). That should cover the easiest mistakes at least.

How can we do that, since Application does not reference a MainContext? I think any solution will need to modify the wrapper and do the qdata solution described above.

sdroege commented 1 year ago

Can you actually use an Application on a non-default main context?