|
| 1 | +From 84b259d19569a6b073adec146a46e849085e65e0 Mon Sep 17 00:00:00 2001 |
| 2 | +From: AllSpark <allspark@microsoft.com> |
| 3 | +Date: Wed, 8 Apr 2026 14:14:23 +0000 |
| 4 | +Subject: [PATCH] loopdev: add LOOPDEV_FL_NOFOLLOW to prevent symlink attacks |
| 5 | + (backport) |
| 6 | + |
| 7 | +Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> |
| 8 | +Upstream-reference: AI Backport of https://github.com/util-linux/util-linux/commit/f55f9906b4f6eeb2b4a4120317df9de935253c10.patch |
| 9 | +--- |
| 10 | + include/loopdev.h | 3 ++- |
| 11 | + lib/loopdev.c | 12 ++++++++++-- |
| 12 | + libmount/src/context_loopdev.c | 3 ++- |
| 13 | + 3 files changed, 14 insertions(+), 4 deletions(-) |
| 14 | + |
| 15 | +diff --git a/include/loopdev.h b/include/loopdev.h |
| 16 | +index 6d400d1..9e4ef23 100644 |
| 17 | +--- a/include/loopdev.h |
| 18 | ++++ b/include/loopdev.h |
| 19 | +@@ -135,7 +135,8 @@ enum { |
| 20 | + LOOPDEV_FL_NOIOCTL = (1 << 6), |
| 21 | + LOOPDEV_FL_DEVSUBDIR = (1 << 7), |
| 22 | + LOOPDEV_FL_CONTROL = (1 << 8), /* system with /dev/loop-control */ |
| 23 | +- LOOPDEV_FL_SIZELIMIT = (1 << 9) |
| 24 | ++ LOOPDEV_FL_SIZELIMIT = (1 << 9), |
| 25 | ++ LOOPDEV_FL_NOFOLLOW = (1 << 10) /* O_NOFOLLOW, don't follow symlinks */ |
| 26 | + }; |
| 27 | + |
| 28 | + /* |
| 29 | +diff --git a/lib/loopdev.c b/lib/loopdev.c |
| 30 | +index d9ea1d4..d6ce572 100644 |
| 31 | +--- a/lib/loopdev.c |
| 32 | ++++ b/lib/loopdev.c |
| 33 | +@@ -1170,7 +1170,10 @@ int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename) |
| 34 | + if (!lc) |
| 35 | + return -EINVAL; |
| 36 | + |
| 37 | +- lc->filename = canonicalize_path(filename); |
| 38 | ++ if (lc->flags & LOOPDEV_FL_NOFOLLOW) |
| 39 | ++ lc->filename = strdup(filename); |
| 40 | ++ else |
| 41 | ++ lc->filename = canonicalize_path(filename); |
| 42 | + if (!lc->filename) |
| 43 | + return -errno; |
| 44 | + |
| 45 | +@@ -1305,7 +1308,12 @@ int loopcxt_setup_device(struct loopdev_cxt *lc) |
| 46 | + if (lc->config.info.lo_flags & LO_FLAGS_READ_ONLY) |
| 47 | + mode = O_RDONLY; |
| 48 | + |
| 49 | +- if ((file_fd = open(lc->filename, mode | O_CLOEXEC)) < 0) { |
| 50 | ++ int flags = O_CLOEXEC; |
| 51 | ++ if (lc->config.info.lo_flags & LO_FLAGS_DIRECT_IO) |
| 52 | ++ flags |= O_DIRECT; |
| 53 | ++ if (lc->flags & LOOPDEV_FL_NOFOLLOW) |
| 54 | ++ flags |= O_NOFOLLOW; |
| 55 | ++ if ((file_fd = open(lc->filename, mode | flags)) < 0) { |
| 56 | + if (mode != O_RDONLY && (errno == EROFS || errno == EACCES)) |
| 57 | + file_fd = open(lc->filename, mode = O_RDONLY); |
| 58 | + |
| 59 | +diff --git a/libmount/src/context_loopdev.c b/libmount/src/context_loopdev.c |
| 60 | +index 6462bfb..2fedd01 100644 |
| 61 | +--- a/libmount/src/context_loopdev.c |
| 62 | ++++ b/libmount/src/context_loopdev.c |
| 63 | +@@ -289,7 +289,8 @@ int mnt_context_setup_loopdev(struct libmnt_context *cxt) |
| 64 | + } |
| 65 | + |
| 66 | + DBG(LOOP, ul_debugobj(cxt, "not found; create a new loop device")); |
| 67 | +- rc = loopcxt_init(&lc, 0); |
| 68 | ++ rc = loopcxt_init(&lc, |
| 69 | ++ mnt_context_is_restricted(cxt) ? LOOPDEV_FL_NOFOLLOW : 0); |
| 70 | + if (rc) |
| 71 | + goto done_no_deinit; |
| 72 | + if (loopval) { |
| 73 | +-- |
| 74 | +2.45.4 |
| 75 | + |
0 commit comments