Release v1.3.4: harden live-path restore operations with dirfd-based helpers
This change completes the v1.3.4 follow-up for the remaining restore-path defense-in-depth
issue discussed during the SUSE security review.
The core goal is to remove the last live-filesystem mutation paths that still depended
on absolute-path syscalls in restoreFilesImpl() and copySymlink(),
and to replace them with helper APIs anchored to trusted parent directory file descriptors.
Background
The earlier v1.3.3 security hardening already moved regular-file restore operations onto
openat(..., O_NOFOLLOW) and fd-based metadata updates.
However, several live-path operations still relied on path-based rename/open/lchown/utimensat calls.
Those remaining operations were not considered immediately exploitable in the default deployment model,
but they still left a defense-in-depth concern when restoring into parent directories writable by non-root
service users.
This commit closes that gap by extending the filesystem helper layer and migrating the remaining live-path
restore code to it.
Main changes
-
Add dirfd-based live-path helpers in filesystemhelpers
- Add safeOpenDirectory() for O_NOFOLLOW directory opens via a trusted parent walk.
- Add safeRenamePathNoFollow() using renameat() on source/destination parent dirfds.
- Add safeReadLinkNoFollow() using readlinkat() from a trusted parent dirfd.
- Add safeCreateSymlinkNoFollow() using symlinkat() from a trusted parent dirfd.
- Add safeSetSymlinkMetadataNoFollow() using fchownat(...,
AT_SYMLINK_NOFOLLOW) and utimensat(..., AT_SYMLINK_NOFOLLOW).
-
Migrate remaining live-path restore operations to the new helpers
- Replace movePathAsideNoFollow() path-based rename() with safeRenamePathNoFollow().
- Replace directory restore open(systemFilePath, ... O_DIRECTORY | O_NOFOLLOW) with safeOpenDirectory().
- Rework copySymlink() to use safeReadLinkNoFollow(),
safeCreateSymlinkNoFollow(), safeRenamePathNoFollow(), and
safeSetSymlinkMetadataNoFollow() instead of path-based readlink(),
symlink(), rename(), lchown(), and utimensat().
-
Preserve and clarify trust boundaries
- Keep safeLstat() explicitly documented as a read-only helper for
trusted snapshot parents and display-only metadata collection. - Clarify in comments that snapshot-side reads remain path-based by
design, while live-path mutations are now dirfd-based.
- Keep safeLstat() explicitly documented as a read-only helper for
-
Finish the remaining correctness cleanup from the helper migration
- Make safeReadLinkNoFollow() retry with a growing QByteArray instead
of a fixed PATH_MAX buffer so large symlink targets are read without
silent truncation.
- Make safeReadLinkNoFollow() retry with a growing QByteArray instead
-
Extend unit coverage
- Add tests for safeOpenDirectory() rejecting symlink paths.
- Add tests for safeRenamePathNoFollow() under trusted and symlinked parent scenarios.
- Add tests for safeCreateSymlinkNoFollow(), safeReadLinkNoFollow(), and safeSetSymlinkMetadataNoFollow().
- Add a regression test that verifies large symlink targets round-trip without truncation.
Other low-risk hardening included in this work
- In fssnapshotstore.cpp, remove the open-fail -> QFile::exists()
pattern and simplify failure handling to QFile::errorString(). - In copyRegularFile(), fail explicitly if sendfile() returns 0 before
the expected byte count is copied, preventing a potential infinite
loop on unexpected EOF. - Document the /tmp fallback in SingleInstanceGuard as a GUI-side
graceful degradation path, not a privileged security boundary.
Verification
- Debug build: qsnapper-dbus-service rebuilt successfully
- Release build: qsnapper-dbus-service rebuilt successfully
- Unit tests passed:
- tst_configname
- tst_filepaths
- tst_filesystemhelpers
Result
After this change, live-path restore mutations no longer depend on path-based rename/open/symlink-metadata syscalls.
The remaining restore path now consistently uses trusted parent dirfd traversal for file and
symlink creation, rename, and metadata updates,
aligning the implementation with the v1.3.4 defense-in-depth objective raised in the SUSE review.
What's Changed
New Contributors
Full Changelog: v1.3.3...v1.3.4