diff options
Diffstat (limited to 'fs/locks.c')
-rw-r--r-- | fs/locks.c | 59 |
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 | } |
2225 | EXPORT_SYMBOL(steal_locks); | 2242 | EXPORT_SYMBOL(steal_locks); |