Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ext4: don't set up encryption key during jbd2 transaction

Commit a80f7fcf1867 ("ext4: fixup ext4_fc_track_* functions' signature")
extended the scope of the transaction in ext4_unlink() too far, making
it include the call to ext4_find_entry(). However, ext4_find_entry()
can deadlock when called from within a transaction because it may need
to set up the directory's encryption key.

Fix this by restoring the transaction to its original scope.

Reported-by: syzbot+1a748d0007eeac3ab079@syzkaller.appspotmail.com
Fixes: a80f7fcf1867 ("ext4: fixup ext4_fc_track_* functions' signature")
Cc: <stable@vger.kernel.org> # v5.10+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Link: https://lore.kernel.org/r/20221106224841.279231-3-ebiggers@kernel.org
Signed-off-by: Theodore Ts'o <tytso@mit.edu>

authored by

Eric Biggers and committed by
Theodore Ts'o
4c0d5778 0fbcb525

+27 -23
+2 -2
fs/ext4/ext4.h
··· 3620 3620 unsigned int blocksize); 3621 3621 extern int ext4_handle_dirty_dirblock(handle_t *handle, struct inode *inode, 3622 3622 struct buffer_head *bh); 3623 - extern int __ext4_unlink(handle_t *handle, struct inode *dir, const struct qstr *d_name, 3624 - struct inode *inode); 3623 + extern int __ext4_unlink(struct inode *dir, const struct qstr *d_name, 3624 + struct inode *inode, struct dentry *dentry); 3625 3625 extern int __ext4_link(struct inode *dir, struct inode *inode, 3626 3626 struct dentry *dentry); 3627 3627
+1 -1
fs/ext4/fast_commit.c
··· 1397 1397 return 0; 1398 1398 } 1399 1399 1400 - ret = __ext4_unlink(NULL, old_parent, &entry, inode); 1400 + ret = __ext4_unlink(old_parent, &entry, inode, NULL); 1401 1401 /* -ENOENT ok coz it might not exist anymore. */ 1402 1402 if (ret == -ENOENT) 1403 1403 ret = 0;
+24 -20
fs/ext4/namei.c
··· 3204 3204 return retval; 3205 3205 } 3206 3206 3207 - int __ext4_unlink(handle_t *handle, struct inode *dir, const struct qstr *d_name, 3208 - struct inode *inode) 3207 + int __ext4_unlink(struct inode *dir, const struct qstr *d_name, 3208 + struct inode *inode, 3209 + struct dentry *dentry /* NULL during fast_commit recovery */) 3209 3210 { 3210 3211 int retval = -ENOENT; 3211 3212 struct buffer_head *bh; 3212 3213 struct ext4_dir_entry_2 *de; 3214 + handle_t *handle; 3213 3215 int skip_remove_dentry = 0; 3214 3216 3217 + /* 3218 + * Keep this outside the transaction; it may have to set up the 3219 + * directory's encryption key, which isn't GFP_NOFS-safe. 3220 + */ 3215 3221 bh = ext4_find_entry(dir, d_name, &de, NULL); 3216 3222 if (IS_ERR(bh)) 3217 3223 return PTR_ERR(bh); ··· 3234 3228 if (EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY) 3235 3229 skip_remove_dentry = 1; 3236 3230 else 3237 - goto out; 3231 + goto out_bh; 3232 + } 3233 + 3234 + handle = ext4_journal_start(dir, EXT4_HT_DIR, 3235 + EXT4_DATA_TRANS_BLOCKS(dir->i_sb)); 3236 + if (IS_ERR(handle)) { 3237 + retval = PTR_ERR(handle); 3238 + goto out_bh; 3238 3239 } 3239 3240 3240 3241 if (IS_DIRSYNC(dir)) ··· 3250 3237 if (!skip_remove_dentry) { 3251 3238 retval = ext4_delete_entry(handle, dir, de, bh); 3252 3239 if (retval) 3253 - goto out; 3240 + goto out_handle; 3254 3241 dir->i_ctime = dir->i_mtime = current_time(dir); 3255 3242 ext4_update_dx_flag(dir); 3256 3243 retval = ext4_mark_inode_dirty(handle, dir); 3257 3244 if (retval) 3258 - goto out; 3245 + goto out_handle; 3259 3246 } else { 3260 3247 retval = 0; 3261 3248 } ··· 3268 3255 ext4_orphan_add(handle, inode); 3269 3256 inode->i_ctime = current_time(inode); 3270 3257 retval = ext4_mark_inode_dirty(handle, inode); 3271 - 3272 - out: 3258 + if (dentry && !retval) 3259 + ext4_fc_track_unlink(handle, dentry); 3260 + out_handle: 3261 + ext4_journal_stop(handle); 3262 + out_bh: 3273 3263 brelse(bh); 3274 3264 return retval; 3275 3265 } 3276 3266 3277 3267 static int ext4_unlink(struct inode *dir, struct dentry *dentry) 3278 3268 { 3279 - handle_t *handle; 3280 3269 int retval; 3281 3270 3282 3271 if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb)))) ··· 3296 3281 if (retval) 3297 3282 goto out_trace; 3298 3283 3299 - handle = ext4_journal_start(dir, EXT4_HT_DIR, 3300 - EXT4_DATA_TRANS_BLOCKS(dir->i_sb)); 3301 - if (IS_ERR(handle)) { 3302 - retval = PTR_ERR(handle); 3303 - goto out_trace; 3304 - } 3305 - 3306 - retval = __ext4_unlink(handle, dir, &dentry->d_name, d_inode(dentry)); 3307 - if (!retval) 3308 - ext4_fc_track_unlink(handle, dentry); 3284 + retval = __ext4_unlink(dir, &dentry->d_name, d_inode(dentry), dentry); 3309 3285 #if IS_ENABLED(CONFIG_UNICODE) 3310 3286 /* VFS negative dentries are incompatible with Encoding and 3311 3287 * Case-insensitiveness. Eventually we'll want avoid ··· 3307 3301 if (IS_CASEFOLDED(dir)) 3308 3302 d_invalidate(dentry); 3309 3303 #endif 3310 - if (handle) 3311 - ext4_journal_stop(handle); 3312 3304 3313 3305 out_trace: 3314 3306 trace_ext4_unlink_exit(dentry, retval);