From nobody Sat Jul 25 17:33:40 2026 Received: from smtpbg150.qq.com (smtpbg150.qq.com [18.132.163.193]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CC7C53D79EF for ; Wed, 15 Jul 2026 08:54:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=18.132.163.193 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784105675; cv=none; b=byNBAcHt01s9DykenKEkMqhPmOna/nXYceYuLt1vp0Nm93y09HOebsyQxBEvVjdQV3ZUresq4W9uFn6KaJ6EDrsjsuWoZTtht6d7DZJkc+rCKRmO5IiuyhzerhiZBn53xT/TkG+zOWDtnFF06OdkMv26zLIUR6p5n5ncwz+XzOE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784105675; c=relaxed/simple; bh=herC1JR+1OIaFj4SZ6r+g/uw9EKF52Ycl0C4tswFzdY=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=Cv0KOrEFWJIKnpXFpu7BGzq+pj6saUv4Eghnoa0ZKAm/tpzf0hBvU+KQNDDRGg/TXFBV2ktMq/5PnKWVw0XsaOcsFu9qnwTEJtDR4gLOseV1Dsot7epQxXHr91SViTp+5EE9Ch29eofTs4j9iOCcYpv09HC2DeIOucwEmgESnbw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=uniontech.com; spf=pass smtp.mailfrom=uniontech.com; dkim=pass (1024-bit key) header.d=uniontech.com header.i=@uniontech.com header.b=a9Koo98f; arc=none smtp.client-ip=18.132.163.193 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=uniontech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=uniontech.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=uniontech.com header.i=@uniontech.com header.b="a9Koo98f" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uniontech.com; s=onoh2408; t=1784105665; bh=TTnyldGWSibnHxV29c8TSmMDt9bw7jVpq7zj38Ch8TI=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=a9Koo98fukABd4FZOE+86fzmI0sGiYYBqc8xuEoPhO4/AFQc0N61uwTlHBKb4RXYM RnLBTjzamW9676zyQmoa+0HRtmx+w04FtTgFVWdIs7PWqYU+iVVuIzXDRPLqX83SGa SCIceGeL5ryYB9Gk5+NUNtoTbYAsKC2Y5vsYIGYc= X-QQ-mid: esmtpgz13t1784105659t52b38a68 X-QQ-Originating-IP: 5a+leSHTGtzLtw45jE+ZvxRVCZOI0Ql5w8e4XAdMahQ= Received: from uniontech.com ( [113.57.152.160]) by bizesmtp.qq.com (ESMTP) with id ; Wed, 15 Jul 2026 16:54:18 +0800 (CST) X-QQ-SSF: 0000000000000000000000000000000 X-QQ-GoodBg: 1 X-BIZMAIL-ID: 10317123681680029359 EX-QQ-RecipientCnt: 12 From: Yichong Chen To: Steve French Cc: Steve French , Paulo Alcantara , Ronnie Sahlberg , Shyam Prasad N , Tom Talpey , Bharath SM , Eugene Korenevsky , linux-cifs@vger.kernel.org, samba-technical@lists.samba.org, linux-kernel@vger.kernel.org, Yichong Chen Subject: [PATCH v2] smb: client: validate DFS referral offsets Date: Wed, 15 Jul 2026 16:54:16 +0800 Message-Id: <20260715085416.1112469-1-chenyichong@uniontech.com> X-Mailer: git-send-email 2.20.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-QQ-SENDSIZE: 520 Feedback-ID: esmtpgz:uniontech.com:qybglogicsvrgz:qybglogicsvrgz3a-0 X-QQ-XMAILINFO: OEyKOSqiGUQN+p8L/LWIelrRBYM/txE4Ai2stKq0/IowT4YrFsCZKwuc 6b9SKKe+XcHADgJ4NXLEXxvWwGhmrmIoH/avIbBTdPodPxvnrKcbE+biuGL7B4z2GU91Dif s2baTr/lElHW02YcApn+YLennb5Oz2h4klDroMTeaqm0tPXnYvMVs9azmwx14BTPIQ8CgAl 9wIorCmaO+IFasIjf1osuOXBWIOgDNV2PuJ6U0RPNj3XqHNlNpAiAbvP813LH3Xnw/w+z4u 5tY5VNOswPyKcl7VbZQ55QyHuNE77+dkFP6C5Rr7ONwUrAGzbH1tM5h+IeFeMmjY93OwiNa CQh7FP9eL2BA/f8gt1IrFn47tYOFg8HdeMy0wz5DJNAYXw/JDEKJtu2xovm/mL/C21h60A5 JdxH8kUyS9oJ6kJhfoMkJZnncuin+qgCrZHhkqUNsPprSMYmKu3zdLVVEHobYJLvzvd/+lk TBjEV0YhfiJL53cEfB0BNQQPcLCmvk1X+OuZAk/OIJzHdixWqVJEgMVdpOWifvG2FhldxXm W3eKYUQIPX0xzFqsK+pA8wGMXmvJpge/iQQ8WHAsXw1W4AZwMWEpG9zgP5KTdCAbtPwjwTJ KRjDLO5Nq5Ej+pwuBfQQdzPlR15OO3n67eCvprktAzPkTA894M2UzM2xPQFpTubb28O0Nob cXSmOs/35QdTXqpIDMuoFVhBBN1ORYRIM0ixdPddpkaWy3lhJQPEDgoZu2TVn+/lLptDpgy mz6kFp2C1d2Bq8ti/JTAVg6QlBwG3FtVkoNIQGPupWyYSN6yCUyATx8APHJqNaLZmqB8ADp kUTFE+ORp4J1tmGkzb915lOG8wrPGmt0BnyDftwhW+jSKTUcRWdy46AwAIR84DQ16PkCk6A aWU6lj991+8dpSVlXIJEVHO6n64FK8PZV+bAwvjNCypvm60+RbWkASSnj3Xro/XOXf2qhqs PG3rKPXAy47GSAqvfLiBySm2ijg+gKyGuQM3XTKmEaSrz7uBfqw4tBRF+GjquFdjYGtxe5s usMf7Kb4wD+c4erFgZkvBz7wHzDbdYw9WA2DZ772RlvArk0nnEpFK4ZW9LoYs= X-QQ-XMRINFO: MPJ6Tf5t3I/ylTmHUqvI8+Wpn+Gzalws3A== X-QQ-RECHKSPAM: 0 Content-Type: text/plain; charset="utf-8" parse_dfs_referrals() validates that the response contains the fixed referral entry array, but it still trusts offsets stored inside each referral entry. A malformed server response can set DfsPathOffset or NetworkAddressOffset past the end of the received buffer. The resulting pointer is then used to derive a remaining length and passed to cifs_strndup_from_utf16(). The response also contains a PathConsumed value that is later used for DFS path parsing. If it is larger than the search name, the parser can advance beyond the end of the path. Validate the per-referral offsets before using them, and reject PathConsumed values that exceed the search name length. Fixes: 6447b0e35556 ("cifs: parse_dfs_referrals: prevent oob on malformed i= nput") Signed-off-by: Yichong Chen --- Changes in v2: - Validate non-Unicode PathConsumed against strlen(searchName), as pointed out by Sashiko and Steve. fs/smb/client/misc.c | 99 ++++++++++++++++++++++++++++++++++++------------= ---- 1 file changed, 68 insertions(+), 31 deletions(-) diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 0c54b9b79a2c..9d84cc718557 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -678,6 +678,11 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, = u32 rsp_size, int i, rc =3D 0; char *data_end; struct dfs_referral_level_3 *ref; + unsigned int num_refs, path_consumed; + size_t search_name_len; + + *target_nodes =3D NULL; + *num_of_nodes =3D 0; =20 if (rsp_size < sizeof(*rsp)) { cifs_dbg(VFS | ONCE, @@ -687,85 +692,117 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp= , u32 rsp_size, goto parse_DFS_referrals_exit; } =20 - *num_of_nodes =3D le16_to_cpu(rsp->NumberOfReferrals); + num_refs =3D le16_to_cpu(rsp->NumberOfReferrals); =20 - if (*num_of_nodes < 1) { + if (num_refs < 1) { cifs_dbg(VFS | ONCE, "%s: [path=3D%s] num_referrals must be at least > 0= , but we got %d\n", - __func__, searchName, *num_of_nodes); + __func__, searchName, num_refs); rc =3D -ENOENT; goto parse_DFS_referrals_exit; } =20 - if (sizeof(*rsp) + *num_of_nodes * sizeof(REFERRAL3) > rsp_size) { + if (sizeof(*rsp) + num_refs * sizeof(REFERRAL3) > rsp_size) { cifs_dbg(VFS | ONCE, "%s: malformed buffer (size is %u, must be at least %zu)\n", __func__, rsp_size, - sizeof(*rsp) + *num_of_nodes * sizeof(REFERRAL3)); + sizeof(*rsp) + num_refs * sizeof(REFERRAL3)); rc =3D -EINVAL; goto parse_DFS_referrals_exit; } =20 ref =3D (struct dfs_referral_level_3 *) &(rsp->referrals); - if (ref->VersionNumber !=3D cpu_to_le16(3)) { - cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\= n", - le16_to_cpu(ref->VersionNumber)); - rc =3D -EINVAL; - goto parse_DFS_referrals_exit; - } =20 /* get the upper boundary of the resp buffer */ data_end =3D (char *)rsp + rsp_size; =20 cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n", - *num_of_nodes, le32_to_cpu(rsp->DFSFlags)); + num_refs, le32_to_cpu(rsp->DFSFlags)); =20 - *target_nodes =3D kzalloc_objs(struct dfs_info3_param, *num_of_nodes); - if (*target_nodes =3D=3D NULL) { + *target_nodes =3D kzalloc_objs(struct dfs_info3_param, num_refs); + if (!*target_nodes) { rc =3D -ENOMEM; goto parse_DFS_referrals_exit; } + *num_of_nodes =3D num_refs; + search_name_len =3D strlen(searchName); =20 /* collect necessary data from referrals */ - for (i =3D 0; i < *num_of_nodes; i++) { + for (i =3D 0; i < num_refs; i++) { char *temp; int max_len; - struct dfs_info3_param *node =3D (*target_nodes)+i; + unsigned int offset, remaining; + struct dfs_info3_param *node =3D (*target_nodes) + i; + + if (ref->VersionNumber !=3D cpu_to_le16(3)) { + cifs_dbg(VFS, + "Referrals of V%d version are not supported, should be V3\n", + le16_to_cpu(ref->VersionNumber)); + rc =3D -EINVAL; + goto parse_DFS_referrals_exit; + } =20 node->flags =3D le32_to_cpu(rsp->DFSFlags); + path_consumed =3D le16_to_cpu(rsp->PathConsumed); if (is_unicode) { - __le16 *tmp =3D kmalloc(strlen(searchName)*2 + 2, - GFP_KERNEL); - if (tmp =3D=3D NULL) { + size_t search_name_utf16_len =3D search_name_len * 2 + 2; + __le16 *tmp; + + if (path_consumed > search_name_utf16_len) { + rc =3D -EINVAL; + goto parse_DFS_referrals_exit; + } + + tmp =3D kmalloc(search_name_utf16_len, GFP_KERNEL); + if (!tmp) { rc =3D -ENOMEM; goto parse_DFS_referrals_exit; } - cifsConvertToUTF16((__le16 *) tmp, searchName, + cifsConvertToUTF16((__le16 *)tmp, searchName, PATH_MAX, nls_codepage, remap); - node->path_consumed =3D cifs_utf16_bytes(tmp, - le16_to_cpu(rsp->PathConsumed), - nls_codepage); + node->path_consumed =3D cifs_utf16_bytes(tmp, path_consumed, + nls_codepage); kfree(tmp); - } else - node->path_consumed =3D le16_to_cpu(rsp->PathConsumed); + } else { + if (path_consumed > search_name_len) { + rc =3D -EINVAL; + goto parse_DFS_referrals_exit; + } + + node->path_consumed =3D path_consumed; + } =20 node->server_type =3D le16_to_cpu(ref->ServerType); node->ref_flag =3D le16_to_cpu(ref->ReferralEntryFlags); =20 + remaining =3D data_end - (char *)ref; + /* copy DfsPath */ - temp =3D (char *)ref + le16_to_cpu(ref->DfsPathOffset); - max_len =3D data_end - temp; + offset =3D le16_to_cpu(ref->DfsPathOffset); + if (offset > remaining) { + rc =3D -EINVAL; + goto parse_DFS_referrals_exit; + } + temp =3D (char *)ref + offset; + max_len =3D remaining - offset; node->path_name =3D cifs_strndup_from_utf16(temp, max_len, - is_unicode, nls_codepage); + is_unicode, + nls_codepage); if (!node->path_name) { rc =3D -ENOMEM; goto parse_DFS_referrals_exit; } =20 /* copy link target UNC */ - temp =3D (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); - max_len =3D data_end - temp; + offset =3D le16_to_cpu(ref->NetworkAddressOffset); + if (offset > remaining) { + rc =3D -EINVAL; + goto parse_DFS_referrals_exit; + } + temp =3D (char *)ref + offset; + max_len =3D remaining - offset; node->node_name =3D cifs_strndup_from_utf16(temp, max_len, - is_unicode, nls_codepage); + is_unicode, + nls_codepage); if (!node->node_name) { rc =3D -ENOMEM; goto parse_DFS_referrals_exit; --=20 2.51.0