aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2018-05-11 04:15:15 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2018-05-31 05:06:12 -0400
commit01b39dcc95680b04c7af5de7f39f577e9c4865e3 (patch)
tree18675377623d5ef108a90a72a9b523cf535e4210
parentac6a52eb65b5327859135269c9374bf2ff731c9f (diff)
ovl: use inode_insert5() to hash a newly created inode
Currently, there is a small window where ovl_obtain_alias() can race with ovl_instantiate() and create two different overlay inodes with the same underlying real non-dir non-hardlink inode. The race requires an adversary to guess the file handle of the yet to be created upper inode and decode the guessed file handle after ovl_creat_real(), but before ovl_instantiate(). This race does not affect overlay directory inodes, because those are decoded via ovl_lookup_real() and not with ovl_obtain_alias(). This patch fixes the race, by using inode_insert5() to add a newly created inode to cache. If the newly created inode apears to already exist in cache (hashed by the same real upper inode), we instantiate the dentry with the old inode and drop the new inode, instead of silently not hashing the new inode. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/overlayfs/dir.c59
-rw-r--r--fs/overlayfs/inode.c12
-rw-r--r--fs/overlayfs/overlayfs.h1
3 files changed, 60 insertions, 12 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index e8c7df070fed..f480b1a2cd2e 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -229,24 +229,54 @@ static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry)
229 return ovl_set_opaque_xerr(dentry, upperdentry, -EIO); 229 return ovl_set_opaque_xerr(dentry, upperdentry, -EIO);
230} 230}
231 231
232/* Common operations required to be done after creation of file on upper */ 232/*
233static void ovl_instantiate(struct dentry *dentry, struct inode *inode, 233 * Common operations required to be done after creation of file on upper.
234 struct dentry *newdentry, bool hardlink) 234 * If @hardlink is false, then @inode is a pre-allocated inode, we may or
235 * may not use to instantiate the new dentry.
236 */
237static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
238 struct dentry *newdentry, bool hardlink)
235{ 239{
240 struct ovl_inode_params oip = {
241 .upperdentry = newdentry,
242 .newinode = inode,
243 };
244
236 ovl_dentry_version_inc(dentry->d_parent, false); 245 ovl_dentry_version_inc(dentry->d_parent, false);
237 ovl_dentry_set_upper_alias(dentry); 246 ovl_dentry_set_upper_alias(dentry);
238 if (!hardlink) { 247 if (!hardlink) {
239 ovl_inode_update(inode, newdentry); 248 /*
240 ovl_copyattr(newdentry->d_inode, inode); 249 * ovl_obtain_alias() can be called after ovl_create_real()
250 * and before we get here, so we may get an inode from cache
251 * with the same real upperdentry that is not the inode we
252 * pre-allocated. In this case we will use the cached inode
253 * to instantiate the new dentry.
254 *
255 * XXX: if we ever use ovl_obtain_alias() to decode directory
256 * file handles, need to use ovl_get_inode_locked() and
257 * d_instantiate_new() here to prevent from creating two
258 * hashed directory inode aliases.
259 */
260 inode = ovl_get_inode(dentry->d_sb, &oip);
261 if (WARN_ON(IS_ERR(inode)))
262 return PTR_ERR(inode);
241 } else { 263 } else {
242 WARN_ON(ovl_inode_real(inode) != d_inode(newdentry)); 264 WARN_ON(ovl_inode_real(inode) != d_inode(newdentry));
243 dput(newdentry); 265 dput(newdentry);
244 inc_nlink(inode); 266 inc_nlink(inode);
245 } 267 }
268
246 d_instantiate(dentry, inode); 269 d_instantiate(dentry, inode);
270 if (inode != oip.newinode) {
271 pr_warn_ratelimited("overlayfs: newly created inode found in cache (%pd2)\n",
272 dentry);
273 }
274
247 /* Force lookup of new upper hardlink to find its lower */ 275 /* Force lookup of new upper hardlink to find its lower */
248 if (hardlink) 276 if (hardlink)
249 d_drop(dentry); 277 d_drop(dentry);
278
279 return 0;
250} 280}
251 281
252static bool ovl_type_merge(struct dentry *dentry) 282static bool ovl_type_merge(struct dentry *dentry)
@@ -285,11 +315,17 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
285 ovl_set_opaque(dentry, newdentry); 315 ovl_set_opaque(dentry, newdentry);
286 } 316 }
287 317
288 ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink); 318 err = ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink);
289 err = 0; 319 if (err)
320 goto out_cleanup;
290out_unlock: 321out_unlock:
291 inode_unlock(udir); 322 inode_unlock(udir);
292 return err; 323 return err;
324
325out_cleanup:
326 ovl_cleanup(udir, newdentry);
327 dput(newdentry);
328 goto out_unlock;
293} 329}
294 330
295static struct dentry *ovl_clear_empty(struct dentry *dentry, 331static struct dentry *ovl_clear_empty(struct dentry *dentry,
@@ -474,8 +510,9 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
474 if (err) 510 if (err)
475 goto out_cleanup; 511 goto out_cleanup;
476 } 512 }
477 ovl_instantiate(dentry, inode, newdentry, hardlink); 513 err = ovl_instantiate(dentry, inode, newdentry, hardlink);
478 err = 0; 514 if (err)
515 goto out_cleanup;
479out_dput: 516out_dput:
480 dput(upper); 517 dput(upper);
481out_unlock: 518out_unlock:
@@ -558,6 +595,7 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
558 if (err) 595 if (err)
559 goto out; 596 goto out;
560 597
598 /* Preallocate inode to be used by ovl_get_inode() */
561 err = -ENOMEM; 599 err = -ENOMEM;
562 inode = ovl_new_inode(dentry->d_sb, mode, rdev); 600 inode = ovl_new_inode(dentry->d_sb, mode, rdev);
563 if (!inode) 601 if (!inode)
@@ -567,7 +605,8 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
567 attr.mode = inode->i_mode; 605 attr.mode = inode->i_mode;
568 606
569 err = ovl_create_or_link(dentry, inode, &attr, false); 607 err = ovl_create_or_link(dentry, inode, &attr, false);
570 if (err) 608 /* Did we end up using the preallocated inode? */
609 if (inode != d_inode(dentry))
571 iput(inode); 610 iput(inode);
572 611
573out_drop_write: 612out_drop_write:
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 2b9e8370500c..1db5b3b458a1 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -749,6 +749,15 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
749 return true; 749 return true;
750} 750}
751 751
752static struct inode *ovl_iget5(struct super_block *sb, struct inode *newinode,
753 struct inode *key)
754{
755 return newinode ? inode_insert5(newinode, (unsigned long) key,
756 ovl_inode_test, ovl_inode_set, key) :
757 iget5_locked(sb, (unsigned long) key,
758 ovl_inode_test, ovl_inode_set, key);
759}
760
752struct inode *ovl_get_inode(struct super_block *sb, 761struct inode *ovl_get_inode(struct super_block *sb,
753 struct ovl_inode_params *oip) 762 struct ovl_inode_params *oip)
754{ 763{
@@ -776,8 +785,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
776 upperdentry); 785 upperdentry);
777 unsigned int nlink = is_dir ? 1 : realinode->i_nlink; 786 unsigned int nlink = is_dir ? 1 : realinode->i_nlink;
778 787
779 inode = iget5_locked(sb, (unsigned long) key, ovl_inode_test, 788 inode = ovl_iget5(sb, oip->newinode, key);
780 ovl_inode_set, key);
781 if (!inode) 789 if (!inode)
782 goto out_nomem; 790 goto out_nomem;
783 if (!(inode->i_state & I_NEW)) { 791 if (!(inode->i_state & I_NEW)) {
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index b8a0160742b2..3c5e9f18b0d9 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -329,6 +329,7 @@ int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
329bool ovl_is_private_xattr(const char *name); 329bool ovl_is_private_xattr(const char *name);
330 330
331struct ovl_inode_params { 331struct ovl_inode_params {
332 struct inode *newinode;
332 struct dentry *upperdentry; 333 struct dentry *upperdentry;
333 struct ovl_path *lowerpath; 334 struct ovl_path *lowerpath;
334 struct dentry *index; 335 struct dentry *index;