aboutsummaryrefslogtreecommitdiffstats
path: root/fs/locks.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/locks.c')
-rw-r--r--fs/locks.c59
1 files changed, 38 insertions, 21 deletions
diff --git a/fs/locks.c b/fs/locks.c
index 11956b6179ff..a1e8b2248014 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -124,6 +124,7 @@
124#include <linux/smp_lock.h> 124#include <linux/smp_lock.h>
125#include <linux/syscalls.h> 125#include <linux/syscalls.h>
126#include <linux/time.h> 126#include <linux/time.h>
127#include <linux/rcupdate.h>
127 128
128#include <asm/semaphore.h> 129#include <asm/semaphore.h>
129#include <asm/uaccess.h> 130#include <asm/uaccess.h>
@@ -315,21 +316,22 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
315 /* POSIX-1996 leaves the case l->l_len < 0 undefined; 316 /* POSIX-1996 leaves the case l->l_len < 0 undefined;
316 POSIX-2001 defines it. */ 317 POSIX-2001 defines it. */
317 start += l->l_start; 318 start += l->l_start;
318 end = start + l->l_len - 1; 319 if (start < 0)
319 if (l->l_len < 0) { 320 return -EINVAL;
321 fl->fl_end = OFFSET_MAX;
322 if (l->l_len > 0) {
323 end = start + l->l_len - 1;
324 fl->fl_end = end;
325 } else if (l->l_len < 0) {
320 end = start - 1; 326 end = start - 1;
327 fl->fl_end = end;
321 start += l->l_len; 328 start += l->l_len;
329 if (start < 0)
330 return -EINVAL;
322 } 331 }
323
324 if (start < 0)
325 return -EINVAL;
326 if (l->l_len > 0 && end < 0)
327 return -EOVERFLOW;
328
329 fl->fl_start = start; /* we record the absolute position */ 332 fl->fl_start = start; /* we record the absolute position */
330 fl->fl_end = end; 333 if (fl->fl_end < fl->fl_start)
331 if (l->l_len == 0) 334 return -EOVERFLOW;
332 fl->fl_end = OFFSET_MAX;
333 335
334 fl->fl_owner = current->files; 336 fl->fl_owner = current->files;
335 fl->fl_pid = current->tgid; 337 fl->fl_pid = current->tgid;
@@ -361,14 +363,21 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
361 return -EINVAL; 363 return -EINVAL;
362 } 364 }
363 365
364 if (((start += l->l_start) < 0) || (l->l_len < 0)) 366 start += l->l_start;
367 if (start < 0)
365 return -EINVAL; 368 return -EINVAL;
366 fl->fl_end = start + l->l_len - 1; 369 fl->fl_end = OFFSET_MAX;
367 if (l->l_len > 0 && fl->fl_end < 0) 370 if (l->l_len > 0) {
368 return -EOVERFLOW; 371 fl->fl_end = start + l->l_len - 1;
372 } else if (l->l_len < 0) {
373 fl->fl_end = start - 1;
374 start += l->l_len;
375 if (start < 0)
376 return -EINVAL;
377 }
369 fl->fl_start = start; /* we record the absolute position */ 378 fl->fl_start = start; /* we record the absolute position */
370 if (l->l_len == 0) 379 if (fl->fl_end < fl->fl_start)
371 fl->fl_end = OFFSET_MAX; 380 return -EOVERFLOW;
372 381
373 fl->fl_owner = current->files; 382 fl->fl_owner = current->files;
374 fl->fl_pid = current->tgid; 383 fl->fl_pid = current->tgid;
@@ -828,12 +837,16 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request)
828 /* Detect adjacent or overlapping regions (if same lock type) 837 /* Detect adjacent or overlapping regions (if same lock type)
829 */ 838 */
830 if (request->fl_type == fl->fl_type) { 839 if (request->fl_type == fl->fl_type) {
840 /* In all comparisons of start vs end, use
841 * "start - 1" rather than "end + 1". If end
842 * is OFFSET_MAX, end + 1 will become negative.
843 */
831 if (fl->fl_end < request->fl_start - 1) 844 if (fl->fl_end < request->fl_start - 1)
832 goto next_lock; 845 goto next_lock;
833 /* If the next lock in the list has entirely bigger 846 /* If the next lock in the list has entirely bigger
834 * addresses than the new one, insert the lock here. 847 * addresses than the new one, insert the lock here.
835 */ 848 */
836 if (fl->fl_start > request->fl_end + 1) 849 if (fl->fl_start - 1 > request->fl_end)
837 break; 850 break;
838 851
839 /* If we come here, the new and old lock are of the 852 /* If we come here, the new and old lock are of the
@@ -2198,21 +2211,24 @@ void steal_locks(fl_owner_t from)
2198{ 2211{
2199 struct files_struct *files = current->files; 2212 struct files_struct *files = current->files;
2200 int i, j; 2213 int i, j;
2214 struct fdtable *fdt;
2201 2215
2202 if (from == files) 2216 if (from == files)
2203 return; 2217 return;
2204 2218
2205 lock_kernel(); 2219 lock_kernel();
2206 j = 0; 2220 j = 0;
2221 rcu_read_lock();
2222 fdt = files_fdtable(files);
2207 for (;;) { 2223 for (;;) {
2208 unsigned long set; 2224 unsigned long set;
2209 i = j * __NFDBITS; 2225 i = j * __NFDBITS;
2210 if (i >= files->max_fdset || i >= files->max_fds) 2226 if (i >= fdt->max_fdset || i >= fdt->max_fds)
2211 break; 2227 break;
2212 set = files->open_fds->fds_bits[j++]; 2228 set = fdt->open_fds->fds_bits[j++];
2213 while (set) { 2229 while (set) {
2214 if (set & 1) { 2230 if (set & 1) {
2215 struct file *file = files->fd[i]; 2231 struct file *file = fdt->fd[i];
2216 if (file) 2232 if (file)
2217 __steal_locks(file, from); 2233 __steal_locks(file, from);
2218 } 2234 }
@@ -2220,6 +2236,7 @@ void steal_locks(fl_owner_t from)
2220 set >>= 1; 2236 set >>= 1;
2221 } 2237 }
2222 } 2238 }
2239 rcu_read_unlock();
2223 unlock_kernel(); 2240 unlock_kernel();
2224} 2241}
2225EXPORT_SYMBOL(steal_locks); 2242EXPORT_SYMBOL(steal_locks);