bcachefs: Move bset size check before csum check
authorAlan Huang <mmpgouride@gmail.com>
Sat, 14 Jun 2025 09:18:07 +0000 (17:18 +0800)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 16 Jun 2025 23:03:52 +0000 (19:03 -0400)
In syzbot's crash, the bset's u64s is larger than the btree node.

Reported-by: syzbot+bfaeaa8e26281970158d@syzkaller.appspotmail.com
Signed-off-by: Alan Huang <mmpgouride@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/btree_io.c

index d8f3c4c65e90cd74d3baa8b92f05d6ec622e4c23..005d5c94edd01b73f1a5238022d5933e51d0eaa3 100644 (file)
@@ -723,12 +723,11 @@ void bch2_btree_node_drop_keys_outside_node(struct btree *b)
 
 static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
                         struct btree *b, struct bset *i,
-                        unsigned offset, unsigned sectors, int write,
+                        unsigned offset, int write,
                         struct bch_io_failures *failed,
                         struct printbuf *err_msg)
 {
        unsigned version = le16_to_cpu(i->version);
-       unsigned ptr_written = btree_ptr_sectors_written(bkey_i_to_s_c(&b->key));
        struct printbuf buf1 = PRINTBUF;
        struct printbuf buf2 = PRINTBUF;
        int ret = 0;
@@ -778,15 +777,6 @@ static int validate_bset(struct bch_fs *c, struct bch_dev *ca,
                     btree_node_unsupported_version,
                     "BSET_SEPARATE_WHITEOUTS no longer supported");
 
-       if (!write &&
-           btree_err_on(offset + sectors > (ptr_written ?: btree_sectors(c)),
-                        -BCH_ERR_btree_node_read_err_fixable,
-                        c, ca, b, i, NULL,
-                        bset_past_end_of_btree_node,
-                        "bset past end of btree node (offset %u len %u but written %zu)",
-                        offset, sectors, ptr_written ?: btree_sectors(c)))
-               i->u64s = 0;
-
        btree_err_on(offset && !i->u64s,
                     -BCH_ERR_btree_node_read_err_fixable,
                     c, ca, b, i, NULL,
@@ -1151,6 +1141,14 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
                             "unknown checksum type %llu", BSET_CSUM_TYPE(i));
 
                if (first) {
+                       sectors = vstruct_sectors(b->data, c->block_bits);
+                       if (btree_err_on(b->written + sectors > (ptr_written ?: btree_sectors(c)),
+                                        -BCH_ERR_btree_node_read_err_fixable,
+                                        c, ca, b, i, NULL,
+                                        bset_past_end_of_btree_node,
+                                        "bset past end of btree node (offset %u len %u but written %zu)",
+                                        b->written, sectors, ptr_written ?: btree_sectors(c)))
+                               i->u64s = 0;
                        if (good_csum_type) {
                                struct bch_csum csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, b->data);
                                bool csum_bad = bch2_crc_cmp(b->data->csum, csum);
@@ -1178,9 +1176,15 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
                                     c, NULL, b, NULL, NULL,
                                     btree_node_unsupported_version,
                                     "btree node does not have NEW_EXTENT_OVERWRITE set");
-
-                       sectors = vstruct_sectors(b->data, c->block_bits);
                } else {
+                       sectors = vstruct_sectors(bne, c->block_bits);
+                       if (btree_err_on(b->written + sectors > (ptr_written ?: btree_sectors(c)),
+                                        -BCH_ERR_btree_node_read_err_fixable,
+                                        c, ca, b, i, NULL,
+                                        bset_past_end_of_btree_node,
+                                        "bset past end of btree node (offset %u len %u but written %zu)",
+                                        b->written, sectors, ptr_written ?: btree_sectors(c)))
+                               i->u64s = 0;
                        if (good_csum_type) {
                                struct bch_csum csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, bne);
                                bool csum_bad = bch2_crc_cmp(bne->csum, csum);
@@ -1201,14 +1205,12 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
                                                "decrypting btree node: %s", bch2_err_str(ret)))
                                        goto fsck_err;
                        }
-
-                       sectors = vstruct_sectors(bne, c->block_bits);
                }
 
                b->version_ondisk = min(b->version_ondisk,
                                        le16_to_cpu(i->version));
 
-               ret = validate_bset(c, ca, b, i, b->written, sectors, READ, failed, err_msg);
+               ret = validate_bset(c, ca, b, i, b->written, READ, failed, err_msg);
                if (ret)
                        goto fsck_err;
 
@@ -2267,7 +2269,7 @@ static void btree_node_write_endio(struct bio *bio)
 }
 
 static int validate_bset_for_write(struct bch_fs *c, struct btree *b,
-                                  struct bset *i, unsigned sectors)
+                                  struct bset *i)
 {
        int ret = bch2_bkey_validate(c, bkey_i_to_s_c(&b->key),
                                     (struct bkey_validate_context) {
@@ -2282,7 +2284,7 @@ static int validate_bset_for_write(struct bch_fs *c, struct btree *b,
        }
 
        ret = validate_bset_keys(c, b, i, WRITE, NULL, NULL) ?:
-               validate_bset(c, NULL, b, i, b->written, sectors, WRITE, NULL, NULL);
+               validate_bset(c, NULL, b, i, b->written, WRITE, NULL, NULL);
        if (ret) {
                bch2_inconsistent_error(c);
                dump_stack();
@@ -2475,7 +2477,7 @@ do_write:
 
        /* if we're going to be encrypting, check metadata validity first: */
        if (validate_before_checksum &&
-           validate_bset_for_write(c, b, i, sectors_to_write))
+           validate_bset_for_write(c, b, i))
                goto err;
 
        ret = bset_encrypt(c, i, b->written << 9);
@@ -2492,7 +2494,7 @@ do_write:
 
        /* if we're not encrypting, check metadata after checksumming: */
        if (!validate_before_checksum &&
-           validate_bset_for_write(c, b, i, sectors_to_write))
+           validate_bset_for_write(c, b, i))
                goto err;
 
        /*