bcachefs: Add a better limit for maximum number of buckets
authorKent Overstreet <kent.overstreet@linux.dev>
Sat, 4 May 2024 17:26:37 +0000 (13:26 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 6 May 2024 14:58:17 +0000 (10:58 -0400)
The bucket_gens array is a single array allocation (one byte per
bucket), and kernel allocations are still limited to INT_MAX.

Check this limit to avoid failing the bucket_gens array allocation.

Reported-by: syzbot+b29f436493184ea42e2b@syzkaller.appspotmail.com
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/bcachefs_format.h
fs/bcachefs/errcode.h
fs/bcachefs/sb-members.c
fs/bcachefs/super.c

index f7fbfccd2b1e4d7e6bafabd839a6917de906edf9..8345a2b2d05be0c1fe4a3707bfb192c30f8f9dc0 100644 (file)
@@ -591,6 +591,12 @@ struct bch_member {
        __le64                  btree_allocated_bitmap;
 };
 
+/*
+ * This limit comes from the bucket_gens array - it's a single allocation, and
+ * kernel allocation are limited to INT_MAX
+ */
+#define BCH_MEMBER_NBUCKETS_MAX        (INT_MAX - 64)
+
 #define BCH_MEMBER_V1_BYTES    56
 
 LE64_BITMASK(BCH_MEMBER_STATE,         struct bch_member, flags,  0,  4)
index 01a79fa3eacb211cb7cd779616f512d427102fd4..dbe35b80bc0b893cb2c914c699c14573966590e4 100644 (file)
        x(EINVAL,                       block_size_too_small)                   \
        x(EINVAL,                       bucket_size_too_small)                  \
        x(EINVAL,                       device_size_too_small)                  \
+       x(EINVAL,                       device_size_too_big)                    \
        x(EINVAL,                       device_not_a_member_of_filesystem)      \
        x(EINVAL,                       device_has_been_removed)                \
        x(EINVAL,                       device_splitbrain)                      \
index 5b8e621ac5eb5780bb0c2e241196737bc0076e2e..44b3f0cb7b49747d9bdbd8cf34f401187c61d841 100644 (file)
@@ -124,9 +124,9 @@ static int validate_member(struct printbuf *err,
                           struct bch_sb *sb,
                           int i)
 {
-       if (le64_to_cpu(m.nbuckets) > LONG_MAX) {
-               prt_printf(err, "device %u: too many buckets (got %llu, max %lu)",
-                          i, le64_to_cpu(m.nbuckets), LONG_MAX);
+       if (le64_to_cpu(m.nbuckets) > BCH_MEMBER_NBUCKETS_MAX) {
+               prt_printf(err, "device %u: too many buckets (got %llu, max %u)",
+                          i, le64_to_cpu(m.nbuckets), BCH_MEMBER_NBUCKETS_MAX);
                return -BCH_ERR_invalid_sb_members;
        }
 
index c2c80e6890aee3dd333caf9686ca01881084c612..dddf57ec4511f3562592bce4c5c59988c9eed352 100644 (file)
@@ -1959,6 +1959,13 @@ int bch2_dev_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)
                goto err;
        }
 
+       if (nbuckets > BCH_MEMBER_NBUCKETS_MAX) {
+               bch_err(ca, "New device size too big (%llu greater than max %u)",
+                       nbuckets, BCH_MEMBER_NBUCKETS_MAX);
+               ret = -BCH_ERR_device_size_too_big;
+               goto err;
+       }
+
        if (bch2_dev_is_online(ca) &&
            get_capacity(ca->disk_sb.bdev->bd_disk) <
            ca->mi.bucket_size * nbuckets) {