[Concept,v2,19/30] ext4l: Add bh_cache_release_jbd() to clean up journal references

Message ID 20260102005112.552256-20-sjg@u-boot.org
State New
Headers
Series ext4l: Add write support (part L) |

Commit Message

Simon Glass Jan. 2, 2026, 12:50 a.m. UTC
  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>
---

(no changes since v1)

 fs/ext4l/ext4_uboot.h |  1 +
 fs/ext4l/support.c    | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)
  

Patch

diff --git a/fs/ext4l/ext4_uboot.h b/fs/ext4l/ext4_uboot.h
index 1c87e2a1181..a5c0eba6f15 100644
--- a/fs/ext4l/ext4_uboot.h
+++ b/fs/ext4l/ext4_uboot.h
@@ -2901,6 +2901,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 0d365e28265..71c906d9c88 100644
--- a/fs/ext4l/support.c
+++ b/fs/ext4l/support.c
@@ -335,6 +335,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
  *