Skip to content

Commit

Permalink
bcachefs: Add missing validation for jset_entry_data_usage
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Kent Overstreet committed Nov 28, 2023
1 parent bbc3a46 commit d5bd378
Showing 4 changed files with 53 additions and 31 deletions.
1 change: 1 addition & 0 deletions fs/bcachefs/errcode.h
Original file line number Diff line number Diff line change
@@ -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) \
12 changes: 11 additions & 1 deletion fs/bcachefs/journal_io.c
Original file line number Diff line number Diff line change
@@ -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;
}

69 changes: 39 additions & 30 deletions fs/bcachefs/replicas.c
Original file line number Diff line number Diff line change
@@ -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,15 +413,15 @@ 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;
}
}

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 changes: 2 additions & 0 deletions fs/bcachefs/replicas.h
Original file line number Diff line number Diff line change
@@ -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 *

0 comments on commit d5bd378

Please sign in to comment.