diff mbox series

jbd2: fix integer underflow in jbd2_journal_initialize_fast_commit()

Message ID SYBPR01MB78813DD23B28BD49B1AA1123AF392@SYBPR01MB7881.ausprd01.prod.outlook.com
State Superseded
Headers show
Series jbd2: fix integer underflow in jbd2_journal_initialize_fast_commit() | expand

Commit Message

Junrui Luo May 12, 2026, 7:49 a.m. UTC
jbd2_journal_initialize_fast_commit() validates journal capacity by
checking (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS).
Both j_last and num_fc_blks are unsigned, so when num_fc_blks exceeds
j_last the subtraction wraps to a large value, bypassing the bounds
check.

The resulting underflow corrupts j_last, j_fc_first, and j_free,
leading to journal abort.

Fix by adding an overflow guard that checks num_fc_blks against j_last
before performing the subtraction.

Fixes: 6866d7b3f2bb ("ext4 / jbd2: add fast commit initialization")
Reported-by: Yuhao Jiang <danisjiang@gmail.com>
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
---
 fs/jbd2/journal.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)


---
base-commit: 7aaa8047eafd0bd628065b15757d9b48c5f9c07d
change-id: 20260512-fixes-2ff4f9f7d064

Best regards,

Comments

Zhang Yi May 12, 2026, 12:08 p.m. UTC | #1
On 5/12/2026 3:49 PM, Junrui Luo wrote:
> jbd2_journal_initialize_fast_commit() validates journal capacity by
> checking (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS).
> Both j_last and num_fc_blks are unsigned, so when num_fc_blks exceeds
> j_last the subtraction wraps to a large value, bypassing the bounds
> check.

I'm wondering, how does the "num_fc_blks exceeds j_last" error occur?
Under normal circumstances, journal->j_last is initialized to
sb->s_maxlen, which is set to the total number of journal blocks (i.e.,
the sum of the normal journal area and the fast commit journal area)
during filesystem formatting by mkfs. Therefore, num_fc_blocks shoud
never exceed journal->j_last. Right?

Have you mounted a deliberately constructed corrupted file system? If
so, I'd prefer to return EFSCORRUPTED here.

Thanks,
Yi.

> 
> The resulting underflow corrupts j_last, j_fc_first, and j_free,
> leading to journal abort.
> 
> Fix by adding an overflow guard that checks num_fc_blks against j_last
> before performing the subtraction.
> 
> Fixes: 6866d7b3f2bb ("ext4 / jbd2: add fast commit initialization")
> Reported-by: Yuhao Jiang <danisjiang@gmail.com>
> Cc: stable@vger.kernel.org
> Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
> ---
>  fs/jbd2/journal.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
> index cb2c529a8f1b..a54146576c3f 100644
> --- a/fs/jbd2/journal.c
> +++ b/fs/jbd2/journal.c
> @@ -2263,7 +2263,8 @@ jbd2_journal_initialize_fast_commit(journal_t *journal)
>  	unsigned long long num_fc_blks;
>  
>  	num_fc_blks = jbd2_journal_get_num_fc_blks(sb);
> -	if (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS)
> +	if (num_fc_blks > journal->j_last ||
> +	    journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS)
>  		return -ENOSPC;
>  
>  	/* Are we called twice? */
> 
> ---
> base-commit: 7aaa8047eafd0bd628065b15757d9b48c5f9c07d
> change-id: 20260512-fixes-2ff4f9f7d064
> 
> Best regards,
Junrui Luo May 12, 2026, 1:03 p.m. UTC | #2
On Tue, May 12, 2026 at 08:08:56PM +0800, Zhang Yi wrote:
> On 5/12/2026 3:49 PM, Junrui Luo wrote:
> > jbd2_journal_initialize_fast_commit() validates journal capacity by
> > checking (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS).
> > Both j_last and num_fc_blks are unsigned, so when num_fc_blks exceeds
> > j_last the subtraction wraps to a large value, bypassing the bounds
> > check.
> 
> I'm wondering, how does the "num_fc_blks exceeds j_last" error occur?
> Under normal circumstances, journal->j_last is initialized to
> sb->s_maxlen, which is set to the total number of journal blocks (i.e.,
> the sum of the normal journal area and the fast commit journal area)
> during filesystem formatting by mkfs. Therefore, num_fc_blocks shoud
> never exceed journal->j_last. Right?

Yes, this is triggered by mounting a crafted filesystem where the ext4
superblock has fast_commit enabled but the journal superblock does not,
while s_num_fc_blks is set larger than s_maxlen.

> Have you mounted a deliberately constructed corrupted file system? If
> so, I'd prefer to return EFSCORRUPTED here.
 
I will change it in v2.

Thanks,
Junrui Luo
diff mbox series

Patch

diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index cb2c529a8f1b..a54146576c3f 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -2263,7 +2263,8 @@  jbd2_journal_initialize_fast_commit(journal_t *journal)
 	unsigned long long num_fc_blks;
 
 	num_fc_blks = jbd2_journal_get_num_fc_blks(sb);
-	if (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS)
+	if (num_fc_blks > journal->j_last ||
+	    journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS)
 		return -ENOSPC;
 
 	/* Are we called twice? */