summaryrefslogtreecommitdiffstats
path: root/fs/ext4/ext4.h
diff options
context:
space:
mode:
authorEric Biggers <ebiggers@google.com>2019-03-20 14:39:13 -0400
committerTheodore Ts'o <tytso@mit.edu>2019-04-17 10:07:51 -0400
commitb01531db6cec2aa330dbc91bfbfaaef4a0d387a4 (patch)
tree215e407f512e5b48d4f3269c3ab717f3080fcf51 /fs/ext4/ext4.h
parentd456a33f041af4b54f3ce495a86d00c246165032 (diff)
fscrypt: fix race where ->lookup() marks plaintext dentry as ciphertext
->lookup() in an encrypted directory begins as follows: 1. fscrypt_prepare_lookup(): a. Try to load the directory's encryption key. b. If the key is unavailable, mark the dentry as a ciphertext name via d_flags. 2. fscrypt_setup_filename(): a. Try to load the directory's encryption key. b. If the key is available, encrypt the name (treated as a plaintext name) to get the on-disk name. Otherwise decode the name (treated as a ciphertext name) to get the on-disk name. But if the key is concurrently added, it may be found at (2a) but not at (1a). In this case, the dentry will be wrongly marked as a ciphertext name even though it was actually treated as plaintext. This will cause the dentry to be wrongly invalidated on the next lookup, potentially causing problems. For example, if the racy ->lookup() was part of sys_mount(), then the new mount will be detached when anything tries to access it. This is despite the mountpoint having a plaintext path, which should remain valid now that the key was added. Of course, this is only possible if there's a userspace race. Still, the additional kernel-side race is confusing and unexpected. Close the kernel-side race by changing fscrypt_prepare_lookup() to also set the on-disk filename (step 2b), consistent with the d_flags update. Fixes: 28b4c263961c ("ext4 crypto: revalidate dentry after adding or removing the key") Signed-off-by: Eric Biggers <ebiggers@google.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/ext4.h')
-rw-r--r--fs/ext4/ext4.h62
1 files changed, 47 insertions, 15 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 82ffdacdc7fa..e64a4ee96d30 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2287,23 +2287,47 @@ extern unsigned ext4_free_clusters_after_init(struct super_block *sb,
2287ext4_fsblk_t ext4_inode_to_goal_block(struct inode *); 2287ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
2288 2288
2289#ifdef CONFIG_FS_ENCRYPTION 2289#ifdef CONFIG_FS_ENCRYPTION
2290static inline void ext4_fname_from_fscrypt_name(struct ext4_filename *dst,
2291 const struct fscrypt_name *src)
2292{
2293 memset(dst, 0, sizeof(*dst));
2294
2295 dst->usr_fname = src->usr_fname;
2296 dst->disk_name = src->disk_name;
2297 dst->hinfo.hash = src->hash;
2298 dst->hinfo.minor_hash = src->minor_hash;
2299 dst->crypto_buf = src->crypto_buf;
2300}
2301
2290static inline int ext4_fname_setup_filename(struct inode *dir, 2302static inline int ext4_fname_setup_filename(struct inode *dir,
2291 const struct qstr *iname, 2303 const struct qstr *iname,
2292 int lookup, struct ext4_filename *fname) 2304 int lookup,
2305 struct ext4_filename *fname)
2293{ 2306{
2294 struct fscrypt_name name; 2307 struct fscrypt_name name;
2295 int err; 2308 int err;
2296 2309
2297 memset(fname, 0, sizeof(struct ext4_filename));
2298
2299 err = fscrypt_setup_filename(dir, iname, lookup, &name); 2310 err = fscrypt_setup_filename(dir, iname, lookup, &name);
2311 if (err)
2312 return err;
2300 2313
2301 fname->usr_fname = name.usr_fname; 2314 ext4_fname_from_fscrypt_name(fname, &name);
2302 fname->disk_name = name.disk_name; 2315 return 0;
2303 fname->hinfo.hash = name.hash; 2316}
2304 fname->hinfo.minor_hash = name.minor_hash; 2317
2305 fname->crypto_buf = name.crypto_buf; 2318static inline int ext4_fname_prepare_lookup(struct inode *dir,
2306 return err; 2319 struct dentry *dentry,
2320 struct ext4_filename *fname)
2321{
2322 struct fscrypt_name name;
2323 int err;
2324
2325 err = fscrypt_prepare_lookup(dir, dentry, &name);
2326 if (err)
2327 return err;
2328
2329 ext4_fname_from_fscrypt_name(fname, &name);
2330 return 0;
2307} 2331}
2308 2332
2309static inline void ext4_fname_free_filename(struct ext4_filename *fname) 2333static inline void ext4_fname_free_filename(struct ext4_filename *fname)
@@ -2317,19 +2341,27 @@ static inline void ext4_fname_free_filename(struct ext4_filename *fname)
2317 fname->usr_fname = NULL; 2341 fname->usr_fname = NULL;
2318 fname->disk_name.name = NULL; 2342 fname->disk_name.name = NULL;
2319} 2343}
2320#else 2344#else /* !CONFIG_FS_ENCRYPTION */
2321static inline int ext4_fname_setup_filename(struct inode *dir, 2345static inline int ext4_fname_setup_filename(struct inode *dir,
2322 const struct qstr *iname, 2346 const struct qstr *iname,
2323 int lookup, struct ext4_filename *fname) 2347 int lookup,
2348 struct ext4_filename *fname)
2324{ 2349{
2325 fname->usr_fname = iname; 2350 fname->usr_fname = iname;
2326 fname->disk_name.name = (unsigned char *) iname->name; 2351 fname->disk_name.name = (unsigned char *) iname->name;
2327 fname->disk_name.len = iname->len; 2352 fname->disk_name.len = iname->len;
2328 return 0; 2353 return 0;
2329} 2354}
2330static inline void ext4_fname_free_filename(struct ext4_filename *fname) { }
2331 2355
2332#endif 2356static inline int ext4_fname_prepare_lookup(struct inode *dir,
2357 struct dentry *dentry,
2358 struct ext4_filename *fname)
2359{
2360 return ext4_fname_setup_filename(dir, &dentry->d_name, 1, fname);
2361}
2362
2363static inline void ext4_fname_free_filename(struct ext4_filename *fname) { }
2364#endif /* !CONFIG_FS_ENCRYPTION */
2333 2365
2334/* dir.c */ 2366/* dir.c */
2335extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *, 2367extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,