[PATCH 2/2] fs: reject U64_MAX cursors in listmount() and listns()

From: fujo

Date: Wed Oct 21 2026 - 13:55:00 EST


listmount(2) and listns(2) treat their cursor as the last ID returned by
the previous invocation and look up the next entry at cursor + 1. For
U64_MAX that addition wraps to zero, so the enumeration silently
restarts at the first visible entry instead of reporting that there are
no more entries.

listmount(LSMT_ROOT, param = U64_MAX) returned the same first mount ID
as param = 0, while param = U64_MAX - 1 returned no entries. listns()
repeated the first page of namespace IDs for a U64_MAX cursor;
U64_MAX - 1 returned ENOENT.

U64_MAX can never be an ID returned by a previous call: mount IDs are
allocated above MNT_UNIQUE_ID_OFFSET and are strictly increasing, and
namespace IDs come from an increasing 64-bit counter. Reject it as an
invalid cursor, like the existing check for reserved mount IDs, so that
the wrap cannot happen.

Reported-by: shaos <shawnq1@xxxxxxx>
Link: https://lore.kernel.org/all/20261020110732.959552-1-shawnq1@xxxxxxx/
Signed-off-by: fujo <hooyan2024@xxxxxxxxx>
---
fs/namespace.c | 9 +++++++--
kernel/nstree.c | 3 +++
2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index c8aceeff6..6806745a3 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -6292,8 +6292,13 @@ static inline int prepare_klistmount(struct klistmount *kls, struct mnt_id_req *
u64 last_mnt_id = kreq->param;
struct mnt_namespace *ns;

- /* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */
- if (last_mnt_id != 0 && last_mnt_id <= MNT_UNIQUE_ID_OFFSET)
+ /*
+ * The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. A
+ * U64_MAX cursor is invalid as well: advancing it by one would wrap
+ * around to zero and restart the enumeration.
+ */
+ if (last_mnt_id != 0 &&
+ (last_mnt_id <= MNT_UNIQUE_ID_OFFSET || last_mnt_id == U64_MAX))
return -EINVAL;

kls->last_mnt_id = last_mnt_id;
diff --git a/kernel/nstree.c b/kernel/nstree.c
index 831f279d1..39978b56b 100644
--- a/kernel/nstree.c
+++ b/kernel/nstree.c
@@ -450,6 +450,9 @@ static int copy_ns_id_req(const struct ns_id_req __user *req,
static inline int prepare_klistns(struct klistns *kls, struct ns_id_req *kreq,
u64 __user *ns_ids, size_t nr_ns_ids)
{
+ /* A U64_MAX cursor would wrap when advanced. */
+ if (kreq->ns_id == U64_MAX)
+ return -EINVAL;
kls->last_ns_id = kreq->ns_id;
kls->user_ns_id = kreq->user_ns_id;
kls->nr_ns_ids = nr_ns_ids;
--
2.53.0