]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
bcachefs: Fix shift-by-64 in bch2_bkey_format_validate()
authorKent Overstreet <kent.overstreet@gmail.com>
Thu, 24 Jun 2021 17:19:25 +0000 (13:19 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:09:07 +0000 (17:09 -0400)
We need to ensure that packed formats can't represent fields larger than
the unpacked format, which is a bit tricky since the calculations can
also overflow a u64. This patch fixes a shift and simplifies the overall
calculations.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
fs/bcachefs/bkey.c

index 5de88a93f33fe5ca7dabe9c73a175e9f55bef6f5..3e62eeb6774e70deb7ca5d3821bcd92e3580386b 100644 (file)
@@ -623,22 +623,22 @@ const char *bch2_bkey_format_validate(struct bkey_format *f)
        if (f->nr_fields != BKEY_NR_FIELDS)
                return "incorrect number of fields";
 
+       /*
+        * Verify that the packed format can't represent fields larger than the
+        * unpacked format:
+        */
        for (i = 0; i < f->nr_fields; i++) {
                unsigned unpacked_bits = bch2_bkey_format_current.bits_per_field[i];
-               u64 unpacked_mask = ~((~0ULL << 1) << (unpacked_bits - 1));
+               u64 unpacked_max = ~((~0ULL << 1) << (unpacked_bits - 1));
+               u64 packed_max = f->bits_per_field[i]
+                       ? ~((~0ULL << 1) << (f->bits_per_field[i] - 1))
+                       : 0;
                u64 field_offset = le64_to_cpu(f->field_offset[i]);
 
-               if (f->bits_per_field[i] > unpacked_bits)
+               if (packed_max + field_offset < packed_max ||
+                   packed_max + field_offset > unpacked_max)
                        return "field too large";
 
-               if ((f->bits_per_field[i] == unpacked_bits) && field_offset)
-                       return "offset + bits overflow";
-
-               if (((field_offset + ((1ULL << f->bits_per_field[i]) - 1)) &
-                    unpacked_mask) <
-                   field_offset)
-                       return "offset + bits overflow";
-
                bits += f->bits_per_field[i];
        }