Commit bac3190a authored by Milos Nikic's avatar Milos Nikic Committed by Theodore Ts'o
Browse files

jbd2: gracefully abort on checkpointing state corruptions



This patch targets two internal state machine invariants in checkpoint.c
residing inside functions that natively return integer error codes.

- In jbd2_cleanup_journal_tail(): A blocknr of 0 indicates a severely
corrupted journal superblock. Replaced the J_ASSERT with a WARN_ON_ONCE
and a graceful journal abort, returning -EFSCORRUPTED.

- In jbd2_log_do_checkpoint(): Replaced the J_ASSERT_BH checking for
an unexpected buffer_jwrite state. If the warning triggers, we
explicitly drop the just-taken get_bh() reference and call __flush_batch()
to safely clean up any previously queued buffers in the j_chkpt_bhs array,
preventing a memory leak before returning -EFSCORRUPTED.

Signed-off-by: default avatarMilos Nikic <nikic.milos@gmail.com>
Reviewed-by: default avatarAndreas Dilger <adilger@dilger.ca>
Reviewed-by: default avatarZhang Yi <yi.zhang@huawei.com>
Reviewed-by: default avatarBaokun Li <libaokun@linux.alibaba.com>
Reviewed-by: default avatarJan Kara <jack@suse.cz>
Link: https://patch.msgid.link/20260311041548.159424-1-nikic.milos@gmail.com


Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Cc: stable@kernel.org
parent 5422fe71
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -267,7 +267,15 @@ int jbd2_log_do_checkpoint(journal_t *journal)
			 */
			BUFFER_TRACE(bh, "queue");
			get_bh(bh);
			J_ASSERT_BH(bh, !buffer_jwrite(bh));
			if (WARN_ON_ONCE(buffer_jwrite(bh))) {
				put_bh(bh); /* drop the ref we just took */
				spin_unlock(&journal->j_list_lock);
				/* Clean up any previously batched buffers */
				if (batch_count)
					__flush_batch(journal, &batch_count);
				jbd2_journal_abort(journal, -EFSCORRUPTED);
				return -EFSCORRUPTED;
			}
			journal->j_chkpt_bhs[batch_count++] = bh;
			transaction->t_chp_stats.cs_written++;
			transaction->t_checkpoint_list = jh->b_cpnext;
@@ -325,7 +333,10 @@ int jbd2_cleanup_journal_tail(journal_t *journal)

	if (!jbd2_journal_get_log_tail(journal, &first_tid, &blocknr))
		return 1;
	J_ASSERT(blocknr != 0);
	if (WARN_ON_ONCE(blocknr == 0)) {
		jbd2_journal_abort(journal, -EFSCORRUPTED);
		return -EFSCORRUPTED;
	}

	/*
	 * We need to make sure that any blocks that were recently written out