[linux-cifs-client] [PATCH] cifs: Fix a kernel BUG with remote OS/2 server

Suresh Jayaraman sjayaraman at suse.de
Tue Mar 30 02:47:43 MDT 2010


While chasing a bug report involving a OS/2 server, I noticed the server sets
pSMBr->CountHigh to a incorrect value even in case of normal writes. This
leads to 'nbytes' being computed wrongly and triggers a kernel BUG at
mm/filemap.c.

void iov_iter_advance(struct iov_iter *i, size_t bytes)
{
        BUG_ON(i->count < bytes);    <--- BUG here

Why the server is setting 'CountHigh' is not clear but only does so after
writing 64k bytes. Though this looks like the server bug, the client side
crash may not be acceptable.

Looking up the CIFS spec, the purpose of 'CountHigh' field in WriteAndX
response is not clear. But since 'nbytes' can be greater than 64k only
in case of large writes (default writes can be upto 56k), I assume 'CountHigh'
is used only for larger writes. The below patch workarounds the case when
servers set 'CountHigh' incorrectly for normal writes.

Signed-off-by: Suresh Jayaraman <sjayaraman at suse.de>
---
 fs/cifs/cifssmb.c |   20 ++++++++++++++++----
 1 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 7cc7f83..ceced62 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1514,8 +1514,14 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
 		cFYI(1, ("Send error in write = %d", rc));
 		*nbytes = 0;
 	} else {
-		*nbytes = le16_to_cpu(pSMBr->CountHigh);
-		*nbytes = (*nbytes) << 16;
+		/*
+		 * Some legacy servers (read OS/2) might incorrectly set
+		 * CountHigh for normal writes resulting in wrong 'nbytes' value
+		 */
+		if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
+			*nbytes = le16_to_cpu(pSMBr->CountHigh);
+			*nbytes = (*nbytes) << 16;
+		}
 		*nbytes += le16_to_cpu(pSMBr->Count);
 	}
 
@@ -1602,8 +1608,14 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
 		rc = -EIO;
 	} else {
 		WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
-		*nbytes = le16_to_cpu(pSMBr->CountHigh);
-		*nbytes = (*nbytes) << 16;
+		/*
+		 * Some legacy servers (read OS/2) might incorrectly set
+		 * CountHigh for normal writes resulting in wrong 'nbytes' value
+		 */
+		if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
+			*nbytes = le16_to_cpu(pSMBr->CountHigh);
+			*nbytes = (*nbytes) << 16;
+		}
 		*nbytes += le16_to_cpu(pSMBr->Count);
 	}
 


More information about the linux-cifs-client mailing list