diff options
author | Amir Goldstein <amir73il@gmail.com> | 2018-05-11 11:15:15 +0300 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2018-05-31 12:06:12 +0300 |
commit | 01b39dcc95680b04c7af5de7f39f577e9c4865e3 (patch) | |
tree | 18675377623d5ef108a90a72a9b523cf535e4210 /fs/overlayfs/inode.c | |
parent | ac6a52eb65b5327859135269c9374bf2ff731c9f (diff) | |
download | linux-01b39dcc95680b04c7af5de7f39f577e9c4865e3.tar.xz |
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>
Diffstat (limited to 'fs/overlayfs/inode.c')
-rw-r--r-- | fs/overlayfs/inode.c | 12 |
1 files changed, 10 insertions, 2 deletions
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, return true; } +static struct inode *ovl_iget5(struct super_block *sb, struct inode *newinode, + struct inode *key) +{ + return newinode ? inode_insert5(newinode, (unsigned long) key, + ovl_inode_test, ovl_inode_set, key) : + iget5_locked(sb, (unsigned long) key, + ovl_inode_test, ovl_inode_set, key); +} + struct inode *ovl_get_inode(struct super_block *sb, struct ovl_inode_params *oip) { @@ -776,8 +785,7 @@ struct inode *ovl_get_inode(struct super_block *sb, upperdentry); unsigned int nlink = is_dir ? 1 : realinode->i_nlink; - inode = iget5_locked(sb, (unsigned long) key, ovl_inode_test, - ovl_inode_set, key); + inode = ovl_iget5(sb, oip->newinode, key); if (!inode) goto out_nomem; if (!(inode->i_state & I_NEW)) { |