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 Link: https://lore.kernel.org/all/20261020110732.959552-1-shawnq1@163.com/ Signed-off-by: fujo --- 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