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 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 fscrypt support, so keep the tree's simpler (non-encrypted) get_name structure.] Signed-off-by: Chen Yuxi <chenyuxi19@huawei.com> --- fs/ceph/export.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/fs/ceph/export.c b/fs/ceph/export.c index 952a590dd4bc..54efb44fc89b 100644 --- a/fs/ceph/export.c +++ b/fs/ceph/export.c @@ -431,6 +431,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 __get_snap_name(struct dentry *parent, char *name, struct dentry *child) { @@ -496,9 +506,8 @@ static int __get_snap_name(struct dentry *parent, char *name, BUG_ON(!rde->inode.in); if (ceph_snap(inode) == le64_to_cpu(rde->inode.in->snapid)) { - memcpy(name, rde->name, rde->name_len); - name[rde->name_len] = '\0'; - err = 0; + err = ceph_export_copy_name(name, rde->name, + rde->name_len); goto out; } } @@ -559,10 +568,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(inode), 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(inode), err); -- 2.34.1