diff --git a/docs/BUGS.md b/docs/BUGS.md index b672a2f..ceb79e3 100644 --- a/docs/BUGS.md +++ b/docs/BUGS.md @@ -100,6 +100,7 @@ a live account since. | Entry | Title | |---|---| +| [B245](#b245--any-child-of-the-daemon-can-answer-the-watchdog-for-it) | Any child of the daemon can answer the watchdog for it | | [B244](#b244--photos-without-a-sidecar-or-a-dated-name-are-filed-under-the-upload-day) | Photos without a sidecar or a dated name are filed under the upload day | | [B243](#b243--a-hung-mount-freezes-the-app-when-a-file-is-opened) | A hung mount freezes the app when a file is opened | | [B242](#b242--opening-a-large-photo-freezes-the-app) | Opening a large photo freezes the app | @@ -347,6 +348,27 @@ a live account since. Newest first. +### B245 — Any child of the daemon can answer the watchdog for it + +**Status:** Fixed, not verified live +**Found:** 2026-10-16, architecture audit (`FINDINGS.md` section 3, systemd unit). +**Where:** `packaging/proton-drive.service` + +**Repro:** A child of the daemon, such as `ffmpeg` making a video thumbnail, inherits +`NOTIFY_SOCKET` and can send `WATCHDOG=1` while the daemon itself is wedged. + +**Cause.** The unit set `NotifyAccess=all`, which accepts notifications from every process of the +unit. The watchdog exists to restart a daemon that stopped answering, and any child could keep it +from doing that. + +**Fix.** `NotifyAccess=main`: only the daemon's own process is heard. The rest of the hardening +the audit listed was left out on purpose. In a user unit every seccomp-based option implies +`NoNewPrivileges`, which stops the setuid `fusermount3` from mounting. + +**Test:** none automated; `systemd-analyze verify --user` accepts the unit. + +--- + ### B244 — Photos without a sidecar or a dated name are filed under the upload day **Status:** Fixed, not verified live diff --git a/packaging/proton-drive.service b/packaging/proton-drive.service index eddceae..ada69ef 100644 --- a/packaging/proton-drive.service +++ b/packaging/proton-drive.service @@ -8,10 +8,17 @@ PartOf=graphical-session.target [Service] # Deliberately not Type=notify: the daemon waits for a stored login before it # mounts anything, and a machine that boots before the user logs in would then -# fail the unit on the start timeout. NotifyAccess=all gets the daemon a -# NOTIFY_SOCKET all the same, which is all the watchdog below needs. +# fail the unit on the start timeout. NotifyAccess=main gets the daemon a +# NOTIFY_SOCKET all the same, which is all the watchdog below needs, and keeps +# its children (fusermount3, ffmpeg, exiftool) from answering the watchdog for +# it. "all" let any of them do so. +# +# Most sandboxing options are left out on purpose: in a user unit, every +# seccomp-based one (SystemCallFilter, LockPersonality, RestrictAddressFamilies +# and the like) implies NoNewPrivileges, which stops the setuid fusermount3 +# from mounting anything. Type=simple -NotifyAccess=all +NotifyAccess=main # The daemon waits for a stored login, mounts ~/ProtonDrive, and keeps it # mounted. SIGTERM (from `systemctl --user stop`) triggers a clean lazy unmount. ExecStart=/usr/bin/pdfs daemon