bcachefs: Add missing validation for jset_entry_data_usage
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 26 Nov 2023 02:42:08 +0000 (21:42 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Tue, 28 Nov 2023 22:18:24 +0000 (17:18 -0500)
Validation was completely missing for replicas entries in the journal
(not the superblock replicas section) - we can't have replicas entries
pointing to invalid devices.

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

index 69b0627c2180e967151c974a2fa9c6b75ed7cb6b..ae7910bf2228c467eb8e52bab82425b8a335c3f3 100644 (file)
        x(BCH_ERR_invalid_sb,           invalid_sb_members)                     \
        x(BCH_ERR_invalid_sb,           invalid_sb_disk_groups)                 \
        x(BCH_ERR_invalid_sb,           invalid_sb_replicas)                    \
+       x(BCH_ERR_invalid_sb,           invalid_replicas_entry)                 \
        x(BCH_ERR_invalid_sb,           invalid_sb_journal)                     \
        x(BCH_ERR_invalid_sb,           invalid_sb_journal_seq_blacklist)       \
        x(BCH_ERR_invalid_sb,           invalid_sb_crypt)                       \
index 02e6484f9953b07a10227333b2d564882534e109..0f17fc5f8d6844f774d71f5a361b55e6acbb866f 100644 (file)
@@ -547,6 +547,7 @@ static int journal_entry_data_usage_validate(struct bch_fs *c,
        struct jset_entry_data_usage *u =
                container_of(entry, struct jset_entry_data_usage, entry);
        unsigned bytes = jset_u64s(le16_to_cpu(entry->u64s)) * sizeof(u64);
+       struct printbuf err = PRINTBUF;
        int ret = 0;
 
        if (journal_entry_err_on(bytes < sizeof(*u) ||
@@ -555,10 +556,19 @@ static int journal_entry_data_usage_validate(struct bch_fs *c,
                                 journal_entry_data_usage_bad_size,
                                 "invalid journal entry usage: bad size")) {
                journal_entry_null_range(entry, vstruct_next(entry));
-               return ret;
+               goto out;
        }
 
+       if (journal_entry_err_on(bch2_replicas_entry_validate(&u->r, c->disk_sb.sb, &err),
+                                c, version, jset, entry,
+                                journal_entry_data_usage_bad_size,
+                                "invalid journal entry usage: %s", err.buf)) {
+               journal_entry_null_range(entry, vstruct_next(entry));
+               goto out;
+       }
+out:
 fsck_err:
+       printbuf_exit(&err);
        return ret;
 }
 
index 1c3ae13bfced1d8ce9eeee118cb6e9fe1552e7a5..2008fe8bf7060d0e4da522e723ce3ae6fbc42d9e 100644 (file)
@@ -68,6 +68,33 @@ void bch2_replicas_entry_to_text(struct printbuf *out,
        prt_printf(out, "]");
 }
 
+int bch2_replicas_entry_validate(struct bch_replicas_entry *r,
+                                struct bch_sb *sb,
+                                struct printbuf *err)
+{
+       if (!r->nr_devs) {
+               prt_printf(err, "no devices in entry ");
+               goto bad;
+       }
+
+       if (r->nr_required > 1 &&
+           r->nr_required >= r->nr_devs) {
+               prt_printf(err, "bad nr_required in entry ");
+               goto bad;
+       }
+
+       for (unsigned i = 0; i < r->nr_devs; i++)
+               if (!bch2_dev_exists(sb, r->devs[i])) {
+                       prt_printf(err, "invalid device %u in entry ", r->devs[i]);
+                       goto bad;
+               }
+
+       return 0;
+bad:
+       bch2_replicas_entry_to_text(err, r);
+       return -BCH_ERR_invalid_replicas_entry;
+}
+
 void bch2_cpu_replicas_to_text(struct printbuf *out,
                               struct bch_replicas_cpu *r)
 {
@@ -163,7 +190,8 @@ void bch2_devlist_to_replicas(struct bch_replicas_entry *e,
 }
 
 static struct bch_replicas_cpu
-cpu_replicas_add_entry(struct bch_replicas_cpu *old,
+cpu_replicas_add_entry(struct bch_fs *c,
+                      struct bch_replicas_cpu *old,
                       struct bch_replicas_entry *new_entry)
 {
        unsigned i;
@@ -173,6 +201,9 @@ cpu_replicas_add_entry(struct bch_replicas_cpu *old,
                                        replicas_entry_bytes(new_entry)),
        };
 
+       for (i = 0; i < new_entry->nr_devs; i++)
+               BUG_ON(!bch2_dev_exists2(c, new_entry->devs[i]));
+
        BUG_ON(!new_entry->data_type);
        verify_replicas_entry(new_entry);
 
@@ -382,7 +413,7 @@ static int bch2_mark_replicas_slowpath(struct bch_fs *c,
 
        if (c->replicas_gc.entries &&
            !__replicas_has_entry(&c->replicas_gc, new_entry)) {
-               new_gc = cpu_replicas_add_entry(&c->replicas_gc, new_entry);
+               new_gc = cpu_replicas_add_entry(c, &c->replicas_gc, new_entry);
                if (!new_gc.entries) {
                        ret = -BCH_ERR_ENOMEM_cpu_replicas;
                        goto err;
@@ -390,7 +421,7 @@ static int bch2_mark_replicas_slowpath(struct bch_fs *c,
        }
 
        if (!__replicas_has_entry(&c->replicas, new_entry)) {
-               new_r = cpu_replicas_add_entry(&c->replicas, new_entry);
+               new_r = cpu_replicas_add_entry(c, &c->replicas, new_entry);
                if (!new_r.entries) {
                        ret = -BCH_ERR_ENOMEM_cpu_replicas;
                        goto err;
@@ -598,7 +629,7 @@ int bch2_replicas_set_usage(struct bch_fs *c,
        if (idx < 0) {
                struct bch_replicas_cpu n;
 
-               n = cpu_replicas_add_entry(&c->replicas, r);
+               n = cpu_replicas_add_entry(c, &c->replicas, r);
                if (!n.entries)
                        return -BCH_ERR_ENOMEM_cpu_replicas;
 
@@ -797,7 +828,7 @@ static int bch2_cpu_replicas_validate(struct bch_replicas_cpu *cpu_r,
                                      struct bch_sb *sb,
                                      struct printbuf *err)
 {
-       unsigned i, j;
+       unsigned i;
 
        sort_cmp_size(cpu_r->entries,
                      cpu_r->nr,
@@ -808,31 +839,9 @@ static int bch2_cpu_replicas_validate(struct bch_replicas_cpu *cpu_r,
                struct bch_replicas_entry *e =
                        cpu_replicas_entry(cpu_r, i);
 
-               if (e->data_type >= BCH_DATA_NR) {
-                       prt_printf(err, "invalid data type in entry ");
-                       bch2_replicas_entry_to_text(err, e);
-                       return -BCH_ERR_invalid_sb_replicas;
-               }
-
-               if (!e->nr_devs) {
-                       prt_printf(err, "no devices in entry ");
-                       bch2_replicas_entry_to_text(err, e);
-                       return -BCH_ERR_invalid_sb_replicas;
-               }
-
-               if (e->nr_required > 1 &&
-                   e->nr_required >= e->nr_devs) {
-                       prt_printf(err, "bad nr_required in entry ");
-                       bch2_replicas_entry_to_text(err, e);
-                       return -BCH_ERR_invalid_sb_replicas;
-               }
-
-               for (j = 0; j < e->nr_devs; j++)
-                       if (!bch2_dev_exists(sb, e->devs[j])) {
-                               prt_printf(err, "invalid device %u in entry ", e->devs[j]);
-                               bch2_replicas_entry_to_text(err, e);
-                               return -BCH_ERR_invalid_sb_replicas;
-                       }
+               int ret = bch2_replicas_entry_validate(e, sb, err);
+               if (ret)
+                       return ret;
 
                if (i + 1 < cpu_r->nr) {
                        struct bch_replicas_entry *n =
index 4887675a86f09c7a3942f3eae33d76179fe3c7bc..f70a642775d1b2b8257caff0ed52c2f8b90016da 100644 (file)
@@ -9,6 +9,8 @@
 void bch2_replicas_entry_sort(struct bch_replicas_entry *);
 void bch2_replicas_entry_to_text(struct printbuf *,
                                 struct bch_replicas_entry *);
+int bch2_replicas_entry_validate(struct bch_replicas_entry *,
+                                struct bch_sb *, struct printbuf *);
 void bch2_cpu_replicas_to_text(struct printbuf *, struct bch_replicas_cpu *);
 
 static inline struct bch_replicas_entry *