Bug 231670 - [GLIB] Simplify SleepDisabler by checking if we are under sandbox
Summary: [GLIB] Simplify SleepDisabler by checking if we are under sandbox
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Platform (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Nobody
URL:
Keywords: Gtk, InRadar
Depends on:
Blocks:
 
Reported: 2021-10-13 05:43 PDT by Carlos Garcia Campos
Modified: 2021-10-18 13:20 PDT (History)
13 users (show)

See Also:


Attachments
Patch (20.19 KB, patch)
2021-10-13 05:52 PDT, Carlos Garcia Campos
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
Patch (20.80 KB, patch)
2021-10-13 06:01 PDT, Carlos Garcia Campos
no flags Details | Formatted Diff | Diff
Patch (22.69 KB, patch)
2021-10-18 01:54 PDT, Carlos Garcia Campos
aperez: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Carlos Garcia Campos 2021-10-13 05:43:28 PDT
Current implementation always tries to connect to ScreenSaver interface and if that fails tries with the portal one. It would be simpler to check if we are under sandbox and it avoids a connection that we know it's always going to fail.
Comment 1 Carlos Garcia Campos 2021-10-13 05:52:47 PDT
Created attachment 441062 [details]
Patch
Comment 2 Carlos Garcia Campos 2021-10-13 06:01:41 PDT
Created attachment 441063 [details]
Patch
Comment 3 Michael Catanzaro 2021-10-13 07:57:55 PDT
Comment on attachment 441063 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=441063&action=review

> Source/WTF/ChangeLog:10
> +        whether portal sandbox should be used. WEBKIT_USE_PORTAL environment variable can be used to force the use of
> +        portals when not running under the sandbox.

Hmmmm. I do like the changes to SleepDisabler.cpp. Picking one API or the other to use does seem nicer. Thing is, many of the portals are good to use always, including when not sandboxed. So checking whether we're sandboxed first isn't *generally* recommended or necessary. I would normally actually try the portal API first, since that is the more modern API, and use the original API only as a fallback if the portal API is not available. But I'm pretty sure the inhibit portal is different and just does not work at all if the application is not sandboxed: it will fail if it can't read the non-falsifiable app ID from ./flatpak-info or wherever snap stores it. So while some portals are OK to use always, this one is not. Then there are some other portals that you *really* want to use always. E.g. for WebRTC stuff, we would always prefer to use the portal for desktop sharing or webcam access, because the portals are the preferred method to talk to Pipewire. I could imagine Pipewire might decide to claim v4l2 for itself in the future, to block other apps from using it and ensure it's always available for Pipewire. Another example: some apps that relied on gnome-shell's screen recorder D-Bus API instead of using the portal are just now discovering that they're blocked in GNOME 41. So we have:

 * Some portals that we always want to use, where using the original non-portal APIs is undesirable even when not sandboxed
 * Some portals that are OK to use when not sandboxed, but also OK to ignore and use the original host APIs
 * Some portals that must only be used when sandboxed and will fail when not sandboxed (pretty sure this is true for the Inhibit portal)

It seems like an xdg-desktop-portal design failure to have three different cases here, but whatever. The implication for this patch is that having WTF::shouldUsePortal probably doesn't make sense, since the right behavior depends on the particular portal in question. I thought about suggesting that you move it to SleepDisabler.cpp instead, and have that as a policy decision specific to the SleepDisabler code rather than assuming it will make sense for every use of any desktop portal anywhere in WebKit. But maybe it would be simpler to just rename it to WTF::mustUsePortal (or WTF::mustUseDesktopPortal) to indicate only the case where you really always have to use portals. Then code that talks to portals can decide for itself whether or not to use WTF::mustUsePortal/mustUseDesktopPortal, since this naming implies it is OK to use the portal anyway even if it returns false, whereas the name WTF::shouldUsePortal implies the code should use the portal only if it returns true.

> Source/WTF/wtf/glib/Sandbox.cpp:27
> +#include <wtf/glib/Sandbox.h>

Alternative names:

wtf/glib/Containers.h?
wtf/glib/IsInside.h?
wtf/glib/MustUseDesktopPortal.h?

> Source/WTF/wtf/glib/Sandbox.cpp:35
> +    return g_file_test("/.flatpak-info", G_FILE_TEST_EXISTS);

Hm, it sould test for snap too, since we should also generally use portals when running under snap. But not under docker, since portals do not expect to be used from docker, even though docker is also a sandbox.

I think I would move isInsideDocker, isInsideFlatpak, and isInsideSnap to here from ProcessLauncherGLib.cpp, then rename shouldUsePortal to mustUseDesktopPortal, and have it check isInsideFlatpak || isInsideSnap, but not isInsideDocker.

> Source/WebCore/PAL/ChangeLog:9
> +        one. It's simpler to check if we are under sandbox and it avoids a connection that we know it's always going to

is always going
Comment 4 Carlos Garcia Campos 2021-10-14 00:47:40 PDT
(In reply to Michael Catanzaro from comment #3)
> Comment on attachment 441063 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=441063&action=review
> 
> > Source/WTF/ChangeLog:10
> > +        whether portal sandbox should be used. WEBKIT_USE_PORTAL environment variable can be used to force the use of
> > +        portals when not running under the sandbox.
> 
> Hmmmm. I do like the changes to SleepDisabler.cpp. Picking one API or the
> other to use does seem nicer. Thing is, many of the portals are good to use
> always, including when not sandboxed.

Why? If I'm not wrong, using the portal normally requires more DBus messages than using the original DBus service directly.

> So checking whether we're sandboxed
> first isn't *generally* recommended or necessary.

I took this approach from GTK4 for what is worth.

> I would normally actually
> try the portal API first, since that is the more modern API, and use the
> original API only as a fallback if the portal API is not available. But I'm
> pretty sure the inhibit portal is different and just does not work at all if
> the application is not sandboxed: it will fail if it can't read the
> non-falsifiable app ID from ./flatpak-info or wherever snap stores it. So
> while some portals are OK to use always, this one is not. Then there are
> some other portals that you *really* want to use always. E.g. for WebRTC
> stuff, we would always prefer to use the portal for desktop sharing or
> webcam access, because the portals are the preferred method to talk to
> Pipewire. I could imagine Pipewire might decide to claim v4l2 for itself in
> the future, to block other apps from using it and ensure it's always
> available for Pipewire. Another example: some apps that relied on
> gnome-shell's screen recorder D-Bus API instead of using the portal are just
> now discovering that they're blocked in GNOME 41. So we have:
> 
>  * Some portals that we always want to use, where using the original
> non-portal APIs is undesirable even when not sandboxed
>  * Some portals that are OK to use when not sandboxed, but also OK to ignore
> and use the original host APIs
>  * Some portals that must only be used when sandboxed and will fail when not
> sandboxed (pretty sure this is true for the Inhibit portal)
> 
> It seems like an xdg-desktop-portal design failure to have three different
> cases here, but whatever. The implication for this patch is that having
> WTF::shouldUsePortal probably doesn't make sense, since the right behavior
> depends on the particular portal in question. I thought about suggesting
> that you move it to SleepDisabler.cpp instead, and have that as a policy
> decision specific to the SleepDisabler code rather than assuming it will
> make sense for every use of any desktop portal anywhere in WebKit. But maybe
> it would be simpler to just rename it to WTF::mustUsePortal (or
> WTF::mustUseDesktopPortal) to indicate only the case where you really always
> have to use portals. Then code that talks to portals can decide for itself
> whether or not to use WTF::mustUsePortal/mustUseDesktopPortal, since this
> naming implies it is OK to use the portal anyway even if it returns false,
> whereas the name WTF::shouldUsePortal implies the code should use the portal
> only if it returns true.
> 
> > Source/WTF/wtf/glib/Sandbox.cpp:27
> > +#include <wtf/glib/Sandbox.h>
> 
> Alternative names:
> 
> wtf/glib/Containers.h?
> wtf/glib/IsInside.h?
> wtf/glib/MustUseDesktopPortal.h?

What's wrong with Sandbox.h?

> > Source/WTF/wtf/glib/Sandbox.cpp:35
> > +    return g_file_test("/.flatpak-info", G_FILE_TEST_EXISTS);
> 
> Hm, it sould test for snap too, since we should also generally use portals
> when running under snap. But not under docker, since portals do not expect
> to be used from docker, even though docker is also a sandbox.
> 
> I think I would move isInsideDocker, isInsideFlatpak, and isInsideSnap to
> here from ProcessLauncherGLib.cpp, then rename shouldUsePortal to
> mustUseDesktopPortal, and have it check isInsideFlatpak || isInsideSnap, but
> not isInsideDocker.
>
> > Source/WebCore/PAL/ChangeLog:9
> > +        one. It's simpler to check if we are under sandbox and it avoids a connection that we know it's always going to
> 
> is always going
Comment 5 Michael Catanzaro 2021-10-14 06:11:22 PDT
(In reply to Carlos Garcia Campos from comment #4)
> Why? If I'm not wrong, using the portal normally requires more DBus messages
> than using the original DBus service directly.

It doesn't matter. I normally prefer to use newer things and fall back to older things only if the newer thing is not present. If the newer API works in all cases, that keeps the code simpler. But in this case, the newer API does not work in all cases, so you really need to choose, which is why your changes to SleepDisablerGLib.cpp look good.

> > So checking whether we're sandboxed
> > first isn't *generally* recommended or necessary.
> 
> I took this approach from GTK4 for what is worth.

Right, but GTK does not do screen sharing or camera access, so our needs are different.

> What's wrong with Sandbox.h?

The naming is OK as your patch is now, but it won't make as much sense aftre my suggested changes.
Comment 6 Carlos Garcia Campos 2021-10-14 06:17:50 PDT
(In reply to Michael Catanzaro from comment #5)
> (In reply to Carlos Garcia Campos from comment #4)
> > Why? If I'm not wrong, using the portal normally requires more DBus messages
> > than using the original DBus service directly.
> 
> It doesn't matter. I normally prefer to use newer things and fall back to
> older things only if the newer thing is not present. If the newer API works
> in all cases, that keeps the code simpler. But in this case, the newer API
> does not work in all cases, so you really need to choose, which is why your
> changes to SleepDisablerGLib.cpp look good.

I don't think it's a matter of new vs old APIs, AFAIK portals where created for sandboxed applications, no?

> > > So checking whether we're sandboxed
> > > first isn't *generally* recommended or necessary.
> > 
> > I took this approach from GTK4 for what is worth.
> 
> Right, but GTK does not do screen sharing or camera access, so our needs are
> different.

If we prefer to use portals for screen sharing and camera access we can simply avoid the shouldUsePortal check in those particular cases, since we already know we should use portals.
 
> > What's wrong with Sandbox.h?
> 
> The naming is OK as your patch is now, but it won't make as much sense aftre
> my suggested changes.

Why? Your suggestions are still related to sandboxing, no?
Comment 7 Michael Catanzaro 2021-10-14 07:13:41 PDT
(In reply to Carlos Garcia Campos from comment #6)
> If we prefer to use portals for screen sharing and camera access we can
> simply avoid the shouldUsePortal check in those particular cases, since we
> already know we should use portals.

OK, fine.

Note: something is wrong with the API tests EWS.

(In reply to Carlos Garcia Campos from comment #6)
> Why? Your suggestions are still related to sandboxing, no?

I suppose. Testing for containerization frameworks vs. testing for actual sandboxing is a little different, though.
Comment 8 Michael Catanzaro 2021-10-15 08:19:21 PDT
Comment on attachment 441063 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=441063&action=review

>>> Source/WTF/wtf/glib/Sandbox.cpp:35
>>> +    return g_file_test("/.flatpak-info", G_FILE_TEST_EXISTS);
>> 
>> Hm, it sould test for snap too, since we should also generally use portals when running under snap. But not under docker, since portals do not expect to be used from docker, even though docker is also a sandbox.
>> 
>> I think I would move isInsideDocker, isInsideFlatpak, and isInsideSnap to here from ProcessLauncherGLib.cpp, then rename shouldUsePortal to mustUseDesktopPortal, and have it check isInsideFlatpak || isInsideSnap, but not isInsideDocker.
> 
> 

Anyway, you've convinced me on everything else, but still need to fix isRunningInSandbox. At minimum, test for snap as well. But probably better to delete it and just directly call isInsideFlatpak/isInsideSnap from shouldUsePortal, and not have isRunningInSandbox at all.

Definitely do not implement isInsideFlatpak using isRunningInSandbox.
Comment 9 Carlos Garcia Campos 2021-10-16 01:32:15 PDT
(In reply to Michael Catanzaro from comment #8)
> Comment on attachment 441063 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=441063&action=review
> 
> >>> Source/WTF/wtf/glib/Sandbox.cpp:35
> >>> +    return g_file_test("/.flatpak-info", G_FILE_TEST_EXISTS);
> >> 
> >> Hm, it sould test for snap too, since we should also generally use portals when running under snap. But not under docker, since portals do not expect to be used from docker, even though docker is also a sandbox.
> >> 
> >> I think I would move isInsideDocker, isInsideFlatpak, and isInsideSnap to here from ProcessLauncherGLib.cpp, then rename shouldUsePortal to mustUseDesktopPortal, and have it check isInsideFlatpak || isInsideSnap, but not isInsideDocker.
> > 
> > 
> 
> Anyway, you've convinced me on everything else, but still need to fix
> isRunningInSandbox. At minimum, test for snap as well. But probably better
> to delete it and just directly call isInsideFlatpak/isInsideSnap from
> shouldUsePortal, and not have isRunningInSandbox at all.
> 
> Definitely do not implement isInsideFlatpak using isRunningInSandbox.

isInsideFlatpak is not exactly the same in this case, that caches the result and it's used by the process launcher to see if we are already under flatpak. If we are not under flatpak false will be cached, but then the bubblewrap launcher will create the flatpak-info, but isInsideFlatpak will return false. That's why isInsideSandbox doesn't cache the result.
Comment 10 Michael Catanzaro 2021-10-16 09:37:26 PDT
(In reply to Carlos Garcia Campos from comment #9)
> isInsideFlatpak is not exactly the same in this case, that caches the result
> and it's used by the process launcher to see if we are already under
> flatpak. 

> If we are not under flatpak false will be cached, but then the
> bubblewrap launcher will create the flatpak-info, but isInsideFlatpak will
> return false. That's why isInsideSandbox doesn't cache the result.

The .flatpak-info is created in the filesystem namespace of the sandboxed subprocess that is going to be launched. The UI process won't see it. Both isInsideSandbox and isInsideFlatpak would behave the same: you're just doing extra work in the case of isInsideSandbox.

You can safely change isInsideSandbox to cache its result.
Comment 11 Carlos Garcia Campos 2021-10-18 01:54:05 PDT
Created attachment 441571 [details]
Patch
Comment 12 Adrian Perez 2021-10-18 05:51:06 PDT
Comment on attachment 441571 [details]
Patch

I left some comments about the helper functions, but as the code was just
moved around it's fine—but if you feel like changing them before landing,
that would be neat :)


View in context: https://bugs.webkit.org/attachment.cgi?id=441571&action=review

> Source/WTF/wtf/glib/Sandbox.cpp:39
> +    return *ret;

It should not be needed to use std::optional here, you can just:

   bool isInsideFlatpak()
   {
       static bool ret = g_file_test("/.flatpak-info", G_FILE_TEST_EXISTS);
       return ret;
   }

The C++11 specification guarantees that a static local is initialized only
once when program control flow goes through it, and that it is thread-safe
(amusingly, manually checking the optional is not thread-safe :D)

The same applies to the other checks below.

> Source/WTF/wtf/glib/Sandbox.cpp:70
> +    return usePortal[0] != '0';

This one is a bit trickier, but one can use a lambda that gets invoked
immediately to initialize the static value:

    bool shouldUsePortal() {
        static bool ret = ([]() -> bool {
            // do checks, return true/false
        })();
        return ret;
    }
Comment 13 Carlos Garcia Campos 2021-10-18 06:18:43 PDT
Committed r284370 (243155@main): <https://commits.webkit.org/243155@main>
Comment 14 Radar WebKit Bug Importer 2021-10-18 06:19:18 PDT
<rdar://problem/84367775>
Comment 15 Michael Catanzaro 2021-10-18 13:20:04 PDT
(In reply to Adrian Perez from comment #12)
> The C++11 specification guarantees that a static local is initialized only
> once when program control flow goes through it, and that it is thread-safe
> (amusingly, manually checking the optional is not thread-safe :D)

Remember Apple ports use some compiler flag to disable the threadsafety guarantee (presumably it has a noticeable perf impact?), so you can only rely on this in Linux-specific code, never in cross-platform code. (In this case, it's fine only because these functions are GLib-specific.)