aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Adamson <andros@citi.umich.edu>2006-03-20 13:44:26 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2006-03-20 13:44:26 -0500
commit8dc7c3115b611c00006eac3ee5b108296432aab7 (patch)
treebe44c59907cbdcb6fdf46d0ad9cc140af757acfc
parent2e0af86f618c697b44e2d67dff151256c58201c4 (diff)
locks,lockd: fix race in nlmsvc_testlock
posix_test_lock() returns a pointer to a struct file_lock which is unprotected and can be removed while in use by the caller. Move the conflicting lock from the return to a parameter, and copy the conflicting lock. In most cases the caller ends up putting the copy of the conflicting lock on the stack. On i386, sizeof(struct file_lock) appears to be about 100 bytes. We're assuming that's reasonable. Signed-off-by: Andy Adamson <andros@citi.umich.edu> Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/lockd/svclock.c12
-rw-r--r--fs/locks.c21
-rw-r--r--fs/nfs/file.c7
-rw-r--r--fs/nfsd/nfs4state.c13
-rw-r--r--include/linux/fs.h2
5 files changed, 28 insertions, 27 deletions
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index f5398097b84b..d683dd022e08 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -376,8 +376,6 @@ u32
376nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, 376nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
377 struct nlm_lock *conflock) 377 struct nlm_lock *conflock)
378{ 378{
379 struct file_lock *fl;
380
381 dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n", 379 dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
382 file->f_file->f_dentry->d_inode->i_sb->s_id, 380 file->f_file->f_dentry->d_inode->i_sb->s_id,
383 file->f_file->f_dentry->d_inode->i_ino, 381 file->f_file->f_dentry->d_inode->i_ino,
@@ -385,14 +383,14 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
385 (long long)lock->fl.fl_start, 383 (long long)lock->fl.fl_start,
386 (long long)lock->fl.fl_end); 384 (long long)lock->fl.fl_end);
387 385
388 if ((fl = posix_test_lock(file->f_file, &lock->fl)) != NULL) { 386 if (posix_test_lock(file->f_file, &lock->fl, &conflock->fl)) {
389 dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n", 387 dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
390 fl->fl_type, (long long)fl->fl_start, 388 conflock->fl.fl_type,
391 (long long)fl->fl_end); 389 (long long)conflock->fl.fl_start,
390 (long long)conflock->fl.fl_end);
392 conflock->caller = "somehost"; /* FIXME */ 391 conflock->caller = "somehost"; /* FIXME */
393 conflock->oh.len = 0; /* don't return OH info */ 392 conflock->oh.len = 0; /* don't return OH info */
394 conflock->svid = fl->fl_pid; 393 conflock->svid = conflock->fl.fl_pid;
395 conflock->fl = *fl;
396 return nlm_lck_denied; 394 return nlm_lck_denied;
397 } 395 }
398 396
diff --git a/fs/locks.c b/fs/locks.c
index cb940b142c5f..231b23c12c3d 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -672,8 +672,9 @@ static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *w
672 return result; 672 return result;
673} 673}
674 674
675struct file_lock * 675int
676posix_test_lock(struct file *filp, struct file_lock *fl) 676posix_test_lock(struct file *filp, struct file_lock *fl,
677 struct file_lock *conflock)
677{ 678{
678 struct file_lock *cfl; 679 struct file_lock *cfl;
679 680
@@ -684,9 +685,13 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
684 if (posix_locks_conflict(cfl, fl)) 685 if (posix_locks_conflict(cfl, fl))
685 break; 686 break;
686 } 687 }
688 if (cfl) {
689 locks_copy_lock(conflock, cfl);
690 unlock_kernel();
691 return 1;
692 }
687 unlock_kernel(); 693 unlock_kernel();
688 694 return 0;
689 return (cfl);
690} 695}
691 696
692EXPORT_SYMBOL(posix_test_lock); 697EXPORT_SYMBOL(posix_test_lock);
@@ -1563,7 +1568,7 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
1563 */ 1568 */
1564int fcntl_getlk(struct file *filp, struct flock __user *l) 1569int fcntl_getlk(struct file *filp, struct flock __user *l)
1565{ 1570{
1566 struct file_lock *fl, file_lock; 1571 struct file_lock *fl, cfl, file_lock;
1567 struct flock flock; 1572 struct flock flock;
1568 int error; 1573 int error;
1569 1574
@@ -1587,7 +1592,7 @@ int fcntl_getlk(struct file *filp, struct flock __user *l)
1587 else 1592 else
1588 fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); 1593 fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
1589 } else { 1594 } else {
1590 fl = posix_test_lock(filp, &file_lock); 1595 fl = (posix_test_lock(filp, &file_lock, &cfl) ? &cfl : NULL);
1591 } 1596 }
1592 1597
1593 flock.l_type = F_UNLCK; 1598 flock.l_type = F_UNLCK;
@@ -1717,7 +1722,7 @@ out:
1717 */ 1722 */
1718int fcntl_getlk64(struct file *filp, struct flock64 __user *l) 1723int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
1719{ 1724{
1720 struct file_lock *fl, file_lock; 1725 struct file_lock *fl, cfl, file_lock;
1721 struct flock64 flock; 1726 struct flock64 flock;
1722 int error; 1727 int error;
1723 1728
@@ -1741,7 +1746,7 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
1741 else 1746 else
1742 fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); 1747 fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
1743 } else { 1748 } else {
1744 fl = posix_test_lock(filp, &file_lock); 1749 fl = (posix_test_lock(filp, &file_lock, &cfl) ? &cfl : NULL);
1745 } 1750 }
1746 1751
1747 flock.l_type = F_UNLCK; 1752 flock.l_type = F_UNLCK;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 1cf07e4ad136..ee140c53dba6 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -392,15 +392,14 @@ out_swapfile:
392 392
393static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) 393static int do_getlk(struct file *filp, int cmd, struct file_lock *fl)
394{ 394{
395 struct file_lock *cfl; 395 struct file_lock cfl;
396 struct inode *inode = filp->f_mapping->host; 396 struct inode *inode = filp->f_mapping->host;
397 int status = 0; 397 int status = 0;
398 398
399 lock_kernel(); 399 lock_kernel();
400 /* Try local locking first */ 400 /* Try local locking first */
401 cfl = posix_test_lock(filp, fl); 401 if (posix_test_lock(filp, fl, &cfl)) {
402 if (cfl != NULL) { 402 locks_copy_lock(fl, &cfl);
403 locks_copy_lock(fl, cfl);
404 goto out; 403 goto out;
405 } 404 }
406 405
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1143cfb64549..f6ab762bea99 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -2639,7 +2639,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock
2639 struct nfs4_stateid *lock_stp; 2639 struct nfs4_stateid *lock_stp;
2640 struct file *filp; 2640 struct file *filp;
2641 struct file_lock file_lock; 2641 struct file_lock file_lock;
2642 struct file_lock *conflock; 2642 struct file_lock conflock;
2643 int status = 0; 2643 int status = 0;
2644 unsigned int strhashval; 2644 unsigned int strhashval;
2645 2645
@@ -2775,11 +2775,11 @@ conflicting_lock:
2775 /* XXX There is a race here. Future patch needed to provide 2775 /* XXX There is a race here. Future patch needed to provide
2776 * an atomic posix_lock_and_test_file 2776 * an atomic posix_lock_and_test_file
2777 */ 2777 */
2778 if (!(conflock = posix_test_lock(filp, &file_lock))) { 2778 if (!posix_test_lock(filp, &file_lock, &conflock)) {
2779 status = nfserr_serverfault; 2779 status = nfserr_serverfault;
2780 goto out; 2780 goto out;
2781 } 2781 }
2782 nfs4_set_lock_denied(conflock, &lock->lk_denied); 2782 nfs4_set_lock_denied(&conflock, &lock->lk_denied);
2783out: 2783out:
2784 if (status && lock->lk_is_new && lock_sop) 2784 if (status && lock->lk_is_new && lock_sop)
2785 release_stateowner(lock_sop); 2785 release_stateowner(lock_sop);
@@ -2800,7 +2800,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock
2800 struct inode *inode; 2800 struct inode *inode;
2801 struct file file; 2801 struct file file;
2802 struct file_lock file_lock; 2802 struct file_lock file_lock;
2803 struct file_lock *conflicting_lock; 2803 struct file_lock conflock;
2804 int status; 2804 int status;
2805 2805
2806 if (nfs4_in_grace()) 2806 if (nfs4_in_grace())
@@ -2864,10 +2864,9 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock
2864 file.f_dentry = current_fh->fh_dentry; 2864 file.f_dentry = current_fh->fh_dentry;
2865 2865
2866 status = nfs_ok; 2866 status = nfs_ok;
2867 conflicting_lock = posix_test_lock(&file, &file_lock); 2867 if (posix_test_lock(&file, &file_lock, &conflock)) {
2868 if (conflicting_lock) {
2869 status = nfserr_denied; 2868 status = nfserr_denied;
2870 nfs4_set_lock_denied(conflicting_lock, &lockt->lt_denied); 2869 nfs4_set_lock_denied(&conflock, &lockt->lt_denied);
2871 } 2870 }
2872out: 2871out:
2873 nfs4_unlock_state(); 2872 nfs4_unlock_state();
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b01482c721ae..8ef4dd788a83 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -754,7 +754,7 @@ extern void locks_init_lock(struct file_lock *);
754extern void locks_copy_lock(struct file_lock *, struct file_lock *); 754extern void locks_copy_lock(struct file_lock *, struct file_lock *);
755extern void locks_remove_posix(struct file *, fl_owner_t); 755extern void locks_remove_posix(struct file *, fl_owner_t);
756extern void locks_remove_flock(struct file *); 756extern void locks_remove_flock(struct file *);
757extern struct file_lock *posix_test_lock(struct file *, struct file_lock *); 757extern int posix_test_lock(struct file *, struct file_lock *, struct file_lock *);
758extern int posix_lock_file(struct file *, struct file_lock *); 758extern int posix_lock_file(struct file *, struct file_lock *);
759extern int posix_lock_file_wait(struct file *, struct file_lock *); 759extern int posix_lock_file_wait(struct file *, struct file_lock *);
760extern int posix_unblock_lock(struct file *, struct file_lock *); 760extern int posix_unblock_lock(struct file *, struct file_lock *);