Skip to content

qSnapper 1.3.4

Latest

Choose a tag to compare

@github-actions github-actions released this 20 Jun 05:37

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


  1. 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).
  2. 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().
  3. 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.
  4. 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.
  5. 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