From b5efb978469d152c2c7c0a09746fb0bfc6171868 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 27 Mar 2012 15:36:15 +0400 Subject: CIFS: Fix VFS lock usage for oplocked files We can deadlock if we have a write oplock and two processes use the same file handle. In this case the first process can't unlock its lock if another process blocked on the lock in the same time. Fix this by removing lock_mutex protection from waiting on a blocked lock and protect only posix_lock_file call. Cc: stable@kernel.org Signed-off-by: Pavel Shilovsky Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/file.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 460d87b7cda..0a11dbbbb13 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -671,6 +671,21 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock) } } +/* + * Copied from fs/locks.c with small changes. + * Remove waiter from blocker's block list. + * When blocker ends up pointing to itself then the list is empty. + */ +static void +cifs_locks_delete_block(struct file_lock *waiter) +{ + lock_flocks(); + list_del_init(&waiter->fl_block); + list_del_init(&waiter->fl_link); + waiter->fl_next = NULL; + unlock_flocks(); +} + static bool __cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset, __u64 length, __u8 type, __u16 netfid, @@ -820,6 +835,39 @@ cifs_posix_lock_test(struct file *file, struct file_lock *flock) return rc; } +/* Called with locked lock_mutex, return with unlocked. */ +static int +cifs_posix_lock_file_wait_locked(struct file *file, struct file_lock *flock) +{ + struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode); + int rc; + + while (true) { + rc = posix_lock_file(file, flock, NULL); + mutex_unlock(&cinode->lock_mutex); + if (rc != FILE_LOCK_DEFERRED) + break; + rc = wait_event_interruptible(flock->fl_wait, !flock->fl_next); + if (!rc) { + mutex_lock(&cinode->lock_mutex); + continue; + } + cifs_locks_delete_block(flock); + break; + } + return rc; +} + +static int +cifs_posix_lock_file_wait(struct file *file, struct file_lock *flock) +{ + struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode); + + mutex_lock(&cinode->lock_mutex); + /* lock_mutex will be released by the function below */ + return cifs_posix_lock_file_wait_locked(file, flock); +} + /* * Set the byte-range lock (posix style). Returns: * 1) 0, if we set the lock and don't need to request to the server; @@ -840,9 +888,9 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock) mutex_unlock(&cinode->lock_mutex); return rc; } - rc = posix_lock_file_wait(file, flock); - mutex_unlock(&cinode->lock_mutex); - return rc; + + /* lock_mutex will be released by the function below */ + return cifs_posix_lock_file_wait_locked(file, flock); } static int @@ -1338,7 +1386,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u8 type, out: if (flock->fl_flags & FL_POSIX) - posix_lock_file_wait(file, flock); + cifs_posix_lock_file_wait(file, flock); return rc; } -- cgit v1.2.3 From b2a3ad9ca502169fc4c11296fa20f56059c7c031 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 26 Mar 2012 09:55:29 -0400 Subject: cifs: silence compiler warnings showing up with gcc-4.7.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc-4.7.0 has started throwing these warnings when building cifs.ko. CC [M] fs/cifs/cifssmb.o fs/cifs/cifssmb.c: In function ‘CIFSSMBSetCIFSACL’: fs/cifs/cifssmb.c:3905:9: warning: array subscript is above array bounds [-Warray-bounds] fs/cifs/cifssmb.c: In function ‘CIFSSMBSetFileInfo’: fs/cifs/cifssmb.c:5711:8: warning: array subscript is above array bounds [-Warray-bounds] fs/cifs/cifssmb.c: In function ‘CIFSSMBUnixSetFileInfo’: fs/cifs/cifssmb.c:6001:25: warning: array subscript is above array bounds [-Warray-bounds] This patch cleans up the code a bit by using the offsetof macro instead of the funky "&pSMB->hdr.Protocol" construct. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 8fecc99be34..f52c5ab78f9 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3892,13 +3892,12 @@ CIFSSMBSetCIFSACL(const int xid, struct cifs_tcon *tcon, __u16 fid, int rc = 0; int bytes_returned = 0; SET_SEC_DESC_REQ *pSMB = NULL; - NTRANSACT_RSP *pSMBr = NULL; + void *pSMBr; setCifsAclRetry: - rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, - (void **) &pSMBr); + rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, &pSMBr); if (rc) - return (rc); + return rc; pSMB->MaxSetupCount = 0; pSMB->Reserved = 0; @@ -3926,9 +3925,8 @@ setCifsAclRetry: pSMB->AclFlags = cpu_to_le32(aclflag); if (pntsd && acllen) { - memcpy((char *) &pSMBr->hdr.Protocol + data_offset, - (char *) pntsd, - acllen); + memcpy((char *)pSMBr + offsetof(struct smb_hdr, Protocol) + + data_offset, pntsd, acllen); inc_rfc1001_len(pSMB, byte_count + data_count); } else inc_rfc1001_len(pSMB, byte_count); @@ -5708,7 +5706,8 @@ CIFSSMBSetFileInfo(const int xid, struct cifs_tcon *tcon, param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; - data_offset = (char *) (&pSMB->hdr.Protocol) + offset; + data_offset = (char *)pSMB + + offsetof(struct smb_hdr, Protocol) + offset; count = sizeof(FILE_BASIC_INFO); pSMB->MaxParameterCount = cpu_to_le16(2); @@ -5977,7 +5976,7 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon, u16 fid, u32 pid_of_opener) { struct smb_com_transaction2_sfi_req *pSMB = NULL; - FILE_UNIX_BASIC_INFO *data_offset; + char *data_offset; int rc = 0; u16 params, param_offset, offset, byte_count, count; @@ -5999,8 +5998,9 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon, param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; - data_offset = (FILE_UNIX_BASIC_INFO *) - ((char *)(&pSMB->hdr.Protocol) + offset); + data_offset = (char *)pSMB + + offsetof(struct smb_hdr, Protocol) + offset; + count = sizeof(FILE_UNIX_BASIC_INFO); pSMB->MaxParameterCount = cpu_to_le16(2); @@ -6022,7 +6022,7 @@ CIFSSMBUnixSetFileInfo(const int xid, struct cifs_tcon *tcon, inc_rfc1001_len(pSMB, byte_count); pSMB->ByteCount = cpu_to_le16(byte_count); - cifs_fill_unix_set_info(data_offset, args); + cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args); rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0); if (rc) -- cgit v1.2.3 From 2545e0720a5a4bf8ebccc6f793f97a246cf3f18d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 1 Mar 2012 10:06:52 +0300 Subject: cifs: writing past end of struct in cifs_convert_address() "s6->sin6_scope_id" is an int bits but strict_strtoul() writes a long so this can corrupt memory on 64 bit systems. Signed-off-by: Dan Carpenter Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/netmisc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index dd23a321bdd..581c225f7f5 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -197,8 +197,7 @@ cifs_convert_address(struct sockaddr *dst, const char *src, int len) memcpy(scope_id, pct + 1, slen); scope_id[slen] = '\0'; - rc = strict_strtoul(scope_id, 0, - (unsigned long *)&s6->sin6_scope_id); + rc = kstrtouint(scope_id, 0, &s6->sin6_scope_id); rc = (rc == 0) ? 1 : 0; } -- cgit v1.2.3 From 9ebb389d0a03b4415fe9014f6922a2412cb1109c Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 1 Apr 2012 13:52:54 -0500 Subject: Revert "CIFS: Fix VFS lock usage for oplocked files" Revert previous version of patch to incorporate feedback so that we can merge version 3 of the patch instead.w This reverts commit b5efb978469d152c2c7c0a09746fb0bfc6171868. --- fs/cifs/file.c | 56 ++++---------------------------------------------------- 1 file changed, 4 insertions(+), 52 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 0a11dbbbb13..460d87b7cda 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -671,21 +671,6 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock) } } -/* - * Copied from fs/locks.c with small changes. - * Remove waiter from blocker's block list. - * When blocker ends up pointing to itself then the list is empty. - */ -static void -cifs_locks_delete_block(struct file_lock *waiter) -{ - lock_flocks(); - list_del_init(&waiter->fl_block); - list_del_init(&waiter->fl_link); - waiter->fl_next = NULL; - unlock_flocks(); -} - static bool __cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset, __u64 length, __u8 type, __u16 netfid, @@ -835,39 +820,6 @@ cifs_posix_lock_test(struct file *file, struct file_lock *flock) return rc; } -/* Called with locked lock_mutex, return with unlocked. */ -static int -cifs_posix_lock_file_wait_locked(struct file *file, struct file_lock *flock) -{ - struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode); - int rc; - - while (true) { - rc = posix_lock_file(file, flock, NULL); - mutex_unlock(&cinode->lock_mutex); - if (rc != FILE_LOCK_DEFERRED) - break; - rc = wait_event_interruptible(flock->fl_wait, !flock->fl_next); - if (!rc) { - mutex_lock(&cinode->lock_mutex); - continue; - } - cifs_locks_delete_block(flock); - break; - } - return rc; -} - -static int -cifs_posix_lock_file_wait(struct file *file, struct file_lock *flock) -{ - struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode); - - mutex_lock(&cinode->lock_mutex); - /* lock_mutex will be released by the function below */ - return cifs_posix_lock_file_wait_locked(file, flock); -} - /* * Set the byte-range lock (posix style). Returns: * 1) 0, if we set the lock and don't need to request to the server; @@ -888,9 +840,9 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock) mutex_unlock(&cinode->lock_mutex); return rc; } - - /* lock_mutex will be released by the function below */ - return cifs_posix_lock_file_wait_locked(file, flock); + rc = posix_lock_file_wait(file, flock); + mutex_unlock(&cinode->lock_mutex); + return rc; } static int @@ -1386,7 +1338,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u8 type, out: if (flock->fl_flags & FL_POSIX) - cifs_posix_lock_file_wait(file, flock); + posix_lock_file_wait(file, flock); return rc; } -- cgit v1.2.3 From 66189be74ff5f9f3fd6444315b85be210d07cef2 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Wed, 28 Mar 2012 21:56:19 +0400 Subject: CIFS: Fix VFS lock usage for oplocked files We can deadlock if we have a write oplock and two processes use the same file handle. In this case the first process can't unlock its lock if the second process blocked on the lock in the same time. Fix it by using posix_lock_file rather than posix_lock_file_wait under cinode->lock_mutex. If we request a blocking lock and posix_lock_file indicates that there is another lock that prevents us, wait untill that lock is released and restart our call. Cc: stable@kernel.org Acked-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/file.c | 10 +++++++++- fs/locks.c | 3 ++- include/linux/fs.h | 5 +++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 460d87b7cda..fae765dac93 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -835,13 +835,21 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock) if ((flock->fl_flags & FL_POSIX) == 0) return rc; +try_again: mutex_lock(&cinode->lock_mutex); if (!cinode->can_cache_brlcks) { mutex_unlock(&cinode->lock_mutex); return rc; } - rc = posix_lock_file_wait(file, flock); + + rc = posix_lock_file(file, flock, NULL); mutex_unlock(&cinode->lock_mutex); + if (rc == FILE_LOCK_DEFERRED) { + rc = wait_event_interruptible(flock->fl_wait, !flock->fl_next); + if (!rc) + goto try_again; + locks_delete_block(flock); + } return rc; } diff --git a/fs/locks.c b/fs/locks.c index 637694bf3a0..0d68f1f8179 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -510,12 +510,13 @@ static void __locks_delete_block(struct file_lock *waiter) /* */ -static void locks_delete_block(struct file_lock *waiter) +void locks_delete_block(struct file_lock *waiter) { lock_flocks(); __locks_delete_block(waiter); unlock_flocks(); } +EXPORT_SYMBOL(locks_delete_block); /* Insert waiter into blocker's block list. * We use a circular list so that processes can be easily woken up in diff --git a/include/linux/fs.h b/include/linux/fs.h index 135693e79f2..528611843ba 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1215,6 +1215,7 @@ extern int vfs_setlease(struct file *, long, struct file_lock **); extern int lease_modify(struct file_lock **, int); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); extern int lock_may_write(struct inode *, loff_t start, unsigned long count); +extern void locks_delete_block(struct file_lock *waiter); extern void lock_flocks(void); extern void unlock_flocks(void); #else /* !CONFIG_FILE_LOCKING */ @@ -1359,6 +1360,10 @@ static inline int lock_may_write(struct inode *inode, loff_t start, return 1; } +static inline void locks_delete_block(struct file_lock *waiter) +{ +} + static inline void lock_flocks(void) { } -- cgit v1.2.3 From 1023807458b6365e28c66095648e1b66e04a4259 Mon Sep 17 00:00:00 2001 From: Sachin Prabhu Date: Wed, 28 Mar 2012 18:07:08 +0100 Subject: Remove unnecessary check for NULL in password parser The password parser has an unnecessary check for a NULL value which triggers warnings in source checking tools. The code contains artifacts from the old parsing code which are no longer required. Signed-off-by: Sachin Prabhu Reviewed-by: Jeff Layton Reported-by: Dan Carpenter Signed-off-by: Steve French --- fs/cifs/connect.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 302a15c505a..0511fdbdf92 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1565,8 +1565,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, /* Obtain the value string */ value = strchr(data, '='); - if (value != NULL) - *value++ = '\0'; + value++; /* Set tmp_end to end of the string */ tmp_end = (char *) value + strlen(value); -- cgit v1.2.3 From e4b41fb9dafb9af4fecb602bf73d858ab651eeed Mon Sep 17 00:00:00 2001 From: Sachin Prabhu Date: Wed, 4 Apr 2012 01:58:56 +0100 Subject: Fix UNC parsing on mount The code cleanup of cifs_parse_mount_options resulted in a new bug being introduced in the parsing of the UNC. This results in vol->UNC being modified before vol->UNC was allocated. Reported-by: Steve French Signed-off-by: Sachin Prabhu Signed-off-by: Steve French --- fs/cifs/connect.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 0511fdbdf92..d81e933a796 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1648,6 +1648,13 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, goto cifs_parse_mount_err; } + vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); + if (vol->UNC == NULL) { + printk(KERN_WARNING "CIFS: no memory for UNC\n"); + goto cifs_parse_mount_err; + } + strcpy(vol->UNC, string); + if (strncmp(string, "//", 2) == 0) { vol->UNC[0] = '\\'; vol->UNC[1] = '\\'; @@ -1657,13 +1664,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, goto cifs_parse_mount_err; } - vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); - if (vol->UNC == NULL) { - printk(KERN_WARNING "CIFS: no memory " - "for UNC\n"); - goto cifs_parse_mount_err; - } - strcpy(vol->UNC, string); break; case Opt_domain: string = match_strdup(args); -- cgit v1.2.3