diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-21 19:24:22 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-21 19:24:22 -0400 |
commit | 99313414dda451a8c00b139afc77a803e647f309 (patch) | |
tree | 8f23042ffbbd231154c6eceb469fcb9c941c4b7f | |
parent | 0151ef0085f946eed75da851297577f88d74c50c (diff) | |
parent | 0e082555cec9510d276965fe391f709acb32c0f4 (diff) |
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs fixes from Miklos Szeredi:
"This fixes a crash with SELinux and several other old and new bugs"
* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
ovl: check for bad and whiteout index on lookup
ovl: do not cleanup directory and whiteout index entries
ovl: fix xattr get and set with selinux
ovl: remove unneeded check for IS_ERR()
ovl: fix origin verification of index dir
ovl: mark parent impure on ovl_link()
ovl: fix random return value on mount
-rw-r--r-- | fs/overlayfs/dir.c | 22 | ||||
-rw-r--r-- | fs/overlayfs/inode.c | 32 | ||||
-rw-r--r-- | fs/overlayfs/namei.c | 41 | ||||
-rw-r--r-- | fs/overlayfs/overlayfs.h | 10 | ||||
-rw-r--r-- | fs/overlayfs/readdir.c | 5 | ||||
-rw-r--r-- | fs/overlayfs/super.c | 13 | ||||
-rw-r--r-- | fs/overlayfs/util.c | 7 |
7 files changed, 88 insertions, 42 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 641d9ee97f91..48b70e6490f3 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c | |||
@@ -481,17 +481,30 @@ out_cleanup: | |||
481 | } | 481 | } |
482 | 482 | ||
483 | static int ovl_create_or_link(struct dentry *dentry, struct inode *inode, | 483 | static int ovl_create_or_link(struct dentry *dentry, struct inode *inode, |
484 | struct cattr *attr, struct dentry *hardlink) | 484 | struct cattr *attr, struct dentry *hardlink, |
485 | bool origin) | ||
485 | { | 486 | { |
486 | int err; | 487 | int err; |
487 | const struct cred *old_cred; | 488 | const struct cred *old_cred; |
488 | struct cred *override_cred; | 489 | struct cred *override_cred; |
490 | struct dentry *parent = dentry->d_parent; | ||
489 | 491 | ||
490 | err = ovl_copy_up(dentry->d_parent); | 492 | err = ovl_copy_up(parent); |
491 | if (err) | 493 | if (err) |
492 | return err; | 494 | return err; |
493 | 495 | ||
494 | old_cred = ovl_override_creds(dentry->d_sb); | 496 | old_cred = ovl_override_creds(dentry->d_sb); |
497 | |||
498 | /* | ||
499 | * When linking a file with copy up origin into a new parent, mark the | ||
500 | * new parent dir "impure". | ||
501 | */ | ||
502 | if (origin) { | ||
503 | err = ovl_set_impure(parent, ovl_dentry_upper(parent)); | ||
504 | if (err) | ||
505 | goto out_revert_creds; | ||
506 | } | ||
507 | |||
495 | err = -ENOMEM; | 508 | err = -ENOMEM; |
496 | override_cred = prepare_creds(); | 509 | override_cred = prepare_creds(); |
497 | if (override_cred) { | 510 | if (override_cred) { |
@@ -550,7 +563,7 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev, | |||
550 | inode_init_owner(inode, dentry->d_parent->d_inode, mode); | 563 | inode_init_owner(inode, dentry->d_parent->d_inode, mode); |
551 | attr.mode = inode->i_mode; | 564 | attr.mode = inode->i_mode; |
552 | 565 | ||
553 | err = ovl_create_or_link(dentry, inode, &attr, NULL); | 566 | err = ovl_create_or_link(dentry, inode, &attr, NULL, false); |
554 | if (err) | 567 | if (err) |
555 | iput(inode); | 568 | iput(inode); |
556 | 569 | ||
@@ -609,7 +622,8 @@ static int ovl_link(struct dentry *old, struct inode *newdir, | |||
609 | inode = d_inode(old); | 622 | inode = d_inode(old); |
610 | ihold(inode); | 623 | ihold(inode); |
611 | 624 | ||
612 | err = ovl_create_or_link(new, inode, NULL, ovl_dentry_upper(old)); | 625 | err = ovl_create_or_link(new, inode, NULL, ovl_dentry_upper(old), |
626 | ovl_type_origin(old)); | ||
613 | if (err) | 627 | if (err) |
614 | iput(inode); | 628 | iput(inode); |
615 | 629 | ||
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 69f4fc26ee39..5bc71642b226 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c | |||
@@ -202,37 +202,38 @@ bool ovl_is_private_xattr(const char *name) | |||
202 | sizeof(OVL_XATTR_PREFIX) - 1) == 0; | 202 | sizeof(OVL_XATTR_PREFIX) - 1) == 0; |
203 | } | 203 | } |
204 | 204 | ||
205 | int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value, | 205 | int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name, |
206 | size_t size, int flags) | 206 | const void *value, size_t size, int flags) |
207 | { | 207 | { |
208 | int err; | 208 | int err; |
209 | struct path realpath; | 209 | struct dentry *upperdentry = ovl_i_dentry_upper(inode); |
210 | enum ovl_path_type type = ovl_path_real(dentry, &realpath); | 210 | struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry); |
211 | const struct cred *old_cred; | 211 | const struct cred *old_cred; |
212 | 212 | ||
213 | err = ovl_want_write(dentry); | 213 | err = ovl_want_write(dentry); |
214 | if (err) | 214 | if (err) |
215 | goto out; | 215 | goto out; |
216 | 216 | ||
217 | if (!value && !OVL_TYPE_UPPER(type)) { | 217 | if (!value && !upperdentry) { |
218 | err = vfs_getxattr(realpath.dentry, name, NULL, 0); | 218 | err = vfs_getxattr(realdentry, name, NULL, 0); |
219 | if (err < 0) | 219 | if (err < 0) |
220 | goto out_drop_write; | 220 | goto out_drop_write; |
221 | } | 221 | } |
222 | 222 | ||
223 | err = ovl_copy_up(dentry); | 223 | if (!upperdentry) { |
224 | if (err) | 224 | err = ovl_copy_up(dentry); |
225 | goto out_drop_write; | 225 | if (err) |
226 | goto out_drop_write; | ||
226 | 227 | ||
227 | if (!OVL_TYPE_UPPER(type)) | 228 | realdentry = ovl_dentry_upper(dentry); |
228 | ovl_path_upper(dentry, &realpath); | 229 | } |
229 | 230 | ||
230 | old_cred = ovl_override_creds(dentry->d_sb); | 231 | old_cred = ovl_override_creds(dentry->d_sb); |
231 | if (value) | 232 | if (value) |
232 | err = vfs_setxattr(realpath.dentry, name, value, size, flags); | 233 | err = vfs_setxattr(realdentry, name, value, size, flags); |
233 | else { | 234 | else { |
234 | WARN_ON(flags != XATTR_REPLACE); | 235 | WARN_ON(flags != XATTR_REPLACE); |
235 | err = vfs_removexattr(realpath.dentry, name); | 236 | err = vfs_removexattr(realdentry, name); |
236 | } | 237 | } |
237 | revert_creds(old_cred); | 238 | revert_creds(old_cred); |
238 | 239 | ||
@@ -242,12 +243,13 @@ out: | |||
242 | return err; | 243 | return err; |
243 | } | 244 | } |
244 | 245 | ||
245 | int ovl_xattr_get(struct dentry *dentry, const char *name, | 246 | int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name, |
246 | void *value, size_t size) | 247 | void *value, size_t size) |
247 | { | 248 | { |
248 | struct dentry *realdentry = ovl_dentry_real(dentry); | ||
249 | ssize_t res; | 249 | ssize_t res; |
250 | const struct cred *old_cred; | 250 | const struct cred *old_cred; |
251 | struct dentry *realdentry = | ||
252 | ovl_i_dentry_upper(inode) ?: ovl_dentry_lower(dentry); | ||
251 | 253 | ||
252 | old_cred = ovl_override_creds(dentry->d_sb); | 254 | old_cred = ovl_override_creds(dentry->d_sb); |
253 | res = vfs_getxattr(realdentry, name, value, size); | 255 | res = vfs_getxattr(realdentry, name, value, size); |
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 9bc0e580a5b3..8aef2b304b2d 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c | |||
@@ -397,8 +397,19 @@ int ovl_verify_index(struct dentry *index, struct path *lowerstack, | |||
397 | if (!d_inode(index)) | 397 | if (!d_inode(index)) |
398 | return 0; | 398 | return 0; |
399 | 399 | ||
400 | err = -EISDIR; | 400 | /* |
401 | if (d_is_dir(index)) | 401 | * Directory index entries are going to be used for looking up |
402 | * redirected upper dirs by lower dir fh when decoding an overlay | ||
403 | * file handle of a merge dir. Whiteout index entries are going to be | ||
404 | * used as an indication that an exported overlay file handle should | ||
405 | * be treated as stale (i.e. after unlink of the overlay inode). | ||
406 | * We don't know the verification rules for directory and whiteout | ||
407 | * index entries, because they have not been implemented yet, so return | ||
408 | * EROFS if those entries are found to avoid corrupting an index that | ||
409 | * was created by a newer kernel. | ||
410 | */ | ||
411 | err = -EROFS; | ||
412 | if (d_is_dir(index) || ovl_is_whiteout(index)) | ||
402 | goto fail; | 413 | goto fail; |
403 | 414 | ||
404 | err = -EINVAL; | 415 | err = -EINVAL; |
@@ -436,8 +447,8 @@ out: | |||
436 | return err; | 447 | return err; |
437 | 448 | ||
438 | fail: | 449 | fail: |
439 | pr_warn_ratelimited("overlayfs: failed to verify index (%pd2, err=%i)\n", | 450 | pr_warn_ratelimited("overlayfs: failed to verify index (%pd2, ftype=%x, err=%i)\n", |
440 | index, err); | 451 | index, d_inode(index)->i_mode & S_IFMT, err); |
441 | goto out; | 452 | goto out; |
442 | } | 453 | } |
443 | 454 | ||
@@ -502,6 +513,7 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry, | |||
502 | goto out; | 513 | goto out; |
503 | } | 514 | } |
504 | 515 | ||
516 | inode = d_inode(index); | ||
505 | if (d_is_negative(index)) { | 517 | if (d_is_negative(index)) { |
506 | if (upper && d_inode(origin)->i_nlink > 1) { | 518 | if (upper && d_inode(origin)->i_nlink > 1) { |
507 | pr_warn_ratelimited("overlayfs: hard link with origin but no index (ino=%lu).\n", | 519 | pr_warn_ratelimited("overlayfs: hard link with origin but no index (ino=%lu).\n", |
@@ -511,11 +523,22 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry, | |||
511 | 523 | ||
512 | dput(index); | 524 | dput(index); |
513 | index = NULL; | 525 | index = NULL; |
514 | } else if (upper && d_inode(index) != d_inode(upper)) { | 526 | } else if (upper && d_inode(upper) != inode) { |
515 | inode = d_inode(index); | 527 | pr_warn_ratelimited("overlayfs: wrong index found (index=%pd2, ino=%lu, upper ino=%lu).\n", |
516 | pr_warn_ratelimited("overlayfs: wrong index found (index ino: %lu, upper ino: %lu).\n", | 528 | index, inode->i_ino, d_inode(upper)->i_ino); |
517 | d_inode(index)->i_ino, | 529 | goto fail; |
518 | d_inode(upper)->i_ino); | 530 | } else if (ovl_dentry_weird(index) || ovl_is_whiteout(index) || |
531 | ((inode->i_mode ^ d_inode(origin)->i_mode) & S_IFMT)) { | ||
532 | /* | ||
533 | * Index should always be of the same file type as origin | ||
534 | * except for the case of a whiteout index. A whiteout | ||
535 | * index should only exist if all lower aliases have been | ||
536 | * unlinked, which means that finding a lower origin on lookup | ||
537 | * whose index is a whiteout should be treated as an error. | ||
538 | */ | ||
539 | pr_warn_ratelimited("overlayfs: bad index found (index=%pd2, ftype=%x, origin ftype=%x).\n", | ||
540 | index, d_inode(index)->i_mode & S_IFMT, | ||
541 | d_inode(origin)->i_mode & S_IFMT); | ||
519 | goto fail; | 542 | goto fail; |
520 | } | 543 | } |
521 | 544 | ||
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 60d26605e039..e927a62c97ae 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h | |||
@@ -47,7 +47,8 @@ enum ovl_flag { | |||
47 | /* Is the real inode encoded in fid an upper inode? */ | 47 | /* Is the real inode encoded in fid an upper inode? */ |
48 | #define OVL_FH_FLAG_PATH_UPPER (1 << 2) | 48 | #define OVL_FH_FLAG_PATH_UPPER (1 << 2) |
49 | 49 | ||
50 | #define OVL_FH_FLAG_ALL (OVL_FH_FLAG_BIG_ENDIAN | OVL_FH_FLAG_ANY_ENDIAN) | 50 | #define OVL_FH_FLAG_ALL (OVL_FH_FLAG_BIG_ENDIAN | OVL_FH_FLAG_ANY_ENDIAN | \ |
51 | OVL_FH_FLAG_PATH_UPPER) | ||
51 | 52 | ||
52 | #if defined(__LITTLE_ENDIAN) | 53 | #if defined(__LITTLE_ENDIAN) |
53 | #define OVL_FH_FLAG_CPU_ENDIAN 0 | 54 | #define OVL_FH_FLAG_CPU_ENDIAN 0 |
@@ -199,6 +200,7 @@ enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path); | |||
199 | struct dentry *ovl_dentry_upper(struct dentry *dentry); | 200 | struct dentry *ovl_dentry_upper(struct dentry *dentry); |
200 | struct dentry *ovl_dentry_lower(struct dentry *dentry); | 201 | struct dentry *ovl_dentry_lower(struct dentry *dentry); |
201 | struct dentry *ovl_dentry_real(struct dentry *dentry); | 202 | struct dentry *ovl_dentry_real(struct dentry *dentry); |
203 | struct dentry *ovl_i_dentry_upper(struct inode *inode); | ||
202 | struct inode *ovl_inode_upper(struct inode *inode); | 204 | struct inode *ovl_inode_upper(struct inode *inode); |
203 | struct inode *ovl_inode_lower(struct inode *inode); | 205 | struct inode *ovl_inode_lower(struct inode *inode); |
204 | struct inode *ovl_inode_real(struct inode *inode); | 206 | struct inode *ovl_inode_real(struct inode *inode); |
@@ -270,9 +272,9 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr); | |||
270 | int ovl_getattr(const struct path *path, struct kstat *stat, | 272 | int ovl_getattr(const struct path *path, struct kstat *stat, |
271 | u32 request_mask, unsigned int flags); | 273 | u32 request_mask, unsigned int flags); |
272 | int ovl_permission(struct inode *inode, int mask); | 274 | int ovl_permission(struct inode *inode, int mask); |
273 | int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value, | 275 | int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name, |
274 | size_t size, int flags); | 276 | const void *value, size_t size, int flags); |
275 | int ovl_xattr_get(struct dentry *dentry, const char *name, | 277 | int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name, |
276 | void *value, size_t size); | 278 | void *value, size_t size); |
277 | ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size); | 279 | ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size); |
278 | struct posix_acl *ovl_get_acl(struct inode *inode, int type); | 280 | struct posix_acl *ovl_get_acl(struct inode *inode, int type); |
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 0298463cf9c3..3d424a51cabb 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c | |||
@@ -703,7 +703,10 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, | |||
703 | err = PTR_ERR(index); | 703 | err = PTR_ERR(index); |
704 | break; | 704 | break; |
705 | } | 705 | } |
706 | if (ovl_verify_index(index, lowerstack, numlower)) { | 706 | err = ovl_verify_index(index, lowerstack, numlower); |
707 | if (err) { | ||
708 | if (err == -EROFS) | ||
709 | break; | ||
707 | err = ovl_cleanup(dir, index); | 710 | err = ovl_cleanup(dir, index); |
708 | if (err) | 711 | if (err) |
709 | break; | 712 | break; |
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 44dc2d6ffe0f..d86e89f97201 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c | |||
@@ -692,7 +692,7 @@ ovl_posix_acl_xattr_get(const struct xattr_handler *handler, | |||
692 | struct dentry *dentry, struct inode *inode, | 692 | struct dentry *dentry, struct inode *inode, |
693 | const char *name, void *buffer, size_t size) | 693 | const char *name, void *buffer, size_t size) |
694 | { | 694 | { |
695 | return ovl_xattr_get(dentry, handler->name, buffer, size); | 695 | return ovl_xattr_get(dentry, inode, handler->name, buffer, size); |
696 | } | 696 | } |
697 | 697 | ||
698 | static int __maybe_unused | 698 | static int __maybe_unused |
@@ -742,7 +742,7 @@ ovl_posix_acl_xattr_set(const struct xattr_handler *handler, | |||
742 | return err; | 742 | return err; |
743 | } | 743 | } |
744 | 744 | ||
745 | err = ovl_xattr_set(dentry, handler->name, value, size, flags); | 745 | err = ovl_xattr_set(dentry, inode, handler->name, value, size, flags); |
746 | if (!err) | 746 | if (!err) |
747 | ovl_copyattr(ovl_inode_real(inode), inode); | 747 | ovl_copyattr(ovl_inode_real(inode), inode); |
748 | 748 | ||
@@ -772,7 +772,7 @@ static int ovl_other_xattr_get(const struct xattr_handler *handler, | |||
772 | struct dentry *dentry, struct inode *inode, | 772 | struct dentry *dentry, struct inode *inode, |
773 | const char *name, void *buffer, size_t size) | 773 | const char *name, void *buffer, size_t size) |
774 | { | 774 | { |
775 | return ovl_xattr_get(dentry, name, buffer, size); | 775 | return ovl_xattr_get(dentry, inode, name, buffer, size); |
776 | } | 776 | } |
777 | 777 | ||
778 | static int ovl_other_xattr_set(const struct xattr_handler *handler, | 778 | static int ovl_other_xattr_set(const struct xattr_handler *handler, |
@@ -780,7 +780,7 @@ static int ovl_other_xattr_set(const struct xattr_handler *handler, | |||
780 | const char *name, const void *value, | 780 | const char *name, const void *value, |
781 | size_t size, int flags) | 781 | size_t size, int flags) |
782 | { | 782 | { |
783 | return ovl_xattr_set(dentry, name, value, size, flags); | 783 | return ovl_xattr_set(dentry, inode, name, value, size, flags); |
784 | } | 784 | } |
785 | 785 | ||
786 | static const struct xattr_handler __maybe_unused | 786 | static const struct xattr_handler __maybe_unused |
@@ -1058,10 +1058,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
1058 | 1058 | ||
1059 | ufs->indexdir = ovl_workdir_create(sb, ufs, workpath.dentry, | 1059 | ufs->indexdir = ovl_workdir_create(sb, ufs, workpath.dentry, |
1060 | OVL_INDEXDIR_NAME, true); | 1060 | OVL_INDEXDIR_NAME, true); |
1061 | err = PTR_ERR(ufs->indexdir); | ||
1062 | if (IS_ERR(ufs->indexdir)) | ||
1063 | goto out_put_lower_mnt; | ||
1064 | |||
1065 | if (ufs->indexdir) { | 1061 | if (ufs->indexdir) { |
1066 | /* Verify upper root is index dir origin */ | 1062 | /* Verify upper root is index dir origin */ |
1067 | err = ovl_verify_origin(ufs->indexdir, ufs->upper_mnt, | 1063 | err = ovl_verify_origin(ufs->indexdir, ufs->upper_mnt, |
@@ -1090,6 +1086,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
1090 | else | 1086 | else |
1091 | sb->s_d_op = &ovl_dentry_operations; | 1087 | sb->s_d_op = &ovl_dentry_operations; |
1092 | 1088 | ||
1089 | err = -ENOMEM; | ||
1093 | ufs->creator_cred = cred = prepare_creds(); | 1090 | ufs->creator_cred = cred = prepare_creds(); |
1094 | if (!cred) | 1091 | if (!cred) |
1095 | goto out_put_indexdir; | 1092 | goto out_put_indexdir; |
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index c492ba75c659..f46ad75dc96a 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c | |||
@@ -157,9 +157,14 @@ struct dentry *ovl_dentry_real(struct dentry *dentry) | |||
157 | return ovl_dentry_upper(dentry) ?: ovl_dentry_lower(dentry); | 157 | return ovl_dentry_upper(dentry) ?: ovl_dentry_lower(dentry); |
158 | } | 158 | } |
159 | 159 | ||
160 | struct dentry *ovl_i_dentry_upper(struct inode *inode) | ||
161 | { | ||
162 | return ovl_upperdentry_dereference(OVL_I(inode)); | ||
163 | } | ||
164 | |||
160 | struct inode *ovl_inode_upper(struct inode *inode) | 165 | struct inode *ovl_inode_upper(struct inode *inode) |
161 | { | 166 | { |
162 | struct dentry *upperdentry = ovl_upperdentry_dereference(OVL_I(inode)); | 167 | struct dentry *upperdentry = ovl_i_dentry_upper(inode); |
163 | 168 | ||
164 | return upperdentry ? d_inode(upperdentry) : NULL; | 169 | return upperdentry ? d_inode(upperdentry) : NULL; |
165 | } | 170 | } |