bcachefs: Delay calculation of trans->journal_u64s
authorAlan Huang <mmpgouride@gmail.com>
Thu, 12 Jun 2025 19:01:59 +0000 (03:01 +0800)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 16 Jun 2025 02:11:55 +0000 (22:11 -0400)
When there is commit error that need split btree leaf, fsck might change
the value of trans->journal_entries.u64s, when retry commit, the value of
trans->journal_u64s would be incorrect, which will lead to trans->journal_res.u64s
underflow, and then out of bounds write will occur:

[  464.496970][T11969] Call trace:
[  464.496973][T11969]  show_stack+0x3c/0x88 (C)
[  464.496995][T11969]  dump_stack_lvl+0xf8/0x178
[  464.497014][T11969]  dump_stack+0x20/0x30
[  464.497031][T11969]  __bch2_trans_log_str+0x344/0x350
[  464.497048][T11969]  bch2_trans_log_str+0x3c/0x60
[  464.497065][T11969]  __bch2_fsck_err+0x11bc/0x1390
[  464.497083][T11969]  bch2_check_discard_freespace_key+0xad4/0x10d0
[  464.497100][T11969]  bch2_bucket_alloc_freelist+0x99c/0x1130
[  464.497117][T11969]  bch2_bucket_alloc_trans+0x79c/0xcb8
[  464.497133][T11969]  bch2_bucket_alloc_set_trans+0x378/0xc20
[  464.497151][T11969]  __open_bucket_add_buckets+0x7fc/0x1c00
[  464.497168][T11969]  open_bucket_add_buckets+0x184/0x3a8
[  464.497185][T11969]  bch2_alloc_sectors_start_trans+0xa04/0x1da0
[  464.497203][T11969]  bch2_btree_reserve_get+0x6e0/0xef0
[  464.497220][T11969]  bch2_btree_update_start+0x1618/0x2600
[  464.497239][T11969]  bch2_btree_split_leaf+0xcc/0x730
[  464.497258][T11969]  bch2_trans_commit_error+0x22c/0xc30
[  464.497276][T11969]  __bch2_trans_commit+0x207c/0x4e30
[  464.497292][T11969]  bch2_journal_replay+0x9e0/0x1420
[  464.497305][T11969]  __bch2_run_recovery_passes+0x458/0xf98
[  464.497318][T11969]  bch2_run_recovery_passes+0x280/0x478
[  464.497331][T11969]  bch2_fs_recovery+0x24f0/0x3a28
[  464.497344][T11969]  bch2_fs_start+0xb80/0x1248
[  464.497358][T11969]  bch2_fs_get_tree+0xe94/0x1708
[  464.497377][T11969]  vfs_get_tree+0x84/0x2d0

Signed-off-by: Alan Huang <mmpgouride@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/btree_trans_commit.c

index 55952143f0d379545be51f05dd5b1f2c28574047..61107f2310ab7de0cd9f6b0c18216f664fd3af91 100644 (file)
@@ -1005,6 +1005,7 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags)
 {
        struct btree_insert_entry *errored_at = NULL;
        struct bch_fs *c = trans->c;
+       unsigned journal_u64s = 0;
        int ret = 0;
 
        bch2_trans_verify_not_unlocked_or_in_restart(trans);
@@ -1033,10 +1034,10 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags)
 
        EBUG_ON(test_bit(BCH_FS_clean_shutdown, &c->flags));
 
-       trans->journal_u64s             = trans->journal_entries.u64s + jset_u64s(trans->accounting.u64s);
+       journal_u64s = jset_u64s(trans->accounting.u64s);
        trans->journal_transaction_names = READ_ONCE(c->opts.journal_transaction_names);
        if (trans->journal_transaction_names)
-               trans->journal_u64s += jset_u64s(JSET_ENTRY_LOG_U64s);
+               journal_u64s += jset_u64s(JSET_ENTRY_LOG_U64s);
 
        trans_for_each_update(trans, i) {
                struct btree_path *path = trans->paths + i->path;
@@ -1056,11 +1057,11 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags)
                        continue;
 
                /* we're going to journal the key being updated: */
-               trans->journal_u64s += jset_u64s(i->k->k.u64s);
+               journal_u64s += jset_u64s(i->k->k.u64s);
 
                /* and we're also going to log the overwrite: */
                if (trans->journal_transaction_names)
-                       trans->journal_u64s += jset_u64s(i->old_k.u64s);
+                       journal_u64s += jset_u64s(i->old_k.u64s);
        }
 
        if (trans->extra_disk_res) {
@@ -1078,6 +1079,8 @@ retry:
                memset(&trans->journal_res, 0, sizeof(trans->journal_res));
        memset(&trans->fs_usage_delta, 0, sizeof(trans->fs_usage_delta));
 
+       trans->journal_u64s = journal_u64s + trans->journal_entries.u64s;
+
        ret = do_bch2_trans_commit(trans, flags, &errored_at, _RET_IP_);
 
        /* make sure we didn't drop or screw up locks: */