I took some time to take a look at this more closely and created a POC LD_PRELOAD library that seems to work[1]. I don’t consider this production code but hopefully some will find it helpful. Anyone is free to pick this up and submit it to the snapcraft-preload project.
Example:
$ sudo snap install test-sem-open --edge
test-sem-open 0 installed
$ test-sem-open.<tab>
test-sem-open.anon test-sem-open.py2 test-sem-open.py3-broken
test-sem-open.named test-sem-open.py2-broken
test-sem-open.named-broken test-sem-open.py3
$ test-sem-open.anon 1 2 # unnamed semaphores always and continue to work
main() about to call sem_timedwait()
sem_post() from handler
sem_timedwait() succeeded
$ test-sem-open.named 1 2 # now named ones work
main() about to call sem_timedwait()
sem_post() from handler
sem_timedwait() succeeded
$ test-sem-open.py2 # even in python2
1 tasks are running
2 tasks are running
3 tasks are running
<Process(Process-3, started)> has finished
<Process(Process-2, started)> has finished
<Process(Process-1, started)> has finished
$ test-sem-open.py3 # and python3
1 tasks are running
2 tasks are running
3 tasks are running
<Process(Process-2, started)> has finished
<Process(Process-3, started)> has finished
<Process(Process-1, started)> has finished
$ test-sem-open.named-broken 1 2 # broken without LD_PRELOAD
sem_open: Permission denied
[1]
$ grep snap.test-sem-open.named-broken /var/log/syslog
audit: type=1400 audit(1544131584.937:2210): apparmor="DENIED" operation="mknod" profile="snap.test-sem-open.named-broken" name="/dev/shm/ueW6dZ" pid=5674 comm="sem-named-test" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
$ test-sem-open.py2-broken
Traceback (most recent call last):
File "/snap/test-sem-open/x1/bin/mp-test.py", line 47, in <module>
test_semaphore()
File "/snap/test-sem-open/x1/bin/mp-test.py", line 28, in test_semaphore
sema = multiprocessing.Semaphore(3)
File "/snap/test-sem-open/x1/usr/lib/python2.7/multiprocessing/__init__.py", line 197, in Semaphore
return Semaphore(value)
File "/snap/test-sem-open/x1/usr/lib/python2.7/multiprocessing/synchronize.py", line 111, in __init__
SemLock.__init__(self, SEMAPHORE, value, SEM_VALUE_MAX)
File "/snap/test-sem-open/x1/usr/lib/python2.7/multiprocessing/synchronize.py", line 75, in __init__
sl = self._semlock = _multiprocessing.SemLock(kind, value, maxvalue)
OSError: [Errno 13] Permission denied
[1]
$ grep 'snap.test-sem-open.py2-broken' /var/log/syslog
audit: type=1400 audit(1544131603.329:2211): apparmor="DENIED" operation="mknod" profile="snap.test-sem-open.py2-broken" name="/dev/shm/4ULMJ2" pid=5699 comm="python2" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
$ test-sem-open.py3-broken
Traceback (most recent call last):
File "/snap/test-sem-open/x1/bin/mp-test3.py", line 47, in <module>
test_semaphore()
File "/snap/test-sem-open/x1/bin/mp-test3.py", line 28, in test_semaphore
sema = multiprocessing.Semaphore(3)
File "/snap/test-sem-open/x1/usr/lib/python3.5/multiprocessing/context.py", line 81, in Semaphore
return Semaphore(value, ctx=self.get_context())
File "/snap/test-sem-open/x1/usr/lib/python3.5/multiprocessing/synchronize.py", line 127, in __init__
SemLock.__init__(self, SEMAPHORE, value, SEM_VALUE_MAX, ctx=ctx)
File "/snap/test-sem-open/x1/usr/lib/python3.5/multiprocessing/synchronize.py", line 60, in __init__
unlink_now)
PermissionError: [Errno 13] Permission denied
[1]
$ grep 'snap.test-sem-open.py3-broken' /var/log/syslog
audit: type=1400 audit(1544131645.601:2212): apparmor="DENIED" operation="mknod" profile="snap.test-sem-open.py3-broken" name="/dev/shm/8NJVRZ" pid=5733 comm="python3" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000
I confirmed this to work on amd64, armhf and arm64.
[1]https://git.launchpad.net/~jdstrand/+git/test-sem-open/tree/lib.c