Commit d5bd3787 authored by Kent Overstreet's avatar Kent Overstreet
Browse files

bcachefs: Add missing validation for jset_entry_data_usage



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: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent bbc3a460
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -210,6 +210,7 @@
	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)			\
+11 −1
Original line number Diff line number Diff line
@@ -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;
}

+39 −30
Original line number Diff line number Diff line
@@ -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 =
+2 −0
Original line number Diff line number Diff line
@@ -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 *