File_mmap on fonts denied even though no stackexec bit set

I have a 32-bit wine application running in wine64. In devmode, everything seems to work but in strict, the application fails to show certain text.

I’ve read through the scanlog output of the devmode snap, and I get A TON of complaints about memory mapping fonts.

= AppArmor =
Time: Mar 17 23:54:48
Log: apparmor="ALLOWED" operation="file_mmap" profile="snap.photoscape.photoscape" name="/usr/share/fonts/truetype/noto/NotoSansBuhid-Regular.ttf" pid=15320 comm="PhotoScape.exe" requested_mask="m" denied_mask="m" fsuid=1000 ouid=0
File: /usr/share/fonts/truetype/noto/NotoSansBuhid-Regular.ttf (mmap)

This reminds me of the stackexec issue we had on some of the electron snaps. Basically: snap denies memory maps from executables which have the stackexec bit set.

However; when I comb through my snap I can’t find any executables with the stackexec bit set:

$ find /snap/photoscape/current/ -executable -type f -exec execstack "{}" \; 2>&1 | grep "X "
$ 

So my questions:

  1. Are these denials caused by READ_IMPLIES_X / stackexec issue?
  2. How can I see whether a running process has the READ_IMPLIES_X personality bit set?
  3. Are there other ways to be “granted” READ_IMPLIES_X?

I’ll side-step the question and ask, if this is in devmode or strict mode, how is it accessing /usr/share/fonts/truetype when that doesn’t exist in the core snap? Is this coming from a content interface?

You can use the review-tools to see if execstack is set in anything in your snap. Eg: sudo snap install review-tools && review-tools.snap-review /path/to/photoscape/snap.

You also mentioned running this under wine64. Have you tried running it under wine instead? Note there are various wine-platform snaps in the store (snap info wine-platform) and forum topics on how they are used that might assist you.

The logs in my original message are from devmode. In devmode, the fonts load correctly. In strict mode, the fonts don’t show. However, all other desktop snaps use those fonts, even those not using the desktop helpers or any extension. If I run snap run --shell photoscape, I can access and memory map those fonts from a python shell.

I am also curious as to how these fonts work. They are not using a layout, and when I change the fonts on my host system, the changes are not shown in /usr/share/fonts. I assume this is an implementation of @jamesh’s proposal here: Desktop: allow access to host system fonts

At the moment I have tried wine and wine64, both from the archives, from winehq and from the wine-platform-5-stable snap. They all show the same behavior:

With vanilla wine, the snap can memory map the fonts in strict mode. However, if I run the application after I install the gdiplus.dll library from Microsoft, memory maps are denied by apparmor. Other Microsoft libraries don’t seem to have that problem, it’s only happening with gdiplus.dll from what I’ve tested.

This seems to point in the direction that somehow gdiplus.dll turns on the READ_IMPLIES_X personality bit of the wine process, but I can’t really test that theory because I didn’t find a way to see the personality bits of a running process. (Are personality bits a Linux thing? Are they an apparmor thing? I couldn’t find much documentation about personality bits.)

Given that gdiplus.dll is not an elf binary, I also can’t test if it has the executable-stack flag. I don’t know if Windows binaries even have executable-stack flags. :thinking: I understand that this might be going too deep into Wine territory, but I’m hoping I can at least find a way to conclusively say the READ_IMPLIES_X personality is the issue, what sets that personality and why, so I know what to ask the Wine devs. Given that many wine applications require gdiplus.dll from Microsoft, I’d like to investigate this further and see if we can fix it somehow…

Ah yes, snap run --shell something.that-plugs-desktop does end up with the fonts in /usr/share/fonts. Thank you for reminding me about that.

Very interesting :slight_smile:

There are likely equivalents in other operating systems, but there is a “personality” syscall you can read about in ‘man personality’.

You can read the personality at /proc/<pid>/personality to see if READ_IMPLIES_X is set (you may need root to read it). You’ll need to look in /usr/include/sys/personality.h (provided by libc6-dev-i386 on Ubuntu). The setarch command might be handy (eg, sudo setarch i386 -F -- cat /proc/self/personality) and while it specifies the -X for READ_IMPLIES_X, it doesn’t seem to do anything here.

All that said, our rule for /usr/share/fonts is /usr/share/fonts/** r,. This does not include ‘w’ and so adding ‘m’ to the policy would be safe. It might simply be that gdiplus.dll (or whatever deals with fonts in wine) simply ends up doing an mmap(..., PROT_READ|PROT_EXEC, ...) on the file.

Can you add this to your apparmor profiles in /var/lib/snap/apparmor/profiles/snap.<your snap>.<command>:

  /usr/share/fonts/** m,

then do: sudo apparmor_parser -r /var/lib/snap/apparmor/profiles/snap.<your snap>.<command> and see if it resolves the issue for you?

FYI, I added these accesses in https://github.com/snapcore/snapd/pull/8443

I tried the latest snapd from the edge channel and I can confirm I don’t get the errors on /usr/share/fonts anymore. However, the fonts are still not showing.

It’s difficult to debug this because when I run the snap strictly confined and look at snappy-debug.security scanlog, the denials seem to be printed really slowly. I get an almost endless stream of denials with the same timestamp, but they are printed minutes apart. Is there a more “raw” version of this log I can see which has less latency?

Note 1: This isn’t an issue for my snap anymore because I found a workaround where I can use wine’s version of gdiplus, which doesn’t have the stackexec issue. Although I’m still interested to get to the bottom of this.
Note 2: It’s still really cool how easily I can test out the latest snapd code and revert back to the stable version. Nice work on that!

That’s weird that they are showing up with the same timestamps… For best results you should pipe the journactl output into snappy-debug. Like so:

$ snappy-debug
INFO: Following '/var/log/syslog'. If have dropped messages, use:
INFO: $ sudo journalctl --output=short --follow --all | sudo snappy-debug
^C
$ sudo journalctl --output=short --follow --all | sudo snappy-debug

but if that isn’t working well for you, instead of piping into snappy-debug, pipe it into grep. Eg:

$ journalctl --output=short --follow | grep audit

though you should disable rate limiting (that is one thing snappy-debug is doing), so it is really:

$ sudo sysctl -w kernel.printk_ratelimit=0 ; journalctl --output=short --follow | grep audit
1 Like