From: Karel Zak Date: Wed, 27 May 2026 10:38:16 +0000 (+0200) Subject: libmount: add mount ID verification and man page TOCTOU note X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=39fef62bf3226abb6bb018fa8750afa0327e84b4;p=thirdparty%2Futil-linux.git libmount: add mount ID verification and man page TOCTOU note Verify mount ID after re-opening the target fd to ensure the mount landed on the expected target. The expected ID is set from fd_tree in hook_create_mount() (new mount API only). Add WARNING to mount.8 about the inherent TOCTOU limitation of the legacy mount(2) syscall for non-superuser mounts. Signed-off-by: Karel Zak --- diff --git a/libmount/src/context.c b/libmount/src/context.c index 1865513c3..d5d9151b3 100644 --- a/libmount/src/context.c +++ b/libmount/src/context.c @@ -412,9 +412,33 @@ int mnt_context_reopen_target_fd(struct libmnt_context *cxt) if (!mnt_context_target_fd_required(cxt)) return 0; + + DBG_OBJ(CXT, cxt, ul_debug("reopen target fd")); + mnt_context_close_target_fd(cxt); if (mnt_context_get_target_fd(cxt) < 0) return -errno; + + /* verify the mount landed on the expected target; + * cxt->fs->id is set from fd_tree in hook_create_mount() */ + if (cxt->fs && cxt->fs->id > 0) { + int id = 0; + + if (mnt_id_from_fd(cxt->fd_target, NULL, &id) == 0 + && id != cxt->fs->id) { + const char *tgt = mnt_fs_get_target(cxt->fs); + + DBG_OBJ(CXT, cxt, ul_debug( + "target mount ID mismatch (expected %d, got %d), umounting", + cxt->fs->id, id)); + if (tgt) + umount2(tgt, MNT_DETACH); + mnt_context_close_target_fd(cxt); + return -EPERM; + } + DBG_OBJ(CXT, cxt, ul_debug("target mount ID verified (%d)", id)); + } + return 0; } @@ -425,9 +449,12 @@ int mnt_context_get_target_fd(struct libmnt_context *cxt) if (cxt->fd_target < 0) { const char *target = mnt_fs_get_target(cxt->fs); - if (target) + if (target) { cxt->fd_target = ul_open_no_symlinks(target, O_PATH | O_CLOEXEC, 0); + DBG_OBJ(CXT, cxt, ul_debug("open target fd=%d [%s]", + cxt->fd_target, target)); + } } return cxt->fd_target; } diff --git a/sys-utils/mount.8.adoc b/sys-utils/mount.8.adoc index c600723d9..abcf08b52 100644 --- a/sys-utils/mount.8.adoc +++ b/sys-utils/mount.8.adoc @@ -188,6 +188,8 @@ For more details, see *fstab*(5). Only the user that mounted a filesystem can un The *user* mount option is accepted if no username is specified. If used in the format *user=someone*, the option is silently ignored and visible only for external mount helpers (/sbin/mount.) for compatibility with some network filesystems. +WARNING: When using the legacy *mount*(2) syscall (on older kernels without the new mount API), the mount target path is resolved by the kernel at syscall time. This means there is an inherent time-of-check-to-time-of-use (TOCTOU) window between the permission verification and the actual mount operation. If an ancestor directory of the mount target is writable by the unprivileged user, a path component could be swapped to redirect the mount to an unintended location. The new mount API (available since Linux 5.2) eliminates this issue by using file-descriptor-based target resolution. Administrators should ensure that mount target paths for *user* mounts do not traverse directories writable by unprivileged users. + === Bind mount operation Remount part of the file hierarchy somewhere else. The call is: