diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-12-15 15:46:48 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-12-15 15:46:48 -0500 |
commit | 227701e0e7078065f24f5a6ca220218fd80023a5 (patch) | |
tree | 6cf2337d3a9a57f81c7e96278fceb505256327b8 | |
parent | 06f976ecc7afbf2e68c44ec9e591af4ceca04ca6 (diff) | |
parent | da2e6b7eeda8919f677c790ef51161dd02e513a6 (diff) |
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs fixes from Miklos Szeredi:
- fix incomplete syncing of filesystem
- fix regression in readdir on ovl over 9p
- only follow redirects when needed
- misc fixes and cleanups
* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
ovl: fix overlay: warning prefix
ovl: Use PTR_ERR_OR_ZERO()
ovl: Sync upper dirty data when syncing overlayfs
ovl: update ctx->pos on impure dir iteration
ovl: Pass ovl_get_nlink() parameters in right order
ovl: don't follow redirects if redirect_dir=off
-rw-r--r-- | Documentation/filesystems/overlayfs.txt | 34 | ||||
-rw-r--r-- | fs/overlayfs/Kconfig | 10 | ||||
-rw-r--r-- | fs/overlayfs/dir.c | 3 | ||||
-rw-r--r-- | fs/overlayfs/namei.c | 18 | ||||
-rw-r--r-- | fs/overlayfs/overlayfs.h | 2 | ||||
-rw-r--r-- | fs/overlayfs/ovl_entry.h | 2 | ||||
-rw-r--r-- | fs/overlayfs/readdir.c | 7 | ||||
-rw-r--r-- | fs/overlayfs/super.c | 87 |
8 files changed, 137 insertions, 26 deletions
diff --git a/Documentation/filesystems/overlayfs.txt b/Documentation/filesystems/overlayfs.txt index 8caa60734647..e6a5f4912b6d 100644 --- a/Documentation/filesystems/overlayfs.txt +++ b/Documentation/filesystems/overlayfs.txt | |||
@@ -156,6 +156,40 @@ handle it in two different ways: | |||
156 | root of the overlay. Finally the directory is moved to the new | 156 | root of the overlay. Finally the directory is moved to the new |
157 | location. | 157 | location. |
158 | 158 | ||
159 | There are several ways to tune the "redirect_dir" feature. | ||
160 | |||
161 | Kernel config options: | ||
162 | |||
163 | - OVERLAY_FS_REDIRECT_DIR: | ||
164 | If this is enabled, then redirect_dir is turned on by default. | ||
165 | - OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW: | ||
166 | If this is enabled, then redirects are always followed by default. Enabling | ||
167 | this results in a less secure configuration. Enable this option only when | ||
168 | worried about backward compatibility with kernels that have the redirect_dir | ||
169 | feature and follow redirects even if turned off. | ||
170 | |||
171 | Module options (can also be changed through /sys/module/overlay/parameters/*): | ||
172 | |||
173 | - "redirect_dir=BOOL": | ||
174 | See OVERLAY_FS_REDIRECT_DIR kernel config option above. | ||
175 | - "redirect_always_follow=BOOL": | ||
176 | See OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW kernel config option above. | ||
177 | - "redirect_max=NUM": | ||
178 | The maximum number of bytes in an absolute redirect (default is 256). | ||
179 | |||
180 | Mount options: | ||
181 | |||
182 | - "redirect_dir=on": | ||
183 | Redirects are enabled. | ||
184 | - "redirect_dir=follow": | ||
185 | Redirects are not created, but followed. | ||
186 | - "redirect_dir=off": | ||
187 | Redirects are not created and only followed if "redirect_always_follow" | ||
188 | feature is enabled in the kernel/module config. | ||
189 | - "redirect_dir=nofollow": | ||
190 | Redirects are not created and not followed (equivalent to "redirect_dir=off" | ||
191 | if "redirect_always_follow" feature is not enabled). | ||
192 | |||
159 | Non-directories | 193 | Non-directories |
160 | --------------- | 194 | --------------- |
161 | 195 | ||
diff --git a/fs/overlayfs/Kconfig b/fs/overlayfs/Kconfig index cbfc196e5dc5..5ac415466861 100644 --- a/fs/overlayfs/Kconfig +++ b/fs/overlayfs/Kconfig | |||
@@ -24,6 +24,16 @@ config OVERLAY_FS_REDIRECT_DIR | |||
24 | an overlay which has redirects on a kernel that doesn't support this | 24 | an overlay which has redirects on a kernel that doesn't support this |
25 | feature will have unexpected results. | 25 | feature will have unexpected results. |
26 | 26 | ||
27 | config OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW | ||
28 | bool "Overlayfs: follow redirects even if redirects are turned off" | ||
29 | default y | ||
30 | depends on OVERLAY_FS | ||
31 | help | ||
32 | Disable this to get a possibly more secure configuration, but that | ||
33 | might not be backward compatible with previous kernels. | ||
34 | |||
35 | For more information, see Documentation/filesystems/overlayfs.txt | ||
36 | |||
27 | config OVERLAY_FS_INDEX | 37 | config OVERLAY_FS_INDEX |
28 | bool "Overlayfs: turn on inodes index feature by default" | 38 | bool "Overlayfs: turn on inodes index feature by default" |
29 | depends on OVERLAY_FS | 39 | depends on OVERLAY_FS |
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index e13921824c70..f9788bc116a8 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c | |||
@@ -887,7 +887,8 @@ static int ovl_set_redirect(struct dentry *dentry, bool samedir) | |||
887 | spin_unlock(&dentry->d_lock); | 887 | spin_unlock(&dentry->d_lock); |
888 | } else { | 888 | } else { |
889 | kfree(redirect); | 889 | kfree(redirect); |
890 | pr_warn_ratelimited("overlay: failed to set redirect (%i)\n", err); | 890 | pr_warn_ratelimited("overlayfs: failed to set redirect (%i)\n", |
891 | err); | ||
891 | /* Fall back to userspace copy-up */ | 892 | /* Fall back to userspace copy-up */ |
892 | err = -EXDEV; | 893 | err = -EXDEV; |
893 | } | 894 | } |
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 625ed8066570..beb945e1963c 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c | |||
@@ -435,7 +435,7 @@ int ovl_verify_index(struct dentry *index, struct ovl_path *lower, | |||
435 | 435 | ||
436 | /* Check if index is orphan and don't warn before cleaning it */ | 436 | /* Check if index is orphan and don't warn before cleaning it */ |
437 | if (d_inode(index)->i_nlink == 1 && | 437 | if (d_inode(index)->i_nlink == 1 && |
438 | ovl_get_nlink(index, origin.dentry, 0) == 0) | 438 | ovl_get_nlink(origin.dentry, index, 0) == 0) |
439 | err = -ENOENT; | 439 | err = -ENOENT; |
440 | 440 | ||
441 | dput(origin.dentry); | 441 | dput(origin.dentry); |
@@ -681,6 +681,22 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, | |||
681 | if (d.stop) | 681 | if (d.stop) |
682 | break; | 682 | break; |
683 | 683 | ||
684 | /* | ||
685 | * Following redirects can have security consequences: it's like | ||
686 | * a symlink into the lower layer without the permission checks. | ||
687 | * This is only a problem if the upper layer is untrusted (e.g | ||
688 | * comes from an USB drive). This can allow a non-readable file | ||
689 | * or directory to become readable. | ||
690 | * | ||
691 | * Only following redirects when redirects are enabled disables | ||
692 | * this attack vector when not necessary. | ||
693 | */ | ||
694 | err = -EPERM; | ||
695 | if (d.redirect && !ofs->config.redirect_follow) { | ||
696 | pr_warn_ratelimited("overlay: refusing to follow redirect for (%pd2)\n", dentry); | ||
697 | goto out_put; | ||
698 | } | ||
699 | |||
684 | if (d.redirect && d.redirect[0] == '/' && poe != roe) { | 700 | if (d.redirect && d.redirect[0] == '/' && poe != roe) { |
685 | poe = roe; | 701 | poe = roe; |
686 | 702 | ||
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 13eab09a6b6f..b489099ccd49 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h | |||
@@ -180,7 +180,7 @@ static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry) | |||
180 | static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode) | 180 | static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode) |
181 | { | 181 | { |
182 | struct dentry *ret = vfs_tmpfile(dentry, mode, 0); | 182 | struct dentry *ret = vfs_tmpfile(dentry, mode, 0); |
183 | int err = IS_ERR(ret) ? PTR_ERR(ret) : 0; | 183 | int err = PTR_ERR_OR_ZERO(ret); |
184 | 184 | ||
185 | pr_debug("tmpfile(%pd2, 0%o) = %i\n", dentry, mode, err); | 185 | pr_debug("tmpfile(%pd2, 0%o) = %i\n", dentry, mode, err); |
186 | return ret; | 186 | return ret; |
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 752bab645879..9d0bc03bf6e4 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h | |||
@@ -14,6 +14,8 @@ struct ovl_config { | |||
14 | char *workdir; | 14 | char *workdir; |
15 | bool default_permissions; | 15 | bool default_permissions; |
16 | bool redirect_dir; | 16 | bool redirect_dir; |
17 | bool redirect_follow; | ||
18 | const char *redirect_mode; | ||
17 | bool index; | 19 | bool index; |
18 | }; | 20 | }; |
19 | 21 | ||
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 0daa4354fec4..8c98578d27a1 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c | |||
@@ -499,7 +499,7 @@ out: | |||
499 | return err; | 499 | return err; |
500 | 500 | ||
501 | fail: | 501 | fail: |
502 | pr_warn_ratelimited("overlay: failed to look up (%s) for ino (%i)\n", | 502 | pr_warn_ratelimited("overlayfs: failed to look up (%s) for ino (%i)\n", |
503 | p->name, err); | 503 | p->name, err); |
504 | goto out; | 504 | goto out; |
505 | } | 505 | } |
@@ -663,7 +663,10 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx) | |||
663 | return PTR_ERR(rdt.cache); | 663 | return PTR_ERR(rdt.cache); |
664 | } | 664 | } |
665 | 665 | ||
666 | return iterate_dir(od->realfile, &rdt.ctx); | 666 | err = iterate_dir(od->realfile, &rdt.ctx); |
667 | ctx->pos = rdt.ctx.pos; | ||
668 | |||
669 | return err; | ||
667 | } | 670 | } |
668 | 671 | ||
669 | 672 | ||
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 288d20f9a55a..76440feb79f6 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c | |||
@@ -33,6 +33,13 @@ module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644); | |||
33 | MODULE_PARM_DESC(ovl_redirect_dir_def, | 33 | MODULE_PARM_DESC(ovl_redirect_dir_def, |
34 | "Default to on or off for the redirect_dir feature"); | 34 | "Default to on or off for the redirect_dir feature"); |
35 | 35 | ||
36 | static bool ovl_redirect_always_follow = | ||
37 | IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW); | ||
38 | module_param_named(redirect_always_follow, ovl_redirect_always_follow, | ||
39 | bool, 0644); | ||
40 | MODULE_PARM_DESC(ovl_redirect_always_follow, | ||
41 | "Follow redirects even if redirect_dir feature is turned off"); | ||
42 | |||
36 | static bool ovl_index_def = IS_ENABLED(CONFIG_OVERLAY_FS_INDEX); | 43 | static bool ovl_index_def = IS_ENABLED(CONFIG_OVERLAY_FS_INDEX); |
37 | module_param_named(index, ovl_index_def, bool, 0644); | 44 | module_param_named(index, ovl_index_def, bool, 0644); |
38 | MODULE_PARM_DESC(ovl_index_def, | 45 | MODULE_PARM_DESC(ovl_index_def, |
@@ -232,6 +239,7 @@ static void ovl_free_fs(struct ovl_fs *ofs) | |||
232 | kfree(ofs->config.lowerdir); | 239 | kfree(ofs->config.lowerdir); |
233 | kfree(ofs->config.upperdir); | 240 | kfree(ofs->config.upperdir); |
234 | kfree(ofs->config.workdir); | 241 | kfree(ofs->config.workdir); |
242 | kfree(ofs->config.redirect_mode); | ||
235 | if (ofs->creator_cred) | 243 | if (ofs->creator_cred) |
236 | put_cred(ofs->creator_cred); | 244 | put_cred(ofs->creator_cred); |
237 | kfree(ofs); | 245 | kfree(ofs); |
@@ -244,6 +252,7 @@ static void ovl_put_super(struct super_block *sb) | |||
244 | ovl_free_fs(ofs); | 252 | ovl_free_fs(ofs); |
245 | } | 253 | } |
246 | 254 | ||
255 | /* Sync real dirty inodes in upper filesystem (if it exists) */ | ||
247 | static int ovl_sync_fs(struct super_block *sb, int wait) | 256 | static int ovl_sync_fs(struct super_block *sb, int wait) |
248 | { | 257 | { |
249 | struct ovl_fs *ofs = sb->s_fs_info; | 258 | struct ovl_fs *ofs = sb->s_fs_info; |
@@ -252,14 +261,24 @@ static int ovl_sync_fs(struct super_block *sb, int wait) | |||
252 | 261 | ||
253 | if (!ofs->upper_mnt) | 262 | if (!ofs->upper_mnt) |
254 | return 0; | 263 | return 0; |
255 | upper_sb = ofs->upper_mnt->mnt_sb; | 264 | |
256 | if (!upper_sb->s_op->sync_fs) | 265 | /* |
266 | * If this is a sync(2) call or an emergency sync, all the super blocks | ||
267 | * will be iterated, including upper_sb, so no need to do anything. | ||
268 | * | ||
269 | * If this is a syncfs(2) call, then we do need to call | ||
270 | * sync_filesystem() on upper_sb, but enough if we do it when being | ||
271 | * called with wait == 1. | ||
272 | */ | ||
273 | if (!wait) | ||
257 | return 0; | 274 | return 0; |
258 | 275 | ||
259 | /* real inodes have already been synced by sync_filesystem(ovl_sb) */ | 276 | upper_sb = ofs->upper_mnt->mnt_sb; |
277 | |||
260 | down_read(&upper_sb->s_umount); | 278 | down_read(&upper_sb->s_umount); |
261 | ret = upper_sb->s_op->sync_fs(upper_sb, wait); | 279 | ret = sync_filesystem(upper_sb); |
262 | up_read(&upper_sb->s_umount); | 280 | up_read(&upper_sb->s_umount); |
281 | |||
263 | return ret; | 282 | return ret; |
264 | } | 283 | } |
265 | 284 | ||
@@ -295,6 +314,11 @@ static bool ovl_force_readonly(struct ovl_fs *ofs) | |||
295 | return (!ofs->upper_mnt || !ofs->workdir); | 314 | return (!ofs->upper_mnt || !ofs->workdir); |
296 | } | 315 | } |
297 | 316 | ||
317 | static const char *ovl_redirect_mode_def(void) | ||
318 | { | ||
319 | return ovl_redirect_dir_def ? "on" : "off"; | ||
320 | } | ||
321 | |||
298 | /** | 322 | /** |
299 | * ovl_show_options | 323 | * ovl_show_options |
300 | * | 324 | * |
@@ -313,12 +337,10 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry) | |||
313 | } | 337 | } |
314 | if (ofs->config.default_permissions) | 338 | if (ofs->config.default_permissions) |
315 | seq_puts(m, ",default_permissions"); | 339 | seq_puts(m, ",default_permissions"); |
316 | if (ofs->config.redirect_dir != ovl_redirect_dir_def) | 340 | if (strcmp(ofs->config.redirect_mode, ovl_redirect_mode_def()) != 0) |
317 | seq_printf(m, ",redirect_dir=%s", | 341 | seq_printf(m, ",redirect_dir=%s", ofs->config.redirect_mode); |
318 | ofs->config.redirect_dir ? "on" : "off"); | ||
319 | if (ofs->config.index != ovl_index_def) | 342 | if (ofs->config.index != ovl_index_def) |
320 | seq_printf(m, ",index=%s", | 343 | seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off"); |
321 | ofs->config.index ? "on" : "off"); | ||
322 | return 0; | 344 | return 0; |
323 | } | 345 | } |
324 | 346 | ||
@@ -348,8 +370,7 @@ enum { | |||
348 | OPT_UPPERDIR, | 370 | OPT_UPPERDIR, |
349 | OPT_WORKDIR, | 371 | OPT_WORKDIR, |
350 | OPT_DEFAULT_PERMISSIONS, | 372 | OPT_DEFAULT_PERMISSIONS, |
351 | OPT_REDIRECT_DIR_ON, | 373 | OPT_REDIRECT_DIR, |
352 | OPT_REDIRECT_DIR_OFF, | ||
353 | OPT_INDEX_ON, | 374 | OPT_INDEX_ON, |
354 | OPT_INDEX_OFF, | 375 | OPT_INDEX_OFF, |
355 | OPT_ERR, | 376 | OPT_ERR, |
@@ -360,8 +381,7 @@ static const match_table_t ovl_tokens = { | |||
360 | {OPT_UPPERDIR, "upperdir=%s"}, | 381 | {OPT_UPPERDIR, "upperdir=%s"}, |
361 | {OPT_WORKDIR, "workdir=%s"}, | 382 | {OPT_WORKDIR, "workdir=%s"}, |
362 | {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, | 383 | {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, |
363 | {OPT_REDIRECT_DIR_ON, "redirect_dir=on"}, | 384 | {OPT_REDIRECT_DIR, "redirect_dir=%s"}, |
364 | {OPT_REDIRECT_DIR_OFF, "redirect_dir=off"}, | ||
365 | {OPT_INDEX_ON, "index=on"}, | 385 | {OPT_INDEX_ON, "index=on"}, |
366 | {OPT_INDEX_OFF, "index=off"}, | 386 | {OPT_INDEX_OFF, "index=off"}, |
367 | {OPT_ERR, NULL} | 387 | {OPT_ERR, NULL} |
@@ -390,10 +410,37 @@ static char *ovl_next_opt(char **s) | |||
390 | return sbegin; | 410 | return sbegin; |
391 | } | 411 | } |
392 | 412 | ||
413 | static int ovl_parse_redirect_mode(struct ovl_config *config, const char *mode) | ||
414 | { | ||
415 | if (strcmp(mode, "on") == 0) { | ||
416 | config->redirect_dir = true; | ||
417 | /* | ||
418 | * Does not make sense to have redirect creation without | ||
419 | * redirect following. | ||
420 | */ | ||
421 | config->redirect_follow = true; | ||
422 | } else if (strcmp(mode, "follow") == 0) { | ||
423 | config->redirect_follow = true; | ||
424 | } else if (strcmp(mode, "off") == 0) { | ||
425 | if (ovl_redirect_always_follow) | ||
426 | config->redirect_follow = true; | ||
427 | } else if (strcmp(mode, "nofollow") != 0) { | ||
428 | pr_err("overlayfs: bad mount option \"redirect_dir=%s\"\n", | ||
429 | mode); | ||
430 | return -EINVAL; | ||
431 | } | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
393 | static int ovl_parse_opt(char *opt, struct ovl_config *config) | 436 | static int ovl_parse_opt(char *opt, struct ovl_config *config) |
394 | { | 437 | { |
395 | char *p; | 438 | char *p; |
396 | 439 | ||
440 | config->redirect_mode = kstrdup(ovl_redirect_mode_def(), GFP_KERNEL); | ||
441 | if (!config->redirect_mode) | ||
442 | return -ENOMEM; | ||
443 | |||
397 | while ((p = ovl_next_opt(&opt)) != NULL) { | 444 | while ((p = ovl_next_opt(&opt)) != NULL) { |
398 | int token; | 445 | int token; |
399 | substring_t args[MAX_OPT_ARGS]; | 446 | substring_t args[MAX_OPT_ARGS]; |
@@ -428,12 +475,11 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) | |||
428 | config->default_permissions = true; | 475 | config->default_permissions = true; |
429 | break; | 476 | break; |
430 | 477 | ||
431 | case OPT_REDIRECT_DIR_ON: | 478 | case OPT_REDIRECT_DIR: |
432 | config->redirect_dir = true; | 479 | kfree(config->redirect_mode); |
433 | break; | 480 | config->redirect_mode = match_strdup(&args[0]); |
434 | 481 | if (!config->redirect_mode) | |
435 | case OPT_REDIRECT_DIR_OFF: | 482 | return -ENOMEM; |
436 | config->redirect_dir = false; | ||
437 | break; | 483 | break; |
438 | 484 | ||
439 | case OPT_INDEX_ON: | 485 | case OPT_INDEX_ON: |
@@ -458,7 +504,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config) | |||
458 | config->workdir = NULL; | 504 | config->workdir = NULL; |
459 | } | 505 | } |
460 | 506 | ||
461 | return 0; | 507 | return ovl_parse_redirect_mode(config, config->redirect_mode); |
462 | } | 508 | } |
463 | 509 | ||
464 | #define OVL_WORKDIR_NAME "work" | 510 | #define OVL_WORKDIR_NAME "work" |
@@ -1160,7 +1206,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
1160 | if (!cred) | 1206 | if (!cred) |
1161 | goto out_err; | 1207 | goto out_err; |
1162 | 1208 | ||
1163 | ofs->config.redirect_dir = ovl_redirect_dir_def; | ||
1164 | ofs->config.index = ovl_index_def; | 1209 | ofs->config.index = ovl_index_def; |
1165 | err = ovl_parse_opt((char *) data, &ofs->config); | 1210 | err = ovl_parse_opt((char *) data, &ofs->config); |
1166 | if (err) | 1211 | if (err) |