From: Michael Bommarito <michael.bommarito@gmail.com> mainline inclusion from mainline-v7.3-rc1 commit eff8013c5a8916613c742ae5a2cc341cb605c0ae category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/18968 CVE: CVE-2026-89652 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- ceph_get_name() copies the MDS-supplied name into the caller's NAME_MAX-sized buffer with memcpy(name, rinfo->dname, rinfo->dname_len) and then writes name[rinfo->dname_len] = 0, without checking dname_len against NAME_MAX. A malicious or buggy MDS that returns a LOOKUPNAME reply with dname_len > NAME_MAX overflows the buffer. __get_snap_name() copies rde->name / rde->name_len the same unchecked way. Impact: a malicious or compromised Ceph MDS overflows the NAME_MAX name buffer in a client's NFS-export get_name path, a slab out-of-bounds write reported by KASAN. Reachable when a CephFS mount is re-exported over NFS. Add ceph_export_copy_name(), which rejects lengths above NAME_MAX with -ENAMETOOLONG before the copy, and use it in both ceph_get_name() and __get_snap_name(). Cc: stable@vger.kernel.org Fixes: 19913b4eac4a ("ceph: add get_name() NFS export callback") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com> Reviewed-by: Viacheslav Dubeyko <slava@dubeyko.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com> Conflicts: fs/ceph/export.c [Downstream tree lacks the __get_snap_name() function and fscrypt support, so only the ceph_export_copy_name() helper and the ceph_get_name() change were applied while keeping the tree's simpler (non-encrypted) get_name structure.] Signed-off-by: Chen Yuxi <chenyuxi19@huawei.com> --- fs/ceph/export.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/fs/ceph/export.c b/fs/ceph/export.c index 3c59ad180ef0..1d087cf1770c 100644 --- a/fs/ceph/export.c +++ b/fs/ceph/export.c @@ -194,6 +194,16 @@ static struct dentry *ceph_fh_to_parent(struct super_block *sb, return dentry; } +static int ceph_export_copy_name(char *name, const char *src, u32 len) +{ + if (len > NAME_MAX) + return -ENAMETOOLONG; + + memcpy(name, src, len); + name[len] = '\0'; + return 0; +} + static int ceph_get_name(struct dentry *parent, char *name, struct dentry *child) { @@ -221,10 +231,11 @@ static int ceph_get_name(struct dentry *parent, char *name, if (!err) { struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info; - memcpy(name, rinfo->dname, rinfo->dname_len); - name[rinfo->dname_len] = 0; - dout("get_name %p ino %llx.%llx name %s\n", - child, ceph_vinop(d_inode(child)), name); + err = ceph_export_copy_name(name, rinfo->dname, + rinfo->dname_len); + if (!err) + dout("get_name %p ino %llx.%llx name %s\n", + child, ceph_vinop(d_inode(child)), name); } else { dout("get_name %p ino %llx.%llx err %d\n", child, ceph_vinop(d_inode(child)), err); -- 2.34.1