From 119d23f1e3f55cf10e3c66dab80fc308c9f26456 Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Wed, 27 May 2026 15:15:22 +0200
Subject: [PATCH] loopdev: use openat2(RESOLVE_NO_SYMLINKS) for backing file

Use ul_open_no_symlinks() instead of open(O_NOFOLLOW) when
LOOPDEV_FL_NOFOLLOW is set.  O_NOFOLLOW only rejects symlinks at the
last path component, but TOCTOU attacks swap intermediate components.
openat2(RESOLVE_NO_SYMLINKS) rejects symlinks at any component.

Signed-off-by: Karel Zak <kzak@redhat.com>
---
 lib/loopdev.c | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

Index: util-linux-2.41.1/lib/loopdev.c
===================================================================
--- util-linux-2.41.1.orig/lib/loopdev.c
+++ util-linux-2.41.1/lib/loopdev.c
@@ -1412,16 +1412,21 @@ int loopcxt_setup_device(struct loopdev_
 	if (lc->config.info.lo_flags & LO_FLAGS_DIRECT_IO)
 		flags |= O_DIRECT;
 	if (lc->flags & LOOPDEV_FL_NOFOLLOW)
-		flags |= O_NOFOLLOW;
+		file_fd = ul_open_no_symlinks(lc->filename, mode | flags, 0);
+	else
+		file_fd = open(lc->filename, mode | flags);
 
-	if ((file_fd = open(lc->filename, mode | flags)) < 0) {
-		if (mode != O_RDONLY && (errno == EROFS || errno == EACCES))
-			file_fd = open(lc->filename, (mode = O_RDONLY) | flags);
-
-		if (file_fd < 0) {
-			DBG(SETUP, ul_debugobj(lc, "open backing file failed: %m"));
-			return -errno;
-		}
+	if (file_fd < 0 && mode != O_RDONLY
+	    && (errno == EROFS || errno == EACCES)) {
+		mode = O_RDONLY;
+		if (lc->flags & LOOPDEV_FL_NOFOLLOW)
+			file_fd = ul_open_no_symlinks(lc->filename, mode | flags, 0);
+		else
+			file_fd = open(lc->filename, mode | flags);
+	}
+	if (file_fd < 0) {
+		DBG(SETUP, ul_debugobj(lc, "open backing file failed: %m"));
+		return -errno;
 	}
 	DBG(SETUP, ul_debugobj(lc, "backing file open: OK"));
 
