]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
smb/client: fix out-of-bounds read in smb2_compound_op()
authorZisen Ye <zisenye@stu.xidian.edu.cn>
Wed, 6 May 2026 03:49:08 +0000 (11:49 +0800)
committerSteve French <stfrench@microsoft.com>
Thu, 7 May 2026 19:09:32 +0000 (14:09 -0500)
If a server sends a truncated response but a large OutputBufferLength, and
terminates the EA list early, check_wsl_eas() returns success without
validating that the entire OutputBufferLength fits within iov_len.

Then smb2_compound_op() does:
    memcpy(idata->wsl.eas, data[0], size[0]);

Where size[0] is OutputBufferLength. If iov_len is smaller than size[0],
memcpy can read beyond the end of the rsp_iov allocation and leak adjacent
kernel heap memory.

Link: https://lore.kernel.org/linux-cifs/d998240c-aca9-420d-9dbd-f5ba24af19e0@chenxiaosong.com/
Fixes: ea41367b2a60 ("smb: client: introduce SMB2_OP_QUERY_WSL_EA")
Cc: stable@vger.kernel.org
Signed-off-by: Zisen Ye <zisenye@stu.xidian.edu.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/smb2inode.c

index 286912616c7339a81d80e17d99219128f591c8b8..6c9c229b91f654cde7688c7b9eff57cd134ba899 100644 (file)
@@ -111,7 +111,7 @@ static int check_wsl_eas(struct kvec *rsp_iov)
        u32 outlen, next;
        u16 vlen;
        u8 nlen;
-       u8 *end;
+       u8 *ea_end, *iov_end;
 
        outlen = le32_to_cpu(rsp->OutputBufferLength);
        if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||
@@ -120,15 +120,19 @@ static int check_wsl_eas(struct kvec *rsp_iov)
 
        ea = (void *)((u8 *)rsp_iov->iov_base +
                      le16_to_cpu(rsp->OutputBufferOffset));
-       end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
+       ea_end = (u8 *)ea + outlen;
+       iov_end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
+       if (ea_end > iov_end)
+               return -EINVAL;
+
        for (;;) {
-               if ((u8 *)ea > end - sizeof(*ea))
+               if ((u8 *)ea > ea_end - sizeof(*ea))
                        return -EINVAL;
 
                nlen = ea->ea_name_length;
                vlen = le16_to_cpu(ea->ea_value_length);
                if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
-                   (u8 *)ea->ea_data + nlen + 1 + vlen > end)
+                   (u8 *)ea->ea_data + nlen + 1 + vlen > ea_end)
                        return -EINVAL;
 
                switch (vlen) {