aboutsummaryrefslogtreecommitdiffstats
path: root/fs/locks.c
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2014-02-03 12:13:08 -0500
committerJeff Layton <jlayton@redhat.com>2014-03-31 08:24:42 -0400
commitef12e72a01f3022c55e5a8e0fa1caebdc7efcfce (patch)
tree54209e0731082ff7e2a36ede9b045d415132134e /fs/locks.c
parent8c3cac5e6a85f03602ffe09c44f14418699e31ec (diff)
locks: fix posix lock range overflow handling
In the 32-bit case fcntl assigns the 64-bit f_pos and i_size to a 32-bit off_t. The existing range checks also seem to depend on signed arithmetic wrapping when it overflows. In practice maybe that works, but we can be more careful. That also allows us to make a more reliable distinction between -EINVAL and -EOVERFLOW. Note that in the 32-bit case SEEK_CUR or SEEK_END might allow the caller to set a lock with starting point no longer representable as a 32-bit value. We could return -EOVERFLOW in such cases, but the locks code is capable of handling such ranges, so we choose to be lenient here. The only problem is that subsequent GETLK calls on such a lock will fail with EOVERFLOW. While we're here, do some cleanup including consolidating code for the flock and flock64 cases. Signed-off-by: J. Bruce Fields <bfields@fieldses.org> Signed-off-by: Jeff Layton <jlayton@redhat.com>
Diffstat (limited to 'fs/locks.c')
-rw-r--r--fs/locks.c100
1 files changed, 32 insertions, 68 deletions
diff --git a/fs/locks.c b/fs/locks.c
index dd309333afc9..b49e853a9c7b 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -344,48 +344,43 @@ static int assign_type(struct file_lock *fl, long type)
344 return 0; 344 return 0;
345} 345}
346 346
347/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX 347static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
348 * style lock. 348 struct flock64 *l)
349 */
350static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
351 struct flock *l)
352{ 349{
353 off_t start, end;
354
355 switch (l->l_whence) { 350 switch (l->l_whence) {
356 case SEEK_SET: 351 case SEEK_SET:
357 start = 0; 352 fl->fl_start = 0;
358 break; 353 break;
359 case SEEK_CUR: 354 case SEEK_CUR:
360 start = filp->f_pos; 355 fl->fl_start = filp->f_pos;
361 break; 356 break;
362 case SEEK_END: 357 case SEEK_END:
363 start = i_size_read(file_inode(filp)); 358 fl->fl_start = i_size_read(file_inode(filp));
364 break; 359 break;
365 default: 360 default:
366 return -EINVAL; 361 return -EINVAL;
367 } 362 }
363 if (l->l_start > OFFSET_MAX - fl->fl_start)
364 return -EOVERFLOW;
365 fl->fl_start += l->l_start;
366 if (fl->fl_start < 0)
367 return -EINVAL;
368 368
369 /* POSIX-1996 leaves the case l->l_len < 0 undefined; 369 /* POSIX-1996 leaves the case l->l_len < 0 undefined;
370 POSIX-2001 defines it. */ 370 POSIX-2001 defines it. */
371 start += l->l_start;
372 if (start < 0)
373 return -EINVAL;
374 fl->fl_end = OFFSET_MAX;
375 if (l->l_len > 0) { 371 if (l->l_len > 0) {
376 end = start + l->l_len - 1; 372 if (l->l_len - 1 > OFFSET_MAX - fl->fl_start)
377 fl->fl_end = end; 373 return -EOVERFLOW;
374 fl->fl_end = fl->fl_start + l->l_len - 1;
375
378 } else if (l->l_len < 0) { 376 } else if (l->l_len < 0) {
379 end = start - 1; 377 if (fl->fl_start + l->l_len < 0)
380 fl->fl_end = end;
381 start += l->l_len;
382 if (start < 0)
383 return -EINVAL; 378 return -EINVAL;
384 } 379 fl->fl_end = fl->fl_start - 1;
385 fl->fl_start = start; /* we record the absolute position */ 380 fl->fl_start += l->l_len;
386 if (fl->fl_end < fl->fl_start) 381 } else
387 return -EOVERFLOW; 382 fl->fl_end = OFFSET_MAX;
388 383
389 fl->fl_owner = current->files; 384 fl->fl_owner = current->files;
390 fl->fl_pid = current->tgid; 385 fl->fl_pid = current->tgid;
391 fl->fl_file = filp; 386 fl->fl_file = filp;
@@ -396,52 +391,21 @@ static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
396 return assign_type(fl, l->l_type); 391 return assign_type(fl, l->l_type);
397} 392}
398 393
399#if BITS_PER_LONG == 32 394/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
400static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl, 395 * style lock.
401 struct flock64 *l) 396 */
397static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
398 struct flock *l)
402{ 399{
403 loff_t start; 400 struct flock64 ll = {
404 401 .l_type = l->l_type,
405 switch (l->l_whence) { 402 .l_whence = l->l_whence,
406 case SEEK_SET: 403 .l_start = l->l_start,
407 start = 0; 404 .l_len = l->l_len,
408 break; 405 };
409 case SEEK_CUR:
410 start = filp->f_pos;
411 break;
412 case SEEK_END:
413 start = i_size_read(file_inode(filp));
414 break;
415 default:
416 return -EINVAL;
417 }
418 406
419 start += l->l_start; 407 return flock64_to_posix_lock(filp, fl, &ll);
420 if (start < 0)
421 return -EINVAL;
422 fl->fl_end = OFFSET_MAX;
423 if (l->l_len > 0) {
424 fl->fl_end = start + l->l_len - 1;
425 } else if (l->l_len < 0) {
426 fl->fl_end = start - 1;
427 start += l->l_len;
428 if (start < 0)
429 return -EINVAL;
430 }
431 fl->fl_start = start; /* we record the absolute position */
432 if (fl->fl_end < fl->fl_start)
433 return -EOVERFLOW;
434
435 fl->fl_owner = current->files;
436 fl->fl_pid = current->tgid;
437 fl->fl_file = filp;
438 fl->fl_flags = FL_POSIX;
439 fl->fl_ops = NULL;
440 fl->fl_lmops = NULL;
441
442 return assign_type(fl, l->l_type);
443} 408}
444#endif
445 409
446/* default lease lock manager operations */ 410/* default lease lock manager operations */
447static void lease_break_callback(struct file_lock *fl) 411static void lease_break_callback(struct file_lock *fl)