]> www.infradead.org Git - nvme.git/commitdiff
bcachefs: Fix shift-by-64 in bformat_needs_redo()
authorKent Overstreet <kent.overstreet@linux.dev>
Mon, 6 May 2024 02:44:27 +0000 (22:44 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 6 May 2024 14:58:17 +0000 (10:58 -0400)
Ancient versions of bcachefs produced packed formats that could
represent keys that our in memory format cannot represent;
bformat_needs_redo() has some tricky shifts to check for this sort of
overflow.

Reported-by: syzbot+594427aebfefeebe91c6@syzkaller.appspotmail.com
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/move.c

index bf68ea49447b95055a4f6a1e6e7c6a7e373aebc5..4d94b7742dbbae3b8dc251d24262c6ee9361aa4a 100644 (file)
@@ -968,24 +968,30 @@ static bool migrate_btree_pred(struct bch_fs *c, void *arg,
        return migrate_pred(c, arg, bkey_i_to_s_c(&b->key), io_opts, data_opts);
 }
 
+/*
+ * Ancient versions of bcachefs produced packed formats which could represent
+ * keys that the in memory format cannot represent; this checks for those
+ * formats so we can get rid of them.
+ */
 static bool bformat_needs_redo(struct bkey_format *f)
 {
-       unsigned i;
-
-       for (i = 0; i < f->nr_fields; i++) {
+       for (unsigned i = 0; i < f->nr_fields; i++) {
+               unsigned f_bits = f->bits_per_field[i];
                unsigned unpacked_bits = bch2_bkey_format_current.bits_per_field[i];
                u64 unpacked_mask = ~((~0ULL << 1) << (unpacked_bits - 1));
                u64 field_offset = le64_to_cpu(f->field_offset[i]);
 
-               if (f->bits_per_field[i] > unpacked_bits)
+               if (f_bits > unpacked_bits)
                        return true;
 
-               if ((f->bits_per_field[i] == unpacked_bits) && field_offset)
+               if ((f_bits == unpacked_bits) && field_offset)
                        return true;
 
-               if (((field_offset + ((1ULL << f->bits_per_field[i]) - 1)) &
-                    unpacked_mask) <
-                   field_offset)
+               u64 f_mask = f_bits
+                       ? ~((~0ULL << (f_bits - 1)) << 1)
+                       : 0;
+
+               if (((field_offset + f_mask) & unpacked_mask) < field_offset)
                        return true;
        }