diff options
author | Andy Adamson <andros@citi.umich.edu> | 2006-03-20 13:44:26 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-03-20 13:44:26 -0500 |
commit | 8dc7c3115b611c00006eac3ee5b108296432aab7 (patch) | |
tree | be44c59907cbdcb6fdf46d0ad9cc140af757acfc | |
parent | 2e0af86f618c697b44e2d67dff151256c58201c4 (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.c | 12 | ||||
-rw-r--r-- | fs/locks.c | 21 | ||||
-rw-r--r-- | fs/nfs/file.c | 7 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 13 | ||||
-rw-r--r-- | include/linux/fs.h | 2 |
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 | |||
376 | nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, | 376 | nlmsvc_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 | ||
675 | struct file_lock * | 675 | int |
676 | posix_test_lock(struct file *filp, struct file_lock *fl) | 676 | posix_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 | ||
692 | EXPORT_SYMBOL(posix_test_lock); | 697 | EXPORT_SYMBOL(posix_test_lock); |
@@ -1563,7 +1568,7 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) | |||
1563 | */ | 1568 | */ |
1564 | int fcntl_getlk(struct file *filp, struct flock __user *l) | 1569 | int 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 | */ |
1718 | int fcntl_getlk64(struct file *filp, struct flock64 __user *l) | 1723 | int 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 | ||
393 | static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) | 393 | static 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); |
2783 | out: | 2783 | out: |
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 | } |
2872 | out: | 2871 | out: |
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 *); | |||
754 | extern void locks_copy_lock(struct file_lock *, struct file_lock *); | 754 | extern void locks_copy_lock(struct file_lock *, struct file_lock *); |
755 | extern void locks_remove_posix(struct file *, fl_owner_t); | 755 | extern void locks_remove_posix(struct file *, fl_owner_t); |
756 | extern void locks_remove_flock(struct file *); | 756 | extern void locks_remove_flock(struct file *); |
757 | extern struct file_lock *posix_test_lock(struct file *, struct file_lock *); | 757 | extern int posix_test_lock(struct file *, struct file_lock *, struct file_lock *); |
758 | extern int posix_lock_file(struct file *, struct file_lock *); | 758 | extern int posix_lock_file(struct file *, struct file_lock *); |
759 | extern int posix_lock_file_wait(struct file *, struct file_lock *); | 759 | extern int posix_lock_file_wait(struct file *, struct file_lock *); |
760 | extern int posix_unblock_lock(struct file *, struct file_lock *); | 760 | extern int posix_unblock_lock(struct file *, struct file_lock *); |