From: Greg Kroah-Hartman Date: Wed, 4 Jan 2023 15:04:27 +0000 (+0100) Subject: 6.0-stable patches X-Git-Tag: v6.1.4~26 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=520619915c30e7ba984cac55a5e5b459bba8e5cc;p=thirdparty%2Fkernel%2Fstable-queue.git 6.0-stable patches added patches: ext4-add-missing-validation-of-fast-commit-record-lengths.patch ext4-disable-fast-commit-of-encrypted-dir-operations.patch ext4-don-t-set-up-encryption-key-during-jbd2-transaction.patch ext4-fix-leaking-uninitialized-memory-in-fast-commit-journal.patch ext4-fix-off-by-one-errors-in-fast-commit-block-filling.patch ext4-fix-unaligned-memory-access-in-ext4_fc_reserve_space.patch ext4-fix-uninititialized-value-in-ext4_evict_inode.patch --- diff --git a/queue-6.0/ext4-add-missing-validation-of-fast-commit-record-lengths.patch b/queue-6.0/ext4-add-missing-validation-of-fast-commit-record-lengths.patch new file mode 100644 index 00000000000..5adec51e144 --- /dev/null +++ b/queue-6.0/ext4-add-missing-validation-of-fast-commit-record-lengths.patch @@ -0,0 +1,97 @@ +From 64b4a25c3de81a69724e888ec2db3533b43816e2 Mon Sep 17 00:00:00 2001 +From: Eric Biggers +Date: Sun, 6 Nov 2022 14:48:38 -0800 +Subject: ext4: add missing validation of fast-commit record lengths + +From: Eric Biggers + +commit 64b4a25c3de81a69724e888ec2db3533b43816e2 upstream. + +Validate the inode and filename lengths in fast-commit journal records +so that a malicious fast-commit journal cannot cause a crash by having +invalid values for these. Also validate EXT4_FC_TAG_DEL_RANGE. + +Fixes: aa75f4d3daae ("ext4: main fast-commit commit path") +Cc: # v5.10+ +Signed-off-by: Eric Biggers +Link: https://lore.kernel.org/r/20221106224841.279231-5-ebiggers@kernel.org +Signed-off-by: Theodore Ts'o +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/fast_commit.c | 38 +++++++++++++++++++------------------- + fs/ext4/fast_commit.h | 2 +- + 2 files changed, 20 insertions(+), 20 deletions(-) + +--- a/fs/ext4/fast_commit.c ++++ b/fs/ext4/fast_commit.c +@@ -2000,32 +2000,31 @@ void ext4_fc_replay_cleanup(struct super + kfree(sbi->s_fc_replay_state.fc_modified_inodes); + } + +-static inline bool ext4_fc_tag_len_isvalid(struct ext4_fc_tl *tl, +- u8 *val, u8 *end) ++static bool ext4_fc_value_len_isvalid(struct ext4_sb_info *sbi, ++ int tag, int len) + { +- if (val + tl->fc_len > end) +- return false; +- +- /* Here only check ADD_RANGE/TAIL/HEAD which will read data when do +- * journal rescan before do CRC check. Other tags length check will +- * rely on CRC check. +- */ +- switch (tl->fc_tag) { ++ switch (tag) { + case EXT4_FC_TAG_ADD_RANGE: +- return (sizeof(struct ext4_fc_add_range) == tl->fc_len); +- case EXT4_FC_TAG_TAIL: +- return (sizeof(struct ext4_fc_tail) <= tl->fc_len); +- case EXT4_FC_TAG_HEAD: +- return (sizeof(struct ext4_fc_head) == tl->fc_len); ++ return len == sizeof(struct ext4_fc_add_range); + case EXT4_FC_TAG_DEL_RANGE: ++ return len == sizeof(struct ext4_fc_del_range); ++ case EXT4_FC_TAG_CREAT: + case EXT4_FC_TAG_LINK: + case EXT4_FC_TAG_UNLINK: +- case EXT4_FC_TAG_CREAT: ++ len -= sizeof(struct ext4_fc_dentry_info); ++ return len >= 1 && len <= EXT4_NAME_LEN; + case EXT4_FC_TAG_INODE: ++ len -= sizeof(struct ext4_fc_inode); ++ return len >= EXT4_GOOD_OLD_INODE_SIZE && ++ len <= sbi->s_inode_size; + case EXT4_FC_TAG_PAD: +- default: +- return true; ++ return true; /* padding can have any length */ ++ case EXT4_FC_TAG_TAIL: ++ return len >= sizeof(struct ext4_fc_tail); ++ case EXT4_FC_TAG_HEAD: ++ return len == sizeof(struct ext4_fc_head); + } ++ return false; + } + + /* +@@ -2088,7 +2087,8 @@ static int ext4_fc_replay_scan(journal_t + cur = cur + EXT4_FC_TAG_BASE_LEN + tl.fc_len) { + ext4_fc_get_tl(&tl, cur); + val = cur + EXT4_FC_TAG_BASE_LEN; +- if (!ext4_fc_tag_len_isvalid(&tl, val, end)) { ++ if (tl.fc_len > end - val || ++ !ext4_fc_value_len_isvalid(sbi, tl.fc_tag, tl.fc_len)) { + ret = state->fc_replay_num_tags ? + JBD2_FC_REPLAY_STOP : -ECANCELED; + goto out_err; +--- a/fs/ext4/fast_commit.h ++++ b/fs/ext4/fast_commit.h +@@ -58,7 +58,7 @@ struct ext4_fc_dentry_info { + __u8 fc_dname[]; + }; + +-/* Value structure for EXT4_FC_TAG_INODE and EXT4_FC_TAG_INODE_PARTIAL. */ ++/* Value structure for EXT4_FC_TAG_INODE. */ + struct ext4_fc_inode { + __le32 fc_ino; + __u8 fc_raw_inode[]; diff --git a/queue-6.0/ext4-disable-fast-commit-of-encrypted-dir-operations.patch b/queue-6.0/ext4-disable-fast-commit-of-encrypted-dir-operations.patch new file mode 100644 index 00000000000..73555dbf010 --- /dev/null +++ b/queue-6.0/ext4-disable-fast-commit-of-encrypted-dir-operations.patch @@ -0,0 +1,151 @@ +From 0fbcb5251fc81b58969b272c4fb7374a7b922e3e Mon Sep 17 00:00:00 2001 +From: Eric Biggers +Date: Sun, 6 Nov 2022 14:48:35 -0800 +Subject: ext4: disable fast-commit of encrypted dir operations + +From: Eric Biggers + +commit 0fbcb5251fc81b58969b272c4fb7374a7b922e3e upstream. + +fast-commit of create, link, and unlink operations in encrypted +directories is completely broken because the unencrypted filenames are +being written to the fast-commit journal instead of the encrypted +filenames. These operations can't be replayed, as encryption keys +aren't present at journal replay time. It is also an information leak. + +Until if/when we can get this working properly, make encrypted directory +operations ineligible for fast-commit. + +Note that fast-commit operations on encrypted regular files continue to +be allowed, as they seem to work. + +Fixes: aa75f4d3daae ("ext4: main fast-commit commit path") +Cc: # v5.10+ +Signed-off-by: Eric Biggers +Link: https://lore.kernel.org/r/20221106224841.279231-2-ebiggers@kernel.org +Signed-off-by: Theodore Ts'o +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/fast_commit.c | 41 +++++++++++++++++++++++++---------------- + fs/ext4/fast_commit.h | 1 + + include/trace/events/ext4.h | 7 +++++-- + 3 files changed, 31 insertions(+), 18 deletions(-) + +--- a/fs/ext4/fast_commit.c ++++ b/fs/ext4/fast_commit.c +@@ -418,25 +418,34 @@ static int __track_dentry_update(struct + struct __track_dentry_update_args *dentry_update = + (struct __track_dentry_update_args *)arg; + struct dentry *dentry = dentry_update->dentry; +- struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); ++ struct inode *dir = dentry->d_parent->d_inode; ++ struct super_block *sb = inode->i_sb; ++ struct ext4_sb_info *sbi = EXT4_SB(sb); + + mutex_unlock(&ei->i_fc_lock); ++ ++ if (IS_ENCRYPTED(dir)) { ++ ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_ENCRYPTED_FILENAME, ++ NULL); ++ mutex_lock(&ei->i_fc_lock); ++ return -EOPNOTSUPP; ++ } ++ + node = kmem_cache_alloc(ext4_fc_dentry_cachep, GFP_NOFS); + if (!node) { +- ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_NOMEM, NULL); ++ ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, NULL); + mutex_lock(&ei->i_fc_lock); + return -ENOMEM; + } + + node->fcd_op = dentry_update->op; +- node->fcd_parent = dentry->d_parent->d_inode->i_ino; ++ node->fcd_parent = dir->i_ino; + node->fcd_ino = inode->i_ino; + if (dentry->d_name.len > DNAME_INLINE_LEN) { + node->fcd_name.name = kmalloc(dentry->d_name.len, GFP_NOFS); + if (!node->fcd_name.name) { + kmem_cache_free(ext4_fc_dentry_cachep, node); +- ext4_fc_mark_ineligible(inode->i_sb, +- EXT4_FC_REASON_NOMEM, NULL); ++ ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, NULL); + mutex_lock(&ei->i_fc_lock); + return -ENOMEM; + } +@@ -2258,17 +2267,17 @@ void ext4_fc_init(struct super_block *sb + journal->j_fc_cleanup_callback = ext4_fc_cleanup; + } + +-static const char *fc_ineligible_reasons[] = { +- "Extended attributes changed", +- "Cross rename", +- "Journal flag changed", +- "Insufficient memory", +- "Swap boot", +- "Resize", +- "Dir renamed", +- "Falloc range op", +- "Data journalling", +- "FC Commit Failed" ++static const char * const fc_ineligible_reasons[] = { ++ [EXT4_FC_REASON_XATTR] = "Extended attributes changed", ++ [EXT4_FC_REASON_CROSS_RENAME] = "Cross rename", ++ [EXT4_FC_REASON_JOURNAL_FLAG_CHANGE] = "Journal flag changed", ++ [EXT4_FC_REASON_NOMEM] = "Insufficient memory", ++ [EXT4_FC_REASON_SWAP_BOOT] = "Swap boot", ++ [EXT4_FC_REASON_RESIZE] = "Resize", ++ [EXT4_FC_REASON_RENAME_DIR] = "Dir renamed", ++ [EXT4_FC_REASON_FALLOC_RANGE] = "Falloc range op", ++ [EXT4_FC_REASON_INODE_JOURNAL_DATA] = "Data journalling", ++ [EXT4_FC_REASON_ENCRYPTED_FILENAME] = "Encrypted filename", + }; + + int ext4_fc_info_show(struct seq_file *seq, void *v) +--- a/fs/ext4/fast_commit.h ++++ b/fs/ext4/fast_commit.h +@@ -96,6 +96,7 @@ enum { + EXT4_FC_REASON_RENAME_DIR, + EXT4_FC_REASON_FALLOC_RANGE, + EXT4_FC_REASON_INODE_JOURNAL_DATA, ++ EXT4_FC_REASON_ENCRYPTED_FILENAME, + EXT4_FC_REASON_MAX + }; + +--- a/include/trace/events/ext4.h ++++ b/include/trace/events/ext4.h +@@ -104,6 +104,7 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_RESIZE) + TRACE_DEFINE_ENUM(EXT4_FC_REASON_RENAME_DIR); + TRACE_DEFINE_ENUM(EXT4_FC_REASON_FALLOC_RANGE); + TRACE_DEFINE_ENUM(EXT4_FC_REASON_INODE_JOURNAL_DATA); ++TRACE_DEFINE_ENUM(EXT4_FC_REASON_ENCRYPTED_FILENAME); + TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX); + + #define show_fc_reason(reason) \ +@@ -116,7 +117,8 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX); + { EXT4_FC_REASON_RESIZE, "RESIZE"}, \ + { EXT4_FC_REASON_RENAME_DIR, "RENAME_DIR"}, \ + { EXT4_FC_REASON_FALLOC_RANGE, "FALLOC_RANGE"}, \ +- { EXT4_FC_REASON_INODE_JOURNAL_DATA, "INODE_JOURNAL_DATA"}) ++ { EXT4_FC_REASON_INODE_JOURNAL_DATA, "INODE_JOURNAL_DATA"}, \ ++ { EXT4_FC_REASON_ENCRYPTED_FILENAME, "ENCRYPTED_FILENAME"}) + + TRACE_EVENT(ext4_other_inode_update_time, + TP_PROTO(struct inode *inode, ino_t orig_ino), +@@ -2764,7 +2766,7 @@ TRACE_EVENT(ext4_fc_stats, + ), + + TP_printk("dev %d,%d fc ineligible reasons:\n" +- "%s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u " ++ "%s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u" + "num_commits:%lu, ineligible: %lu, numblks: %lu", + MAJOR(__entry->dev), MINOR(__entry->dev), + FC_REASON_NAME_STAT(EXT4_FC_REASON_XATTR), +@@ -2776,6 +2778,7 @@ TRACE_EVENT(ext4_fc_stats, + FC_REASON_NAME_STAT(EXT4_FC_REASON_RENAME_DIR), + FC_REASON_NAME_STAT(EXT4_FC_REASON_FALLOC_RANGE), + FC_REASON_NAME_STAT(EXT4_FC_REASON_INODE_JOURNAL_DATA), ++ FC_REASON_NAME_STAT(EXT4_FC_REASON_ENCRYPTED_FILENAME), + __entry->fc_commits, __entry->fc_ineligible_commits, + __entry->fc_numblks) + ); diff --git a/queue-6.0/ext4-don-t-set-up-encryption-key-during-jbd2-transaction.patch b/queue-6.0/ext4-don-t-set-up-encryption-key-during-jbd2-transaction.patch new file mode 100644 index 00000000000..1db9a51478b --- /dev/null +++ b/queue-6.0/ext4-don-t-set-up-encryption-key-during-jbd2-transaction.patch @@ -0,0 +1,158 @@ +From 4c0d5778385cb3618ff26a561ce41de2b7d9de70 Mon Sep 17 00:00:00 2001 +From: Eric Biggers +Date: Sun, 6 Nov 2022 14:48:36 -0800 +Subject: ext4: don't set up encryption key during jbd2 transaction + +From: Eric Biggers + +commit 4c0d5778385cb3618ff26a561ce41de2b7d9de70 upstream. + +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: # v5.10+ +Signed-off-by: Eric Biggers +Link: https://lore.kernel.org/r/20221106224841.279231-3-ebiggers@kernel.org +Signed-off-by: Theodore Ts'o +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/ext4.h | 4 ++-- + fs/ext4/fast_commit.c | 2 +- + fs/ext4/namei.c | 44 ++++++++++++++++++++++++-------------------- + 3 files changed, 27 insertions(+), 23 deletions(-) + +--- a/fs/ext4/ext4.h ++++ b/fs/ext4/ext4.h +@@ -3622,8 +3622,8 @@ extern void ext4_initialize_dirent_tail( + unsigned int blocksize); + extern int ext4_handle_dirty_dirblock(handle_t *handle, struct inode *inode, + struct buffer_head *bh); +-extern int __ext4_unlink(handle_t *handle, struct inode *dir, const struct qstr *d_name, +- struct inode *inode); ++extern int __ext4_unlink(struct inode *dir, const struct qstr *d_name, ++ struct inode *inode, struct dentry *dentry); + extern int __ext4_link(struct inode *dir, struct inode *inode, + struct dentry *dentry); + +--- a/fs/ext4/fast_commit.c ++++ b/fs/ext4/fast_commit.c +@@ -1410,7 +1410,7 @@ static int ext4_fc_replay_unlink(struct + return 0; + } + +- ret = __ext4_unlink(NULL, old_parent, &entry, inode); ++ ret = __ext4_unlink(old_parent, &entry, inode, NULL); + /* -ENOENT ok coz it might not exist anymore. */ + if (ret == -ENOENT) + ret = 0; +--- a/fs/ext4/namei.c ++++ b/fs/ext4/namei.c +@@ -3204,14 +3204,20 @@ end_rmdir: + return retval; + } + +-int __ext4_unlink(handle_t *handle, struct inode *dir, const struct qstr *d_name, +- struct inode *inode) ++int __ext4_unlink(struct inode *dir, const struct qstr *d_name, ++ struct inode *inode, ++ struct dentry *dentry /* NULL during fast_commit recovery */) + { + int retval = -ENOENT; + struct buffer_head *bh; + struct ext4_dir_entry_2 *de; ++ handle_t *handle; + int skip_remove_dentry = 0; + ++ /* ++ * Keep this outside the transaction; it may have to set up the ++ * directory's encryption key, which isn't GFP_NOFS-safe. ++ */ + bh = ext4_find_entry(dir, d_name, &de, NULL); + if (IS_ERR(bh)) + return PTR_ERR(bh); +@@ -3228,7 +3234,14 @@ int __ext4_unlink(handle_t *handle, stru + if (EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY) + skip_remove_dentry = 1; + else +- goto out; ++ goto out_bh; ++ } ++ ++ handle = ext4_journal_start(dir, EXT4_HT_DIR, ++ EXT4_DATA_TRANS_BLOCKS(dir->i_sb)); ++ if (IS_ERR(handle)) { ++ retval = PTR_ERR(handle); ++ goto out_bh; + } + + if (IS_DIRSYNC(dir)) +@@ -3237,12 +3250,12 @@ int __ext4_unlink(handle_t *handle, stru + if (!skip_remove_dentry) { + retval = ext4_delete_entry(handle, dir, de, bh); + if (retval) +- goto out; ++ goto out_handle; + dir->i_ctime = dir->i_mtime = current_time(dir); + ext4_update_dx_flag(dir); + retval = ext4_mark_inode_dirty(handle, dir); + if (retval) +- goto out; ++ goto out_handle; + } else { + retval = 0; + } +@@ -3255,15 +3268,17 @@ int __ext4_unlink(handle_t *handle, stru + ext4_orphan_add(handle, inode); + inode->i_ctime = current_time(inode); + retval = ext4_mark_inode_dirty(handle, inode); +- +-out: ++ if (dentry && !retval) ++ ext4_fc_track_unlink(handle, dentry); ++out_handle: ++ ext4_journal_stop(handle); ++out_bh: + brelse(bh); + return retval; + } + + static int ext4_unlink(struct inode *dir, struct dentry *dentry) + { +- handle_t *handle; + int retval; + + if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb)))) +@@ -3281,16 +3296,7 @@ static int ext4_unlink(struct inode *dir + if (retval) + goto out_trace; + +- handle = ext4_journal_start(dir, EXT4_HT_DIR, +- EXT4_DATA_TRANS_BLOCKS(dir->i_sb)); +- if (IS_ERR(handle)) { +- retval = PTR_ERR(handle); +- goto out_trace; +- } +- +- retval = __ext4_unlink(handle, dir, &dentry->d_name, d_inode(dentry)); +- if (!retval) +- ext4_fc_track_unlink(handle, dentry); ++ retval = __ext4_unlink(dir, &dentry->d_name, d_inode(dentry), dentry); + #if IS_ENABLED(CONFIG_UNICODE) + /* VFS negative dentries are incompatible with Encoding and + * Case-insensitiveness. Eventually we'll want avoid +@@ -3301,8 +3307,6 @@ static int ext4_unlink(struct inode *dir + if (IS_CASEFOLDED(dir)) + d_invalidate(dentry); + #endif +- if (handle) +- ext4_journal_stop(handle); + + out_trace: + trace_ext4_unlink_exit(dentry, retval); diff --git a/queue-6.0/ext4-fix-leaking-uninitialized-memory-in-fast-commit-journal.patch b/queue-6.0/ext4-fix-leaking-uninitialized-memory-in-fast-commit-journal.patch new file mode 100644 index 00000000000..777fe4ab55d --- /dev/null +++ b/queue-6.0/ext4-fix-leaking-uninitialized-memory-in-fast-commit-journal.patch @@ -0,0 +1,43 @@ +From 594bc43b410316d70bb42aeff168837888d96810 Mon Sep 17 00:00:00 2001 +From: Eric Biggers +Date: Sun, 6 Nov 2022 14:48:37 -0800 +Subject: ext4: fix leaking uninitialized memory in fast-commit journal + +From: Eric Biggers + +commit 594bc43b410316d70bb42aeff168837888d96810 upstream. + +When space at the end of fast-commit journal blocks is unused, make sure +to zero it out so that uninitialized memory is not leaked to disk. + +Fixes: aa75f4d3daae ("ext4: main fast-commit commit path") +Cc: # v5.10+ +Signed-off-by: Eric Biggers +Link: https://lore.kernel.org/r/20221106224841.279231-4-ebiggers@kernel.org +Signed-off-by: Theodore Ts'o +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/fast_commit.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/fs/ext4/fast_commit.c ++++ b/fs/ext4/fast_commit.c +@@ -745,6 +745,9 @@ static u8 *ext4_fc_reserve_space(struct + *crc = ext4_chksum(sbi, *crc, tl, EXT4_FC_TAG_BASE_LEN); + if (pad_len > 0) + ext4_fc_memzero(sb, tl + 1, pad_len, crc); ++ /* Don't leak uninitialized memory in the unused last byte. */ ++ *((u8 *)(tl + 1) + pad_len) = 0; ++ + ext4_fc_submit_bh(sb, false); + + ret = jbd2_fc_get_buf(EXT4_SB(sb)->s_journal, &bh); +@@ -801,6 +804,8 @@ static int ext4_fc_write_tail(struct sup + dst += sizeof(tail.fc_tid); + tail.fc_crc = cpu_to_le32(crc); + ext4_fc_memcpy(sb, dst, &tail.fc_crc, sizeof(tail.fc_crc), NULL); ++ dst += sizeof(tail.fc_crc); ++ memset(dst, 0, bsize - off); /* Don't leak uninitialized memory. */ + + ext4_fc_submit_bh(sb, true); + diff --git a/queue-6.0/ext4-fix-off-by-one-errors-in-fast-commit-block-filling.patch b/queue-6.0/ext4-fix-off-by-one-errors-in-fast-commit-block-filling.patch new file mode 100644 index 00000000000..11d1718f9af --- /dev/null +++ b/queue-6.0/ext4-fix-off-by-one-errors-in-fast-commit-block-filling.patch @@ -0,0 +1,166 @@ +From 48a6a66db82b8043d298a630f22c62d43550cae5 Mon Sep 17 00:00:00 2001 +From: Eric Biggers +Date: Sun, 6 Nov 2022 14:48:40 -0800 +Subject: ext4: fix off-by-one errors in fast-commit block filling + +From: Eric Biggers + +commit 48a6a66db82b8043d298a630f22c62d43550cae5 upstream. + +Due to several different off-by-one errors, or perhaps due to a late +change in design that wasn't fully reflected in the code that was +actually merged, there are several very strange constraints on how +fast-commit blocks are filled with tlv entries: + +- tlvs must start at least 10 bytes before the end of the block, even + though the minimum tlv length is 8. Otherwise, the replay code will + ignore them. (BUG: ext4_fc_reserve_space() could violate this + requirement if called with a len of blocksize - 9 or blocksize - 8. + Fortunately, this doesn't seem to happen currently.) + +- tlvs must end at least 1 byte before the end of the block. Otherwise + the replay code will consider them to be invalid. This quirk + contributed to a bug (fixed by an earlier commit) where uninitialized + memory was being leaked to disk in the last byte of blocks. + +Also, strangely these constraints don't apply to the replay code in +e2fsprogs, which will accept any tlvs in the blocks (with no bounds +checks at all, but that is a separate issue...). + +Given that this all seems to be a bug, let's fix it by just filling +blocks with tlv entries in the natural way. + +Note that old kernels will be unable to replay fast-commit journals +created by kernels that have this commit. + +Fixes: aa75f4d3daae ("ext4: main fast-commit commit path") +Cc: # v5.10+ +Signed-off-by: Eric Biggers +Link: https://lore.kernel.org/r/20221106224841.279231-7-ebiggers@kernel.org +Signed-off-by: Theodore Ts'o +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/fast_commit.c | 66 +++++++++++++++++++++++++------------------------- + 1 file changed, 33 insertions(+), 33 deletions(-) + +--- a/fs/ext4/fast_commit.c ++++ b/fs/ext4/fast_commit.c +@@ -722,43 +722,43 @@ static u8 *ext4_fc_reserve_space(struct + struct buffer_head *bh; + int bsize = sbi->s_journal->j_blocksize; + int ret, off = sbi->s_fc_bytes % bsize; +- int pad_len; ++ int remaining; + u8 *dst; + + /* +- * After allocating len, we should have space at least for a 0 byte +- * padding. ++ * If 'len' is too long to fit in any block alongside a PAD tlv, then we ++ * cannot fulfill the request. + */ +- if (len + EXT4_FC_TAG_BASE_LEN > bsize) ++ if (len > bsize - EXT4_FC_TAG_BASE_LEN) + return NULL; + +- if (bsize - off - 1 > len + EXT4_FC_TAG_BASE_LEN) { +- /* +- * Only allocate from current buffer if we have enough space for +- * this request AND we have space to add a zero byte padding. +- */ +- if (!sbi->s_fc_bh) { +- ret = jbd2_fc_get_buf(EXT4_SB(sb)->s_journal, &bh); +- if (ret) +- return NULL; +- sbi->s_fc_bh = bh; +- } +- sbi->s_fc_bytes += len; +- return sbi->s_fc_bh->b_data + off; ++ if (!sbi->s_fc_bh) { ++ ret = jbd2_fc_get_buf(EXT4_SB(sb)->s_journal, &bh); ++ if (ret) ++ return NULL; ++ sbi->s_fc_bh = bh; + } +- /* Need to add PAD tag */ + dst = sbi->s_fc_bh->b_data + off; ++ ++ /* ++ * Allocate the bytes in the current block if we can do so while still ++ * leaving enough space for a PAD tlv. ++ */ ++ remaining = bsize - EXT4_FC_TAG_BASE_LEN - off; ++ if (len <= remaining) { ++ sbi->s_fc_bytes += len; ++ return dst; ++ } ++ ++ /* ++ * Else, terminate the current block with a PAD tlv, then allocate a new ++ * block and allocate the bytes at the start of that new block. ++ */ ++ + tl.fc_tag = cpu_to_le16(EXT4_FC_TAG_PAD); +- pad_len = bsize - off - 1 - EXT4_FC_TAG_BASE_LEN; +- tl.fc_len = cpu_to_le16(pad_len); ++ tl.fc_len = cpu_to_le16(remaining); + ext4_fc_memcpy(sb, dst, &tl, EXT4_FC_TAG_BASE_LEN, crc); +- dst += EXT4_FC_TAG_BASE_LEN; +- if (pad_len > 0) { +- ext4_fc_memzero(sb, dst, pad_len, crc); +- dst += pad_len; +- } +- /* Don't leak uninitialized memory in the unused last byte. */ +- *dst = 0; ++ ext4_fc_memzero(sb, dst + EXT4_FC_TAG_BASE_LEN, remaining, crc); + + ext4_fc_submit_bh(sb, false); + +@@ -766,7 +766,7 @@ static u8 *ext4_fc_reserve_space(struct + if (ret) + return NULL; + sbi->s_fc_bh = bh; +- sbi->s_fc_bytes = (sbi->s_fc_bytes / bsize + 1) * bsize + len; ++ sbi->s_fc_bytes += bsize - off + len; + return sbi->s_fc_bh->b_data; + } + +@@ -797,7 +797,7 @@ static int ext4_fc_write_tail(struct sup + off = sbi->s_fc_bytes % bsize; + + tl.fc_tag = cpu_to_le16(EXT4_FC_TAG_TAIL); +- tl.fc_len = cpu_to_le16(bsize - off - 1 + sizeof(struct ext4_fc_tail)); ++ tl.fc_len = cpu_to_le16(bsize - off + sizeof(struct ext4_fc_tail)); + sbi->s_fc_bytes = round_up(sbi->s_fc_bytes, bsize); + + ext4_fc_memcpy(sb, dst, &tl, EXT4_FC_TAG_BASE_LEN, &crc); +@@ -2065,7 +2065,7 @@ static int ext4_fc_replay_scan(journal_t + state = &sbi->s_fc_replay_state; + + start = (u8 *)bh->b_data; +- end = (__u8 *)bh->b_data + journal->j_blocksize - 1; ++ end = start + journal->j_blocksize; + + if (state->fc_replay_expected_off == 0) { + state->fc_cur_tag = 0; +@@ -2086,7 +2086,7 @@ static int ext4_fc_replay_scan(journal_t + } + + state->fc_replay_expected_off++; +- for (cur = start; cur < end - EXT4_FC_TAG_BASE_LEN; ++ for (cur = start; cur <= end - EXT4_FC_TAG_BASE_LEN; + cur = cur + EXT4_FC_TAG_BASE_LEN + tl.fc_len) { + ext4_fc_get_tl(&tl, cur); + val = cur + EXT4_FC_TAG_BASE_LEN; +@@ -2204,9 +2204,9 @@ static int ext4_fc_replay(journal_t *jou + #endif + + start = (u8 *)bh->b_data; +- end = (__u8 *)bh->b_data + journal->j_blocksize - 1; ++ end = start + journal->j_blocksize; + +- for (cur = start; cur < end - EXT4_FC_TAG_BASE_LEN; ++ for (cur = start; cur <= end - EXT4_FC_TAG_BASE_LEN; + cur = cur + EXT4_FC_TAG_BASE_LEN + tl.fc_len) { + ext4_fc_get_tl(&tl, cur); + val = cur + EXT4_FC_TAG_BASE_LEN; diff --git a/queue-6.0/ext4-fix-unaligned-memory-access-in-ext4_fc_reserve_space.patch b/queue-6.0/ext4-fix-unaligned-memory-access-in-ext4_fc_reserve_space.patch new file mode 100644 index 00000000000..98d2b021daf --- /dev/null +++ b/queue-6.0/ext4-fix-unaligned-memory-access-in-ext4_fc_reserve_space.patch @@ -0,0 +1,99 @@ +From 8415ce07ecf0cc25efdd5db264a7133716e503cf Mon Sep 17 00:00:00 2001 +From: Eric Biggers +Date: Sun, 6 Nov 2022 14:48:39 -0800 +Subject: ext4: fix unaligned memory access in ext4_fc_reserve_space() + +From: Eric Biggers + +commit 8415ce07ecf0cc25efdd5db264a7133716e503cf upstream. + +As is done elsewhere in the file, build the struct ext4_fc_tl on the +stack and memcpy() it into the buffer, rather than directly writing it +to a potentially-unaligned location in the buffer. + +Fixes: aa75f4d3daae ("ext4: main fast-commit commit path") +Cc: # v5.10+ +Signed-off-by: Eric Biggers +Link: https://lore.kernel.org/r/20221106224841.279231-6-ebiggers@kernel.org +Signed-off-by: Theodore Ts'o +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/fast_commit.c | 39 +++++++++++++++++++++------------------ + 1 file changed, 21 insertions(+), 18 deletions(-) + +--- a/fs/ext4/fast_commit.c ++++ b/fs/ext4/fast_commit.c +@@ -683,6 +683,15 @@ static void ext4_fc_submit_bh(struct sup + + /* Ext4 commit path routines */ + ++/* memcpy to fc reserved space and update CRC */ ++static void *ext4_fc_memcpy(struct super_block *sb, void *dst, const void *src, ++ int len, u32 *crc) ++{ ++ if (crc) ++ *crc = ext4_chksum(EXT4_SB(sb), *crc, src, len); ++ return memcpy(dst, src, len); ++} ++ + /* memzero and update CRC */ + static void *ext4_fc_memzero(struct super_block *sb, void *dst, int len, + u32 *crc) +@@ -708,12 +717,13 @@ static void *ext4_fc_memzero(struct supe + */ + static u8 *ext4_fc_reserve_space(struct super_block *sb, int len, u32 *crc) + { +- struct ext4_fc_tl *tl; ++ struct ext4_fc_tl tl; + struct ext4_sb_info *sbi = EXT4_SB(sb); + struct buffer_head *bh; + int bsize = sbi->s_journal->j_blocksize; + int ret, off = sbi->s_fc_bytes % bsize; + int pad_len; ++ u8 *dst; + + /* + * After allocating len, we should have space at least for a 0 byte +@@ -737,16 +747,18 @@ static u8 *ext4_fc_reserve_space(struct + return sbi->s_fc_bh->b_data + off; + } + /* Need to add PAD tag */ +- tl = (struct ext4_fc_tl *)(sbi->s_fc_bh->b_data + off); +- tl->fc_tag = cpu_to_le16(EXT4_FC_TAG_PAD); ++ dst = sbi->s_fc_bh->b_data + off; ++ tl.fc_tag = cpu_to_le16(EXT4_FC_TAG_PAD); + pad_len = bsize - off - 1 - EXT4_FC_TAG_BASE_LEN; +- tl->fc_len = cpu_to_le16(pad_len); +- if (crc) +- *crc = ext4_chksum(sbi, *crc, tl, EXT4_FC_TAG_BASE_LEN); +- if (pad_len > 0) +- ext4_fc_memzero(sb, tl + 1, pad_len, crc); ++ tl.fc_len = cpu_to_le16(pad_len); ++ ext4_fc_memcpy(sb, dst, &tl, EXT4_FC_TAG_BASE_LEN, crc); ++ dst += EXT4_FC_TAG_BASE_LEN; ++ if (pad_len > 0) { ++ ext4_fc_memzero(sb, dst, pad_len, crc); ++ dst += pad_len; ++ } + /* Don't leak uninitialized memory in the unused last byte. */ +- *((u8 *)(tl + 1) + pad_len) = 0; ++ *dst = 0; + + ext4_fc_submit_bh(sb, false); + +@@ -758,15 +770,6 @@ static u8 *ext4_fc_reserve_space(struct + return sbi->s_fc_bh->b_data; + } + +-/* memcpy to fc reserved space and update CRC */ +-static void *ext4_fc_memcpy(struct super_block *sb, void *dst, const void *src, +- int len, u32 *crc) +-{ +- if (crc) +- *crc = ext4_chksum(EXT4_SB(sb), *crc, src, len); +- return memcpy(dst, src, len); +-} +- + /* + * Complete a fast commit by writing tail tag. + * diff --git a/queue-6.0/ext4-fix-uninititialized-value-in-ext4_evict_inode.patch b/queue-6.0/ext4-fix-uninititialized-value-in-ext4_evict_inode.patch new file mode 100644 index 00000000000..aa5b0426ea6 --- /dev/null +++ b/queue-6.0/ext4-fix-uninititialized-value-in-ext4_evict_inode.patch @@ -0,0 +1,92 @@ +From 7ea71af94eaaaf6d9aed24bc94a05b977a741cb9 Mon Sep 17 00:00:00 2001 +From: Ye Bin +Date: Thu, 17 Nov 2022 15:36:03 +0800 +Subject: ext4: fix uninititialized value in 'ext4_evict_inode' + +From: Ye Bin + +commit 7ea71af94eaaaf6d9aed24bc94a05b977a741cb9 upstream. + +Syzbot found the following issue: +===================================================== +BUG: KMSAN: uninit-value in ext4_evict_inode+0xdd/0x26b0 fs/ext4/inode.c:180 + ext4_evict_inode+0xdd/0x26b0 fs/ext4/inode.c:180 + evict+0x365/0x9a0 fs/inode.c:664 + iput_final fs/inode.c:1747 [inline] + iput+0x985/0xdd0 fs/inode.c:1773 + __ext4_new_inode+0xe54/0x7ec0 fs/ext4/ialloc.c:1361 + ext4_mknod+0x376/0x840 fs/ext4/namei.c:2844 + vfs_mknod+0x79d/0x830 fs/namei.c:3914 + do_mknodat+0x47d/0xaa0 + __do_sys_mknodat fs/namei.c:3992 [inline] + __se_sys_mknodat fs/namei.c:3989 [inline] + __ia32_sys_mknodat+0xeb/0x150 fs/namei.c:3989 + do_syscall_32_irqs_on arch/x86/entry/common.c:112 [inline] + __do_fast_syscall_32+0xa2/0x100 arch/x86/entry/common.c:178 + do_fast_syscall_32+0x33/0x70 arch/x86/entry/common.c:203 + do_SYSENTER_32+0x1b/0x20 arch/x86/entry/common.c:246 + entry_SYSENTER_compat_after_hwframe+0x70/0x82 + +Uninit was created at: + __alloc_pages+0x9f1/0xe80 mm/page_alloc.c:5578 + alloc_pages+0xaae/0xd80 mm/mempolicy.c:2285 + alloc_slab_page mm/slub.c:1794 [inline] + allocate_slab+0x1b5/0x1010 mm/slub.c:1939 + new_slab mm/slub.c:1992 [inline] + ___slab_alloc+0x10c3/0x2d60 mm/slub.c:3180 + __slab_alloc mm/slub.c:3279 [inline] + slab_alloc_node mm/slub.c:3364 [inline] + slab_alloc mm/slub.c:3406 [inline] + __kmem_cache_alloc_lru mm/slub.c:3413 [inline] + kmem_cache_alloc_lru+0x6f3/0xb30 mm/slub.c:3429 + alloc_inode_sb include/linux/fs.h:3117 [inline] + ext4_alloc_inode+0x5f/0x860 fs/ext4/super.c:1321 + alloc_inode+0x83/0x440 fs/inode.c:259 + new_inode_pseudo fs/inode.c:1018 [inline] + new_inode+0x3b/0x430 fs/inode.c:1046 + __ext4_new_inode+0x2a7/0x7ec0 fs/ext4/ialloc.c:959 + ext4_mkdir+0x4d5/0x1560 fs/ext4/namei.c:2992 + vfs_mkdir+0x62a/0x870 fs/namei.c:4035 + do_mkdirat+0x466/0x7b0 fs/namei.c:4060 + __do_sys_mkdirat fs/namei.c:4075 [inline] + __se_sys_mkdirat fs/namei.c:4073 [inline] + __ia32_sys_mkdirat+0xc4/0x120 fs/namei.c:4073 + do_syscall_32_irqs_on arch/x86/entry/common.c:112 [inline] + __do_fast_syscall_32+0xa2/0x100 arch/x86/entry/common.c:178 + do_fast_syscall_32+0x33/0x70 arch/x86/entry/common.c:203 + do_SYSENTER_32+0x1b/0x20 arch/x86/entry/common.c:246 + entry_SYSENTER_compat_after_hwframe+0x70/0x82 + +CPU: 1 PID: 4625 Comm: syz-executor.2 Not tainted 6.1.0-rc4-syzkaller-62821-gcb231e2f67ec #0 +Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/26/2022 +===================================================== + +Now, 'ext4_alloc_inode()' didn't init 'ei->i_flags'. If new inode failed +before set 'ei->i_flags' in '__ext4_new_inode()', then do 'iput()'. As after +6bc0d63dad7f commit will access 'ei->i_flags' in 'ext4_evict_inode()' which +will lead to access uninit-value. +To solve above issue just init 'ei->i_flags' in 'ext4_alloc_inode()'. + +Reported-by: syzbot+57b25da729eb0b88177d@syzkaller.appspotmail.com +Signed-off-by: Ye Bin +Fixes: 6bc0d63dad7f ("ext4: remove EA inode entry from mbcache on inode eviction") +Reviewed-by: Jan Kara +Reviewed-by: Eric Biggers +Link: https://lore.kernel.org/r/20221117073603.2598882-1-yebin@huaweicloud.com +Signed-off-by: Theodore Ts'o +Cc: stable@kernel.org +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/super.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/fs/ext4/super.c ++++ b/fs/ext4/super.c +@@ -1323,6 +1323,7 @@ static struct inode *ext4_alloc_inode(st + return NULL; + + inode_set_iversion(&ei->vfs_inode, 1); ++ ei->i_flags = 0; + spin_lock_init(&ei->i_raw_lock); + INIT_LIST_HEAD(&ei->i_prealloc_list); + atomic_set(&ei->i_prealloc_active, 0); diff --git a/queue-6.0/series b/queue-6.0/series index 63cadec195b..918ed336b4a 100644 --- a/queue-6.0/series +++ b/queue-6.0/series @@ -143,3 +143,10 @@ ext4-journal_path-mount-options-should-follow-links.patch ext4-check-and-assert-if-marking-an-no_delete-evicting-inode-dirty.patch ext4-fix-bug_on-in-__es_tree_search-caused-by-bad-boot-loader-inode.patch ext4-don-t-allow-journal-inode-to-have-encrypt-flag.patch +ext4-disable-fast-commit-of-encrypted-dir-operations.patch +ext4-fix-leaking-uninitialized-memory-in-fast-commit-journal.patch +ext4-don-t-set-up-encryption-key-during-jbd2-transaction.patch +ext4-add-missing-validation-of-fast-commit-record-lengths.patch +ext4-fix-unaligned-memory-access-in-ext4_fc_reserve_space.patch +ext4-fix-off-by-one-errors-in-fast-commit-block-filling.patch +ext4-fix-uninititialized-value-in-ext4_evict_inode.patch