canvas.createContext("webgl") returns null in Electron

I’m using electron-builder 19.49.2 with electron 1.8.1 and this is my snap config:

    "snap": {
      "confinement": "devmode",
      "summary": "Powerful and easy-to-use photo editor.",
      "grade": "devel",
      "stagePackages": [
        "default",
        "libcanberra-gtk-module",
        "unity-gtk-module-common",
        "libvips42"
      ],
      "plugs": [
        "home",
        "x11",
        "unity7",
        "browser-support",
        "network",
        "gsettings",
        "pulseaudio",
        "opengl"
      ]
    }

I’ve tried setting

const { app, BrowserWindow, ipcMain, autoUpdater } = require('electron')
app.commandLine.appendSwitch('ignore-gpu-blacklist', 'true')

to get around any GPU blacklisting in my main.js file, but webgl still doesn’t work. The app works on the system I built it on in devmode, but when I run it in a VM, canvas.createContext('webGL') always fails.

If I assign window.location="chrome://gpu/" in the web console, I get

I checked the security log (https://docs.snapcraft.io/build-snaps/debugging) and received

$ sudo /snap/bin/snappy-debug.security scanlog polarr
INFO: following '/var/log/syslog'. If have dropped messages, use:
INFO: $ sudo journalctl --output=short --follow --all | sudo snappy-debug.security scanlog
kernel.printk_ratelimit = 0
= AppArmor =
Time: Dec 17 22:34:40
Log: apparmor="DENIED" operation="dbus_method_call"  bus="system" path="/" interface="org.freedesktop.DBus.ObjectManager" member="GetManagedObjects" mask="send" name="org.bluez" pid=26725 label="snap.polarr.polarr" peer_pid=1194 peer_label="unconfined"
DBus access

= AppArmor =
Time: Dec 17 22:34:40
Log: apparmor="DENIED" operation="file_mmap" profile="snap.polarr.polarr" name="/dev/nvidiactl" pid=26914 comm="polarr" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
File: /dev/nvidiactl (mmap)
Suggestion:
* verify program isn't using an executable stack: https://forum.snapcraft.io/t/snap-and-executable-stacks/1812

= AppArmor =
Time: Dec 17 22:47:57
Log: apparmor="ALLOWED" operation="dbus_signal"  bus="system" path="/org/bluez/hci0/dev_A8_5C_2C_95_4C_E8" interface="org.freedesktop.DBus.Properties" member="PropertiesChanged" name=":1.18" mask="receive" pid=4215 label="snap.polarr.polarr" peer_pid=1194 peer_label="unconfined"
DBus access
Suggestion:
* try adding 'bluez' to 'plugs'

= AppArmor =
Time: Dec 17 22:47:57
Log: apparmor="ALLOWED" operation="dbus_signal"  bus="system" path="/" interface="org.freedesktop.DBus.ObjectManager" member="InterfacesAdded" name=":1.18" mask="receive" pid=4215 label="snap.polarr.polarr" peer_pid=1194 peer_label="unconfined"
DBus access

I’m not really sure why my electron is messing with bluez, but I see the same logs from @popey in Chromium snap doesn't work with the nvidia proprietary driver. I’m just guessing, but I think what’s really killing the webgl context is the deny on /dev/nvidiactl.

I see that there’s some ongoing work with nvidia in snaps:

Is all I can do at this point just wait for snaps to support NVIDIA? If that’s the case, how long will I have to wait and how will I know that this has been taken care of so that I can move forward with packaging my app for Ubuntu?

Bluez API calls may come from this:

Should I add bluez to the list of plugs, or is it safe to not do that so long as I don’t want to mess with the user’s bluetooth? My main problem is that I’m failing to get a webgl context for a webgl application.

You probably don’t want to add bluez to your plugs, the webbluetooth API is somewhat questionable and sparked some controversies:

The nvidia thing needs to be looked at by someone who might now why the driver might be doing a mmap.

@jdstrand does this change make sense?

diff --git a/interfaces/builtin/opengl.go b/interfaces/builtin/opengl.go
index 93ccfa3e1..0e8b50747 100644
--- a/interfaces/builtin/opengl.go
+++ b/interfaces/builtin/opengl.go
@@ -59,7 +59,7 @@ const openglConnectedPlugAppArmor = `
 /etc/vdpau_wrapper.cfg r,
 @{PROC}/driver/nvidia/params r,
 @{PROC}/modules r,
-/dev/nvidia* rw,
+/dev/nvidia* rwm,
 unix (send, receive) type=dgram peer=(addr="@nvidia[0-9a-f]*"),
 
 # eglfs

Did you read this to see if your snap is affected?

Almost certainly, no it doesn’t. Please see Snap and executable stacks

@jdstrand In short, that solved my problem. Thanks.

In long, I was able to run

watch -n 1 execstack -c ./dist/linux-unpacked/polarr

when snapping my application with electron-builder and was lucky enough that the execstack bit got cleared between when electron-builder produced the binary and primed it into the snap.

Is this a problem with electron-builder or just the way I’m using it? How do I set up the build in a correct way to not have execstack set?

When I boot into my Ubuntu 17.10 partition and run the same snap that I built and had work on 16.04, I once again fail to get a webgl context. The only notable difference is that I no longer get an nvidia message in the snappy-debug.security log. There is still the one log about bluez, but I would guess that that isn’t preventing webgl from working.

If I install the app’s dependencies (libgconf-2-4 and libvips42) and run the binary in the linux-unpacked directory directly, it works fine. It also works when I use electron-packager to package the app as a deb and then install that.

I’m not sure what to do now since there’s nothing in the snappy-debug.security log for me to fix but the app works iff not snap.

That’s a great question. It would be fantastic for someone in the electron community to look into this. It shouldn’t be needed (eg, chromium from Ubuntu and chrome from Google don’t have executable stacks). Snapcraft could make this easier (see my comment here: AppArmor denial for /dev/nvidiactl, but ideally it would be fixed in the electron build process.

I see on AppArmor denial for /dev/nvidiactl that electron builds occasionally have binaries with executable stacks. I have dlib and libraw C++ nodejs addons, so I use npm rebuild and node-gyp rebuild:

ELECTRON_VERSION="1.8.1";

npm rebuild \
  --runtime=electron \
  --target=${ELECTRON_VERSION} \
  --disturl=https://atom.io/download/atom-shell \
  --build-from-source
cd dlib
node-gyp rebuild \
  --runtime=electron \
  --target=${ELECTRON_VERSION} \
  --disturl=https://atom.io/download/atom-shell \
  --build-from-source
cd ..
cd libraw
node-gyp rebuild \
  --runtime=electron \
  --target=${ELECTRON_VERSION} \
  --disturl=https://atom.io/download/atom-shell \
  --build-from-source
cd ..

Since I’m using npm rebuild, am I responsible for configuring npm and gyp so that they don’t produce binaries with executable stacks? Or is electron responsible for the way that it npm rebuilds to not have an executable stack? When I get home I’ll try using a version of electron other than 1.8.1 to see if it produces an binary without an executable stack.

I tried a variety of Electron versions electron versions

npm install --save electron@1.7.1 && find node_modules/electron/ | xargs execstack 2>/dev/null | grep X 
npm install --save electron@1.2.1 && find node_modules/electron/ | xargs execstack 2>/dev/null | grep X 
npm install --save electron@1.2.0 && find node_modules/electron/ | xargs execstack 2>/dev/null | grep X 
# ... etc ...

All revisions that existed had an execstack for node_modules/electron/dist/electron. In total, I tried 1.7.1, 1.2.1, 1.2.0, 1.3.0, 1.5.0, 1.5.1, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5, 1.6.6, 1.6.7, 1.6.8, 1.6.9, 1.6.10, 1.7.7, 1.7.8, 1.7.9, and 1.8.1.

I tried clearing execstack on 1.4.3 with

watch -n 0.1 execstack -c ./dist/linux-unpacked/polarr

and that version also lacked WebGL support.

I’ve created a simple electron WebGL app for testing. It supports WebGL when run with npm start but does not support WebGL when run as a snap:

Can I get an ETA on WebGL support in Snap? Electron is listed as a supported language in the Electron docs https://docs.snapcraft.io/build-snaps/electron. At this time, there is no way to publish Electron WebGL applications to the Ubuntu Store since .deb isn’t allowed anymore and WebGL doesn’t work in Snap yet. If WebGL Snap support is going to take a long time that’s okay, but if that’s the case I would like for the deb that I’ve packaged to go into the Ubuntu Store in the interim.

@oSoMoN - is this something you could help with? I looked at the chromium snap which doesn’t have any executable stack binaries and put in chrome://gpu and it lists WebGL as available on my 17.10 Intel system (I don’t have an nVidia system).

What is the output of snap version? If it isn’t 2.30 or higher, can you try with the snap from beta (sudo snap refresh core --beta)?

If you launch the snap from a terminal, what are the differences with the terminal output between devmode and strict?

If you install the snap with an executable stack, and then you add this to /var/lib/snap/apparmor/profiles/snap.polarr.polarr:

  /dev/nvidiactl m,

then run sudo apparmor_parser -r /var/lib/snap/apparmor/profiles/snap.polarr.polarr, then rerun your application, does it make a difference (I don’t expect it will, but it will definitively rule out execstack)?

Does stracing the snap reveal anything interesting? Note that sometimes it is handy to compare an strace from running in devmode and running in strict mode.

I tried these two suggestions with the electron-snap-webgl test app above and was still unable to get a webgl context.

strace snap run webgl (devmode): https://drive.google.com/open?id=1oQbv_53yjyP0l3egQgZi2ZKIkGG4o5we
strace snap run webgl (strict): https://drive.google.com/open?id=1pSQYqAspKEeMQ6-Y_jegX-weIP-z53Ss
The app crashes before producing a GUI when run with strace. You can diff those two files, and the diffs line up pretty well. Right before the crash in strict mode, the electron app tries to access the .Xauthority file in my home directory and is denied, but it is able to access that file in devmode and continues running:

access("/home/amiller/.Xauthority", R_OK) = -1 EACCES (Permission denied)

I’ll update my Apparmor for the webgl test to see if the deny on the .Xauthority file is what’s causing the Electron GPU process to exit with code 1024.

I changed the permissions so that the webgl snap would be able to access the .Xauthority file in my home directory in strict mode. This had the effect of making the snap not die on startup with strict mode (it never died in devmode), but it did not make webgl work in the snap when the snap continued to run.

This is the log of the webgl snap when run with read permissions granted on /home/amiller/.Xauthority:

Does publicly posting these strace logs pose a security risk to me, and if so, what do I need to do to re-secure my system?

Do you need any additional information from me to make WebGL work in Electron when packaged with Snap?

If your snap needs to access ~/.Xauthority, then you should plugs the ‘x11’ interface. If your snap is plugging the x11 interface and you have no logged security denials, I suspect your ~/.Xauthority file may have incorrect ownership (eg, root:root 0600).

Looking at the strace, it seems you are running your snap under sudo without the -H option which would explain the ~/.Xauthority denial: the snap is running with uid 0 but the /home/amiller/.Xauthority file is owned by your uid, and appamror is enforcing owner match. What happens if, after adjusting the permissions on ~/.Xauthority as necessary, you run the snap as non-root or you run the snap as root with ‘sudo -H …’?

Also, you should be seeing security denials in the logs, but haven’t posted them. Can you paste the output of journalctl | grep audit for any denials after trying the above (alternatively, you can run sudo journalctl --output=short --follow --all | sudo snappy-debug.security scanlog and paste the output after trying the above).

They shouldn’t be a problem unless you are doing something like putting sensitive items in your environment, accessing file paths you’d rather not have people know, etc. If you are going to paste, you might choose to clean them up a bit by either picking the syscalls you know you are interested in (hard to know in this case) or to remove ones that are noisy. Eg, you might also exclude: recvmsg,poll,writev.

I noticed your root running process trying to access pulseaudio files in /run/user/1000/… which would also be disallowed which certainly might cause your snap to misbehave.

I was only running as root so that I could run strace. The application wouldn’t normally be run as root and it doesn’t die on .Xauthority when not run as root.

Thanks for clarifying that.