summaryrefslogtreecommitdiff
path: root/fs/smb/client
diff options
context:
space:
mode:
authorRatnaker Gautam <ratnaker.gautam@kuleuven.be>2026-10-17 23:15:05 +0200
committerPaulo Alcantara <pc@manguebit.org>2026-10-21 21:23:15 -0300
commit83d0d5e0ca22233c75daf139ec0063b9d2ff1ac7 (patch)
treeb9a9305d49a071261d7f44472e1bd17522143e9f /fs/smb/client
parent8fc9af68c57156ff64769f30e831553cd03b3000 (diff)
smb: client: validate NT transaction response layout and offsets
validate_ntransact() assumes that the response uses the fixed 18-word NT transaction layout. A different WordCount makes get_bcc() locate the byte count at a different offset from pSMBr->ByteCount, allowing the subsequent bounds checks to use inconsistent response boundaries. The function also turns the 32-bit ParameterOffset and DataOffset fields into pointers before checking their bounds. On a 32-bit client, a large offset can wrap the pointer below end_of_smb and pass the one-sided pointer comparison. The caller then dereferences the parameter pointer or copies from the data pointer. Require the expected WordCount before reading the byte count. Check each offset and count against the received SMB length as integers before forming either pointer. Use subtraction after validating the offset so that the range checks cannot overflow. Reported-by: Ratnaker Gautam <ratnaker.gautam@kuleuven.be> Fixes: 0a4b92c05ed0 ("[CIFS] Add worker function for Get ACL cifs style") Cc: stable@vger.kernel.org Signed-off-by: Ratnaker Gautam <ratnaker.gautam@kuleuven.be> Reviewed-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Paulo Alcantara <pc@manguebit.org>
Diffstat (limited to 'fs/smb/client')
-rw-r--r--fs/smb/client/cifssmb.c32
1 files changed, 19 insertions, 13 deletions
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index e335686a68f2..9ffc94ac2069 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -3787,9 +3787,10 @@ static int
validate_ntransact(char *buf, char **ppparm, char **ppdata,
__u32 *pparmlen, __u32 *pdatalen)
{
- char *end_of_smb;
__u32 data_count, data_offset, parm_count, parm_offset;
struct smb_com_ntransact_rsp *pSMBr;
+ size_t buflen;
+ char *end_buf;
u16 bcc;
*pdatalen = 0;
@@ -3800,37 +3801,42 @@ validate_ntransact(char *buf, char **ppparm, char **ppdata,
pSMBr = (struct smb_com_ntransact_rsp *)buf;
+ if (pSMBr->hdr.WordCount != 18) {
+ cifs_dbg(FYI, "invalid NT transaction WordCount %u\n",
+ pSMBr->hdr.WordCount);
+ return -EINVAL;
+ }
+
bcc = get_bcc(&pSMBr->hdr);
- end_of_smb = 2 /* sizeof byte count */ + bcc +
- (char *)&pSMBr->ByteCount;
+ end_buf = 2 /* sizeof byte count */ + bcc + (char *)&pSMBr->ByteCount;
+ buflen = end_buf - buf;
data_offset = le32_to_cpu(pSMBr->DataOffset);
data_count = le32_to_cpu(pSMBr->DataCount);
parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
parm_count = le32_to_cpu(pSMBr->ParameterCount);
- *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
- *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
-
/* should we also check that parm and data areas do not overlap? */
- if (*ppparm > end_of_smb) {
+ if (parm_offset > buflen) {
cifs_dbg(FYI, "parms start after end of smb\n");
return -EINVAL;
- } else if (parm_count + *ppparm > end_of_smb) {
+ } else if (parm_count > buflen - parm_offset) {
cifs_dbg(FYI, "parm end after end of smb\n");
return -EINVAL;
- } else if (*ppdata > end_of_smb) {
+ } else if (data_offset > buflen) {
cifs_dbg(FYI, "data starts after end of smb\n");
return -EINVAL;
- } else if (data_count + *ppdata > end_of_smb) {
- cifs_dbg(FYI, "data %p + count %d (%p) past smb end %p start %p\n",
- *ppdata, data_count, (data_count + *ppdata),
- end_of_smb, pSMBr);
+ } else if (data_count > buflen - data_offset) {
+ cifs_dbg(FYI, "data offset %u + count %u past smb length %zu\n",
+ data_offset, data_count, buflen);
return -EINVAL;
} else if (parm_count + data_count > bcc) {
cifs_dbg(FYI, "parm count and data count larger than SMB\n");
return -EINVAL;
}
+
+ *ppparm = buf + parm_offset;
+ *ppdata = buf + data_offset;
*pdatalen = data_count;
*pparmlen = parm_count;
return 0;