From: Simon Glass <simon.glass@canonical.com> Add bh_cache_release_jbd() to forcibly release any journal_heads still attached to buffer_heads after journal destroy. This must be called after journal destroy but before bh_cache_clear() to ensure all journal_heads are properly released, even if journal destroy did not fully clean up (e.g., on abort). The function clears b_bh in each journal_head to prevent use-after-free when the buffer_head is later freed, and resets transaction pointers. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/ext4_uboot.h | 1 + fs/ext4l/support.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/fs/ext4l/ext4_uboot.h b/fs/ext4l/ext4_uboot.h index ba4b4266daa..00419fe834e 100644 --- a/fs/ext4l/ext4_uboot.h +++ b/fs/ext4l/ext4_uboot.h @@ -2891,6 +2891,7 @@ void free_buffer_head(struct buffer_head *bh); /* ext4l support functions (support.c) */ void ext4l_crc32c_init(void); +void bh_cache_release_jbd(void); void bh_cache_clear(void); int bh_cache_sync(void); int ext4l_read_block(sector_t block, size_t size, void *buffer); diff --git a/fs/ext4l/support.c b/fs/ext4l/support.c index 3133a4ebead..aaaf89092eb 100644 --- a/fs/ext4l/support.c +++ b/fs/ext4l/support.c @@ -332,6 +332,42 @@ void bh_cache_clear(void) } } +/** + * bh_cache_release_jbd() - Release all JBD references from buffer cache + * + * This must be called after journal destroy but before bh_cache_clear(). + * It ensures all journal_heads are properly released from buffer_heads + * even if the journal destroy didn't fully clean up (e.g., on abort). + */ +void bh_cache_release_jbd(void) +{ + int i; + struct bh_cache_entry *entry; + + for (i = 0; i < BH_CACHE_SIZE; i++) { + for (entry = bh_cache[i]; entry; entry = entry->next) { + if (entry->bh && buffer_jbd(entry->bh)) { + struct buffer_head *bh = entry->bh; + struct journal_head *jh = bh2jh(bh); + + /* + * Forcibly release the journal_head. + * Clear b_bh to prevent use-after-free when + * the buffer_head is later freed. + */ + if (jh) { + jh->b_bh = NULL; + jh->b_transaction = NULL; + jh->b_next_transaction = NULL; + jh->b_cp_transaction = NULL; + } + clear_buffer_jbd(bh); + bh->b_private = NULL; + } + } + } +} + /** * bh_cache_sync() - Sync all dirty buffers to disk * -- 2.43.0