diff options
author | Maxim Patlasov <mpatlasov@virtuozzo.com> | 2016-07-21 21:24:26 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2016-07-22 04:54:20 -0400 |
commit | cfc9fde0b07c3b44b570057c5f93dda59dca1c94 (patch) | |
tree | 7f36d4f2a49719673c436767707d25ef5f5e9041 | |
parent | 07a2daab49c549a37b5b744cbebb6e3f445f12bc (diff) |
ovl: verify upper dentry in ovl_remove_and_whiteout()
The upper dentry may become stale before we call ovl_lock_rename_workdir.
For example, someone could (mistakenly or maliciously) manually unlink(2)
it directly from upperdir.
To ensure it is not stale, let's lookup it after ovl_lock_rename_workdir
and and check if it matches the upper dentry.
Essentially, it is the same problem and similar solution as in
commit 11f3710417d0 ("ovl: verify upper dentry before unlink and rename").
Signed-off-by: Maxim Patlasov <mpatlasov@virtuozzo.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Cc: <stable@vger.kernel.org>
-rw-r--r-- | fs/overlayfs/dir.c | 54 |
1 files changed, 24 insertions, 30 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index c2a6b0894022..5c9d2d80ff70 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c | |||
@@ -505,6 +505,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir) | |||
505 | struct dentry *upper; | 505 | struct dentry *upper; |
506 | struct dentry *opaquedir = NULL; | 506 | struct dentry *opaquedir = NULL; |
507 | int err; | 507 | int err; |
508 | int flags = 0; | ||
508 | 509 | ||
509 | if (WARN_ON(!workdir)) | 510 | if (WARN_ON(!workdir)) |
510 | return -EROFS; | 511 | return -EROFS; |
@@ -534,46 +535,39 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir) | |||
534 | if (err) | 535 | if (err) |
535 | goto out_dput; | 536 | goto out_dput; |
536 | 537 | ||
537 | whiteout = ovl_whiteout(workdir, dentry); | 538 | upper = lookup_one_len(dentry->d_name.name, upperdir, |
538 | err = PTR_ERR(whiteout); | 539 | dentry->d_name.len); |
539 | if (IS_ERR(whiteout)) | 540 | err = PTR_ERR(upper); |
541 | if (IS_ERR(upper)) | ||
540 | goto out_unlock; | 542 | goto out_unlock; |
541 | 543 | ||
542 | upper = ovl_dentry_upper(dentry); | 544 | err = -ESTALE; |
543 | if (!upper) { | 545 | if ((opaquedir && upper != opaquedir) || |
544 | upper = lookup_one_len(dentry->d_name.name, upperdir, | 546 | (!opaquedir && ovl_dentry_upper(dentry) && |
545 | dentry->d_name.len); | 547 | upper != ovl_dentry_upper(dentry))) { |
546 | err = PTR_ERR(upper); | 548 | goto out_dput_upper; |
547 | if (IS_ERR(upper)) | 549 | } |
548 | goto kill_whiteout; | ||
549 | |||
550 | err = ovl_do_rename(wdir, whiteout, udir, upper, 0); | ||
551 | dput(upper); | ||
552 | if (err) | ||
553 | goto kill_whiteout; | ||
554 | } else { | ||
555 | int flags = 0; | ||
556 | 550 | ||
557 | if (opaquedir) | 551 | whiteout = ovl_whiteout(workdir, dentry); |
558 | upper = opaquedir; | 552 | err = PTR_ERR(whiteout); |
559 | err = -ESTALE; | 553 | if (IS_ERR(whiteout)) |
560 | if (upper->d_parent != upperdir) | 554 | goto out_dput_upper; |
561 | goto kill_whiteout; | ||
562 | 555 | ||
563 | if (is_dir) | 556 | if (d_is_dir(upper)) |
564 | flags |= RENAME_EXCHANGE; | 557 | flags = RENAME_EXCHANGE; |
565 | 558 | ||
566 | err = ovl_do_rename(wdir, whiteout, udir, upper, flags); | 559 | err = ovl_do_rename(wdir, whiteout, udir, upper, flags); |
567 | if (err) | 560 | if (err) |
568 | goto kill_whiteout; | 561 | goto kill_whiteout; |
562 | if (flags) | ||
563 | ovl_cleanup(wdir, upper); | ||
569 | 564 | ||
570 | if (is_dir) | ||
571 | ovl_cleanup(wdir, upper); | ||
572 | } | ||
573 | ovl_dentry_version_inc(dentry->d_parent); | 565 | ovl_dentry_version_inc(dentry->d_parent); |
574 | out_d_drop: | 566 | out_d_drop: |
575 | d_drop(dentry); | 567 | d_drop(dentry); |
576 | dput(whiteout); | 568 | dput(whiteout); |
569 | out_dput_upper: | ||
570 | dput(upper); | ||
577 | out_unlock: | 571 | out_unlock: |
578 | unlock_rename(workdir, upperdir); | 572 | unlock_rename(workdir, upperdir); |
579 | out_dput: | 573 | out_dput: |