bcachefs: Fix a buffer overrun in bch2_fs_usage_read()
authorKent Overstreet <kent.overstreet@linux.dev>
Mon, 22 May 2023 18:39:44 +0000 (14:39 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:10:01 +0000 (17:10 -0400)
We were copying the size of a struct bch_fs_usage_online to a struct
bch_fs_usage, which is 8 bytes smaller.

This adds some new helpers so we can do this correctly, and get rid of
some magic +1s too.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/buckets.c
fs/bcachefs/buckets.h

index bce42eef6f576001341fe94283a05ea46652a1d9..bd144182c1e12bc2014f7b1359fa03987e77ebf9 100644 (file)
@@ -137,17 +137,17 @@ u64 bch2_fs_usage_read_one(struct bch_fs *c, u64 *v)
 struct bch_fs_usage_online *bch2_fs_usage_read(struct bch_fs *c)
 {
        struct bch_fs_usage_online *ret;
-       unsigned seq, i, v, u64s = fs_usage_u64s(c) + 1;
+       unsigned nr_replicas = READ_ONCE(c->replicas.nr);
+       unsigned seq, i;
 retry:
-       ret = kmalloc(u64s * sizeof(u64), GFP_NOFS);
+       ret = kmalloc(__fs_usage_online_u64s(nr_replicas) * sizeof(u64), GFP_NOFS);
        if (unlikely(!ret))
                return NULL;
 
        percpu_down_read(&c->mark_lock);
 
-       v = fs_usage_u64s(c) + 1;
-       if (unlikely(u64s != v)) {
-               u64s = v;
+       if (nr_replicas != c->replicas.nr) {
+               nr_replicas = c->replicas.nr;
                percpu_up_read(&c->mark_lock);
                kfree(ret);
                goto retry;
@@ -157,10 +157,12 @@ retry:
 
        do {
                seq = read_seqcount_begin(&c->usage_lock);
-               unsafe_memcpy(&ret->u, c->usage_base, u64s * sizeof(u64),
+               unsafe_memcpy(&ret->u, c->usage_base,
+                             __fs_usage_u64s(nr_replicas) * sizeof(u64),
                              "embedded variable length struct");
                for (i = 0; i < ARRAY_SIZE(c->usage); i++)
-                       acc_u64s_percpu((u64 *) &ret->u, (u64 __percpu *) c->usage[i], u64s);
+                       acc_u64s_percpu((u64 *) &ret->u, (u64 __percpu *) c->usage[i],
+                                       __fs_usage_u64s(nr_replicas));
        } while (read_seqcount_retry(&c->usage_lock, seq));
 
        return ret;
index d677b0225c52bf1d9388790e8680805a124f572e..bdf4fff9cb8a8a69e372a03daf9051231ae352bd 100644 (file)
@@ -207,10 +207,24 @@ static inline u64 dev_buckets_available(struct bch_dev *ca,
 
 /* Filesystem usage: */
 
+static inline unsigned __fs_usage_u64s(unsigned nr_replicas)
+{
+       return sizeof(struct bch_fs_usage) / sizeof(u64) + nr_replicas;
+}
+
 static inline unsigned fs_usage_u64s(struct bch_fs *c)
 {
-       return sizeof(struct bch_fs_usage) / sizeof(u64) +
-               READ_ONCE(c->replicas.nr);
+       return __fs_usage_u64s(READ_ONCE(c->replicas.nr));
+}
+
+static inline unsigned __fs_usage_online_u64s(unsigned nr_replicas)
+{
+       return sizeof(struct bch_fs_usage_online) / sizeof(u64) + nr_replicas;
+}
+
+static inline unsigned fs_usage_online_u64s(struct bch_fs *c)
+{
+       return __fs_usage_online_u64s(READ_ONCE(c->replicas.nr));
 }
 
 static inline unsigned dev_usage_u64s(void)