Ubuntu, Apparmor, Firefox and Webrender

TL;DR

This blog post is quite rambly and reconstructs the journey in fixing the problems with Firefox Hardware Acceleration and DRM content.

Jump to the Conclusion at the end to get a summary of what was the problem and how it can be fixed.

Background

My current Linux installation is a Ubuntu 22.04 system. After the update to 22.04., I noticed degraded performance with Firefox. It was slower and using sites with DRM content like Netflix and Spotify crashed the Widevine plugin.

This was around mid of 2022 I think? I found out that starting with Ubuntu 22.04., Firefox would be a Snap package. As I’ve made bad experiences with Snap packages and did not have a good experience with Firefox Snap, I searched for alternative solutions.

What I found is the official Mozilla PPA, which provides Firefox and Thunderbird in non-Snap version. With some package pinning, I used that instead of the Snap package and was happier than before, albeit not completely happy: performance got better, but DRM content was still crashing and I could not find a solution for that.

Back to the present

I’m currently using D&D Beyond for managing my characters and a new campaign will start soon were we will probably use that, too. The problem with that - besides the current OGL controversy, see #OpenDND - is the abyssmal performance of the dice rolling. My laptop is just seconds away from taking off into the air while I still get only 10 FPS or so?

A single d20 takes a good 33 seconds to fully roll with display of the result. Here is video evidence for your entertainment:

That was the last straw needed and I took off yesterday and today to dig into the issue.

Hardware Acceleration and WebRender

A coworker mentioned that there are some settings regarding to Hardware Video Acceleration and the WebRender engine described in the Archwiki, so I took a look at it. Looking at about:support, I saw that the Software WebRender is used. Which is bad, as it is using llvmpipe for software rendering using the CPU without using the GPU for help.

In this state, forcing Firefox to use WebRender regardless with setting gfx.webrender.all=true, Firefox just crashes when rolling dice.

All neccessary drivers were installed, VA-API and VDPAU were configured correctly and so Hardware Video Acceleration was working in general.

Checking out a Manjaro Live USB for comparison

This is not my first rodeo with quirky settings, so I booted an older Manjaro Live USB stick I still had ready and lo and behold, Firefox there showed under about:support that WebRender was working fine, everything is enabled, have a nice day.

Sure enought, rolling dice on D&D Beyond worked very, very smoothly and fast. To my surprise, using Netflix and Spotify was also also possible. At this point I thought that X11 or Wayland were not picking up the correct drivers, but that worked fine so I thought of something new.

Trying Firefox tarball and Firefox Flatpak

In my desperation, I downloaded the current Firefox 109 as a tarball, extracted it and… everything just worked?? What the hell. To be sure, I installed the Firefox Flatpak version and sure enough, everything worked there, too. Netflix, Spotify, dice rolling - all I ever wanted.

What was the difference? I tried running all of these version from the command line and there I saw some errors regarding MESA not being able to load the driver and Firefox not being able to create a DRI3 or DRI2 screen.

Exploring Firefox on Ubuntu

At this point I could concentrate on Firefox from PPA itself, as my system per se was perfectly capable of using Hardware Acceleration and WebRender with a differently packaged Firefox.

When installing PPA Firefox, /usr/bin/firefox is a symlink to /usr/lib/firefox/firefox.sh, which handles things like handling Wayland mode and debugging and at last executes /usr/lib/firefox/firefox after everything was set up.

I ran /usr/lib/firefox/firefox manually while providing environment variables to get debug output via LIBGL_DEBUG=verbose MESA_DEBUG=1 and there it said that it could not load a file altough it existed.

It was at this point that I had another suspicion who the culprit might be.

Enter AppArmor

AppArmor is a security mechanism activated by default in Ubuntu. It can help with securing applications utilizing a profile that determines what an application is allowed to read, write or access in general. This is a simplification, but that works well enough for this blog post.

I’ve encountered that pattern of “an existing file can not be accessed for some reason” a while ago and associate it deeply with AppArmor. Sure enough, after checking the output of sudo aa-status, I saw that there is a Firefox profile and that the current Firefox processes are in a so-called enforce mode, which means that all allowed accesses are determined by the AppArmor profile.

In the past, I disabled AppArmor completely because I couldn’t be bothered. But now I had the time and energy to get into AppArmor configuration and do it right.

First, I had to confirm that it actually is AppArmor’s fault. This can be done in two ways:

  • by completely stopping the service with sudo systemctl sop apparmor.service
  • by switching the profile to complain mode with sudo aa-complain usr.bin.firefox

Complain mode has the advantage of allowing everything the application wants to do, but logging all requested accesses for later fixing. Lo and behold, if I put the Firefox profile in complain mode, everything works: dice rolling, WebRender, Netflix, Spotify.

Now, how to fix this?

Fixing the AppArmor profile

The AppArmor profile for Firefox is located in /etc/apparmor.d/usr.bin.firefox and provides an automatically loaded extension point in /etc/apparmor.d/local/usr.bin.firefox. Parsing the existing profile and /etc/apparmor.d, I saw that some common configuration are provided as abstractions.

Fixing Hardware Acceleration

Viewing the log messages with sudo journalctl -fx, the errors mentioned above regarding MESA and the DRI screens are preceded by Firefox trying to access two files for a PCI device:

audit[99316]: AVC apparmor="DENIED" operation="open" profile="firefox" name="/sys/devices/pci0000:00/0000:00:02.0/revision" pid=99316 comm
="firefox" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0                                                                                                                          
audit[99316]: AVC apparmor="DENIED" operation="open" profile="firefox" name="/sys/devices/pci0000:00/0000:00:02.0/config" pid=99316 comm="
firefox" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0  

Crosschecking with lspci -nnk | grep -i VGA -A2, this is indeed my GPU:

00:02.0 VGA compatible controller [0300]: Intel Corporation TigerLake-LP GT2 [Iris Xe Graphics] [8086:9a49] (rev 01)

Bingo. Now how to allow access? Easy in this case - remember the abstractions? Searching for the filename revision and config led me to abstractions/opencl-intel. Looking through the file, everything in there would allow an application to accees the dri devices for direct rendering and Hardware Acceleration.

Another error message I encountered was something regarding mesa_shader_cache:

audit[99316]: AVC apparmor="DENIED" operation="file_lock" profile="firefox" name="/home/cvigano/.cache/mesa_shader_cache/fa/b2a2ddb2c84ef4
c225c26c7320470fa9b7921e.tmp" pid=99316 comm="firefox:disk$0" requested_mask="k" denied_mask="k" fsuid=1000 ouid=1000 

Searching for mesa_shader_cache, I found abstractions/mesa, which concerned itself with exactly that.

Those two includes now went into /etc/apparmor.d/local/usr.bin.firefox:

#include <abstractions/opencl-intel>
#include <abstractions/mesa>

Parsing and replacing the modified profile is done with sudo apparmor_parser -r /etc/apparmor.d/usr.bin.firefox. Restarting Firefox, I now had a hardware WebRender, dice rolling was super smooth. Another page I used was a WebGL example, which now ran consistently at 60 FPS regardless of the configuration on the page.

Fixing DRM content

DRM content is managed by the Widevine plugin, which could not be loaded via mmap according to this error message:

audit[78563]: AVC apparmor="DENIED" operation="file_mmap" profile="firefox" name="/home/cvigano/.mozilla/firefox/j8p4aobs.default-release/gmp-widevinecdm/4.10.2557.0/libwidevinecdm.so"

There is no abstraction for this, but the rule itself is a short one:

owner @{HOME}/.{firefox,mozilla}/**/libwidevinecdm.so m,

This allows Firefox to load this library via mmap somewhere in the .mozilla folder. After parsing and replacing the profile with sudo apparmor_parser -r /etc/apparmor.d/usr.bin.firefox and restarting Firefox, Netflix and Spotify worked. 🥳

Fixing MPRIS

Firefox can be controlled by using MPRIS when playing a video or using Netflix and Spotify. This requires some D-Bus calls which need to be allowed explicitly in the AppArmor profile. After a quick internet search, I found a nice approximation of the necessary rules.

Those rules were missing a rule for the ReleaseName command, which I adapted from the already specified rule for RequestName:

# Inspired by https://gitlab.com/ubports/development/core/apparmor-easyprof-ubuntu/-/issues/9#note_1200857002
# Added another definition for ReleaseName
dbus (send)
     bus=session
     interface=org.freedesktop.DBus
     path=/org/freedesktop/DBus
     member=RequestName
     peer=(label=unconfined),
dbus (send)
     bus=session
     interface=org.freedesktop.DBus
     path=/org/freedesktop/DBus
     member=ReleaseName
     peer=(label=unconfined),
dbus (bind)
     bus=session
     name=org.mpris.MediaPlayer2.firefox.*,
dbus (receive, send)
     bus=session
     interface=org.mpris.MediaPlayer2
     path=/org/mpris/MediaPlayer2
     peer=(label=unconfined),
dbus (receive, send)
     bus=session
     interface=org.mpris.MediaPlayer2.Player
     path=/org/mpris/MediaPlayer2
     peer=(label=unconfined),
dbus (receive, send)
     bus=session
     path=/org/mpris/MediaPlayer2
     interface=org.freedesktop.DBus.Properties
     peer=(label=unconfined),

Parsing and replacing the profile with sudo apparmor_parser -r /etc/apparmor.d/usr.bin.firefox, all media output became availabe using MPRIS and could be controlled with my media keys.

Conclusion

AppArmor was at fault for denying access to files and devices that Firefox needs for Hardware Acceleration and DRM content. This is the complete file /etc/apparmor.d/local/usr.bin.firefox with the necessary changes to fix these issues:

#include <abstractions/opencl-intel>
#include <abstractions/mesa>

owner @{HOME}/.{firefox,mozilla}/**/libwidevinecdm.so m,

It enables Hardware Acceleration and DRM content for Firefox installed from the PPA on Ubuntu 22.04. LTS. Reload using sudo apparmor_parser -r /etc/apparmor.d/usr.bin.firefox and enjoy an overall faster browser that can offload render payloads to the GPU.

I’m quite happy to have actually worked through the AppArmor configuration and fixing it instead of deactivating it wholesale. Maybe this would also help with Firefox Snap, but I did not test that yet. Also, Snap packages can provide their own AppArmor profiles so applying these changes does not necessarily change anything.

I hope this helps, it sure did help me. 😊

For comparison, rolling 19d20 now only takes seven seconds. This is what that looks like:

Compare this with the earlier bad example of rolling 1d20, which took a good 33 seconds: