Skip to content

Linux: do not mount a volume again while a detached service keeps it open - #1967

Open
flatstik wants to merge 4 commits into
veracrypt:masterfrom
flatstik:fix-1959-detached-service
Open

flatstik wants to merge 4 commits into
veracrypt:masterfrom
flatstik:fix-1959-detached-service

Conversation

@flatstik

@flatstik flatstik commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1959.

An emergency dismount lazily detaches the file systems, and the FUSE service keeps the volume open until the last file on the detached file system is closed, but the volume is no longer in the mount table. Once the volume is open, an exclusive flock is taken for it and inherited by the FUSE service, which holds it until it exits; a second mount fails with "already mounted" meanwhile. A busy lock is retried for 10 s because a normally dismounted service may still be exiting.

The lock is named after the device number, or the file system and inode numbers, that fstat reports for the opened volume, so it applies to the file actually served whatever path leads to it. It is not taken on the volume file itself, whose locking is disabled for remote file systems. The lock files are created with openat in the root owned /var/run/veracrypt (also present where /run is not), which is opened without following symbolic links and checked through its descriptor. Without root or a usable lock directory, mounting works as before. The lock files are not removed, to avoid the usual unlink race; they are small and on tmpfs. This adds File::GetSystemHandle().

A second commit covers kernel crypto, where the deferred device-mapper device keeps using the volume after the FUSE service has exited: before taking the lock, a volume device held by a device-mapper device, or a volume file backing a loop device (CoreLinux::IsVolumeUsedByKernelDevice, using the existing loop helpers), is refused. A volume mounted under another path is refused at once instead of after the 10 s wait, and --auto-mount=devices skips such a device instead of stopping. The lock directory handling is shared with #1966 (OpenMountLockFile).

Tested:

  • OpenWrt 25.12.6 mipsel (FUSE3, root), 1.26.30 against 1.26.30 with this change: with a file open on the volume and the volume emergency-dismounted, mounting it again succeeded (mounted twice), against refused with "already mounted"; after the file is closed and the old service exits, it mounts normally.
  • Tests/test_linux_emergency_dismount_remount.py (new, run in the Linux workflow), with FUSE and with kernel crypto, and a mount through a symbolic link while the volume is mounted: passes on ubuntu-24.04 and ubuntu-26.04, https://github.com/flatstik/VeraCrypt/actions/runs/40582819859; with only the first commit, the kernel crypto remount succeeds and the mount through the link fails only after 10 s, https://github.com/flatstik/VeraCrypt/actions/runs/40582822189.
  • The --auto-mount=devices change is not covered by a test.

The new test adds lines next to test_mount_helper.py in .github/workflows/build-linux.yml, as do the other PRs split from #1963, so whichever is merged later conflicts there; I will rebase it.

Split from #1963, with #1968 (#1958), #1966 (#1960), #1964 (#1961), #1965 (#1962).

@curious-rabbit

Copy link
Copy Markdown
Contributor

MountAllDeviceHostedVolumes does not catch VolumeAlreadyMounted. A device already mounted under another path now stops --auto-mount=devices after 10 s instead of being skipped.
With kernel crypto the lock is released when the aux FUSE mount is unmounted, while the deferred dm device still uses the container. A remount in another slot is not refused.

@flatstik
flatstik force-pushed the fix-1959-detached-service branch from de268d1 to 5a39440 Compare October 27, 2026 21:11
@flatstik

Copy link
Copy Markdown
Contributor Author

Both fixed in a second commit. A volume device held by a device-mapper device, or a volume file backing a loop device, is now refused before the lock is taken, which covers the deferred dm device after an emergency dismount with kernel crypto. A volume mounted under another path is refused at once, and --auto-mount=devices skips it. The test now runs the emergency dismount with kernel crypto too and mounts through a symbolic link: with only the first commit both checks fail, https://github.com/flatstik/VeraCrypt/actions/runs/40582822189, and they pass with the second, https://github.com/flatstik/VeraCrypt/actions/runs/40582819859. The --auto-mount=devices path has no test.

@idrassi idrassi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address the issues inline.

Both this PR and #1966 add the same OpenMountLockFile helper. Once one is merged, please rebase the other and reuse the helper already in master.

Comment thread src/Core/Unix/CoreUnix.cpp Outdated
Comment thread src/Core/Unix/CoreUnix.cpp
Comment thread src/Core/Unix/CoreUnix.cpp Outdated
Comment thread src/Core/Unix/CoreUnix.cpp
Comment thread Tests/test_linux_emergency_dismount_remount.py
@flatstik
flatstik force-pushed the fix-1959-detached-service branch from 5a39440 to db4f54b Compare October 28, 2026 21:36
@flatstik

Copy link
Copy Markdown
Contributor Author

OpenMountLockFile and GetMountLockDirectory are now the same in this PR and #1966. Once one of them is merged, I will rebase the other onto master and drop its copy.

…open

An emergency dismount lazily detaches the file systems. The FUSE
service keeps running with the volume open until the last file on the
detached file system is closed, but the volume is no longer in the
mount table, so it could be mounted again and two services wrote to it.

Once the volume is open, take an exclusive flock for it. The FUSE
service inherits the descriptor and holds the lock until it exits; a
second mount fails with "already mounted" meanwhile. Because a normally
dismounted service may still be exiting, a busy lock is retried for up
to 10 seconds.

The lock is named after the device number, or the file system and inode
numbers, that fstat reports for the opened volume, so it applies to the
file actually served, whatever path leads to it. It is not taken on the
volume file itself, whose locking is disabled to avoid remote file
system issues. The lock files are created with openat in the root owned
/var/run/veracrypt, which is opened without following symbolic links
and checked through its descriptor. /var/run also exists on systems
without /run. Without root or a usable lock directory, mounting works
as before.

Fixes veracrypt#1959
With kernel crypto, the volume is used by a device-mapper device, which an
emergency dismount removes deferred. The FUSE service exits, so the
volume lock was released while the device still used the volume, and the
volume could be mounted again in another slot. Before taking the lock,
refuse a volume device held by a device-mapper device, or a volume file
backing a loop device.

A volume mounted under another path, for example a device alias, made a
second mount wait for the lock for 10 seconds. Refuse at once when a
mounted volume is the same file or device. --auto-mount=devices skips
such a device instead of stopping at it.
The volume lock and the kernel device check let mounting continue when
they could not establish that the volume was unused: an unusable lock
directory, an unexpected flock error, a sysfs or loop device that could
not be inspected, or an fstat failing on a file opened with the
requester's credentials, as FUSE checks the credentials of the calling
process. A writable mount is now refused in these cases; a read-only one
continues as before. A loop device that disappears during the check is
still taken as not using the volume.

The requester's process passes the device and inode of the file it
opened, and the lock uses them for such files instead of fstat.

The kernel device check is repeated once the lock is held: another mount
of the volume may have finished, and been dismounted in an emergency,
while this one was waiting for it. The lock descriptor is closed if an
exception occurs before it is returned.

The lock directory must belong to the user running VeraCrypt, root,
and be writable only by it. CoreUnix::GetMountLockDirectory() gives its
location, which the unprivileged mount test overrides, so that it uses
the production locks instead of failing to create /var/run/veracrypt.
Tests/test_linux_emergency_dismount_remount.py checks no second mount
while an emergency-detached service keeps the volume open against
the installed package in the Linux workflow.
@flatstik
flatstik force-pushed the fix-1959-detached-service branch from db4f54b to a579445 Compare October 29, 2026 10:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

After emergency unmount the same volume can be mounted twice and gets corrupted

3 participants