From a62d8713f84f49d723aebc9d0271abf4c9dae335 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sat, 4 Apr 2020 13:59:13 -0400 Subject: [PATCH] Update bcachefs sources to 7bfc741f64 bcachefs: Fix a null ptr deref during journal replay --- .bcachefs_revision | 2 +- libbcachefs/btree_iter.c | 13 ++-- libbcachefs/btree_iter.h | 31 ++++++++- libbcachefs/btree_types.h | 1 + libbcachefs/btree_update_interior.c | 99 +++++++++++++++-------------- libbcachefs/btree_update_interior.h | 4 ++ libbcachefs/btree_update_leaf.c | 2 +- libbcachefs/extents.c | 15 +++++ libbcachefs/extents.h | 5 +- libbcachefs/fs-io.c | 11 ++-- libbcachefs/fs.c | 7 +- libbcachefs/recovery.c | 7 +- 12 files changed, 123 insertions(+), 74 deletions(-) diff --git a/.bcachefs_revision b/.bcachefs_revision index de5c4792..b28d5986 100644 --- a/.bcachefs_revision +++ b/.bcachefs_revision @@ -1 +1 @@ -a897b0f199b2fb888f5885f115306759199094dd +7bfc741f64204731ceeabf3061c0470613c67e86 diff --git a/libbcachefs/btree_iter.c b/libbcachefs/btree_iter.c index 7345fec8..5528ba0f 100644 --- a/libbcachefs/btree_iter.c +++ b/libbcachefs/btree_iter.c @@ -1912,13 +1912,14 @@ static struct btree_iter *btree_trans_iter_alloc(struct btree_trans *trans) struct btree_iter *iter; trans_for_each_iter(trans, iter) { - pr_err("iter: btree %s pos %llu:%llu%s%s%s", + pr_err("iter: btree %s pos %llu:%llu%s%s%s %pf", bch2_btree_ids[iter->btree_id], iter->pos.inode, iter->pos.offset, (trans->iters_live & (1ULL << iter->idx)) ? " live" : "", (trans->iters_touched & (1ULL << iter->idx)) ? " touched" : "", - iter->flags & BTREE_ITER_KEEP_UNTIL_COMMIT ? " keep" : ""); + iter->flags & BTREE_ITER_KEEP_UNTIL_COMMIT ? " keep" : "", + (void *) iter->ip_allocated); } panic("trans iter oveflow\n"); @@ -2025,9 +2026,9 @@ static struct btree_iter *__btree_trans_get_iter(struct btree_trans *trans, return iter; } -struct btree_iter *bch2_trans_get_iter(struct btree_trans *trans, - enum btree_id btree_id, - struct bpos pos, unsigned flags) +struct btree_iter *__bch2_trans_get_iter(struct btree_trans *trans, + enum btree_id btree_id, + struct bpos pos, unsigned flags) { struct btree_iter *iter = __btree_trans_get_iter(trans, btree_id, pos, flags); @@ -2064,7 +2065,7 @@ struct btree_iter *bch2_trans_get_node_iter(struct btree_trans *trans, return iter; } -struct btree_iter *bch2_trans_copy_iter(struct btree_trans *trans, +struct btree_iter *__bch2_trans_copy_iter(struct btree_trans *trans, struct btree_iter *src) { struct btree_iter *iter; diff --git a/libbcachefs/btree_iter.h b/libbcachefs/btree_iter.h index 1a3672a2..6456787a 100644 --- a/libbcachefs/btree_iter.h +++ b/libbcachefs/btree_iter.h @@ -257,10 +257,35 @@ int bch2_trans_iter_free(struct btree_trans *, struct btree_iter *); void bch2_trans_unlink_iters(struct btree_trans *); -struct btree_iter *bch2_trans_get_iter(struct btree_trans *, enum btree_id, - struct bpos, unsigned); -struct btree_iter *bch2_trans_copy_iter(struct btree_trans *, +struct btree_iter *__bch2_trans_get_iter(struct btree_trans *, enum btree_id, + struct bpos, unsigned); + +static inline struct btree_iter * +bch2_trans_get_iter(struct btree_trans *trans, enum btree_id btree_id, + struct bpos pos, unsigned flags) +{ + struct btree_iter *iter = + __bch2_trans_get_iter(trans, btree_id, pos, flags); + + if (!IS_ERR(iter)) + iter->ip_allocated = _THIS_IP_; + return iter; +} + +struct btree_iter *__bch2_trans_copy_iter(struct btree_trans *, struct btree_iter *); +static inline struct btree_iter * +bch2_trans_copy_iter(struct btree_trans *trans, struct btree_iter *src) +{ + struct btree_iter *iter = + __bch2_trans_copy_iter(trans, src); + + if (!IS_ERR(iter)) + iter->ip_allocated = _THIS_IP_; + return iter; + +} + struct btree_iter *bch2_trans_get_node_iter(struct btree_trans *, enum btree_id, struct bpos, unsigned, unsigned, unsigned); diff --git a/libbcachefs/btree_types.h b/libbcachefs/btree_types.h index e2649503..732cdc35 100644 --- a/libbcachefs/btree_types.h +++ b/libbcachefs/btree_types.h @@ -253,6 +253,7 @@ struct btree_iter { * bch2_btree_iter_next_slot() can correctly advance pos. */ struct bkey k; + unsigned long ip_allocated; }; static inline enum btree_iter_type btree_iter_type(struct btree_iter *iter) diff --git a/libbcachefs/btree_update_interior.c b/libbcachefs/btree_update_interior.c index c4235f72..f1b72e58 100644 --- a/libbcachefs/btree_update_interior.c +++ b/libbcachefs/btree_update_interior.c @@ -27,43 +27,37 @@ static void btree_update_drop_new_node(struct bch_fs *, struct btree *); /* Debug code: */ +/* + * Verify that child nodes correctly span parent node's range: + */ static void btree_node_interior_verify(struct btree *b) { +#ifdef CONFIG_BCACHEFS_DEBUG + struct bpos next_node = b->data->min_key; struct btree_node_iter iter; - struct bkey_packed *k; + struct bkey_s_c k; + struct bkey_s_c_btree_ptr_v2 bp; + struct bkey unpacked; BUG_ON(!b->level); - bch2_btree_node_iter_init(&iter, b, &b->key.k.p); -#if 1 - BUG_ON(!(k = bch2_btree_node_iter_peek(&iter, b)) || - bkey_cmp_left_packed(b, k, &b->key.k.p)); + bch2_btree_node_iter_init_from_start(&iter, b); - BUG_ON((bch2_btree_node_iter_advance(&iter, b), - !bch2_btree_node_iter_end(&iter))); -#else - const char *msg; + while (1) { + k = bch2_btree_node_iter_peek_unpack(&iter, b, &unpacked); + bp = bkey_s_c_to_btree_ptr_v2(k); - msg = "not found"; - k = bch2_btree_node_iter_peek(&iter, b); - if (!k) - goto err; + BUG_ON(bkey_cmp(next_node, bp.v->min_key)); - msg = "isn't what it should be"; - if (bkey_cmp_left_packed(b, k, &b->key.k.p)) - goto err; + bch2_btree_node_iter_advance(&iter, b); - bch2_btree_node_iter_advance(&iter, b); + if (bch2_btree_node_iter_end(&iter)) { + BUG_ON(bkey_cmp(k.k->p, b->key.k.p)); + break; + } - msg = "isn't last key"; - if (!bch2_btree_node_iter_end(&iter)) - goto err; - return; -err: - bch2_dump_btree_node(b); - printk(KERN_ERR "last key %llu:%llu %s\n", b->key.k.p.inode, - b->key.k.p.offset, msg); - BUG(); + next_node = bkey_successor(k.k->p); + } #endif } @@ -644,8 +638,6 @@ static void btree_update_nodes_written(struct closure *cl) struct bch_fs *c = as->c; struct btree *b; struct bset *i; - struct bkey_i *k; - unsigned journal_u64s = 0; int ret; /* @@ -674,13 +666,7 @@ again: list_del(&as->unwritten_list); - journal_u64s = 0; - - if (as->mode != BTREE_INTERIOR_UPDATING_ROOT) - for_each_keylist_key(&as->parent_keys, k) - journal_u64s += jset_u64s(k->k.u64s); - - ret = bch2_journal_res_get(&c->journal, &res, journal_u64s, + ret = bch2_journal_res_get(&c->journal, &res, as->journal_u64s, JOURNAL_RES_GET_RESERVED); if (ret) { BUG_ON(!bch2_journal_error(&c->journal)); @@ -688,13 +674,14 @@ again: goto free_update; } - if (as->mode != BTREE_INTERIOR_UPDATING_ROOT) - for_each_keylist_key(&as->parent_keys, k) - bch2_journal_add_entry(&c->journal, &res, - BCH_JSET_ENTRY_btree_keys, - as->btree_id, - as->level, - k, k->k.u64s); + { + struct journal_buf *buf = &c->journal.buf[res.idx]; + struct jset_entry *entry = vstruct_idx(buf->data, res.offset); + + res.offset += as->journal_u64s; + res.u64s -= as->journal_u64s; + memcpy_u64s(entry, as->journal_entries, as->journal_u64s); + } switch (as->mode) { case BTREE_INTERIOR_NO_UPDATE: @@ -983,7 +970,7 @@ bch2_btree_update_start(struct bch_fs *c, enum btree_id id, bch2_keylist_init(&as->parent_keys, as->inline_keys); ret = bch2_journal_preres_get(&c->journal, &as->journal_preres, - jset_u64s(BKEY_BTREE_PTR_U64s_MAX) * 3, 0); + ARRAY_SIZE(as->journal_entries), 0); if (ret) { bch2_btree_reserve_put(c, reserve); closure_debug_destroy(&as->cl); @@ -1103,10 +1090,21 @@ static void bch2_insert_fixup_btree_ptr(struct btree_update *as, struct btree *b { struct bch_fs *c = as->c; struct bch_fs_usage *fs_usage; + struct jset_entry *entry; struct bkey_packed *k; struct bkey tmp; - BUG_ON(insert->k.u64s > bch_btree_keys_u64s_remaining(c, b)); + BUG_ON(as->journal_u64s + jset_u64s(insert->k.u64s) > + ARRAY_SIZE(as->journal_entries)); + + entry = (void *) &as->journal_entries[as->journal_u64s]; + memset(entry, 0, sizeof(*entry)); + entry->u64s = cpu_to_le16(insert->k.u64s); + entry->type = BCH_JSET_ENTRY_btree_keys; + entry->btree_id = b->btree_id; + entry->level = b->level; + memcpy_u64s_small(entry->_data, insert, insert->k.u64s); + as->journal_u64s += jset_u64s(insert->k.u64s); mutex_lock(&c->btree_interior_update_lock); percpu_down_read(&c->mark_lock); @@ -1255,6 +1253,14 @@ static void btree_split_insert_keys(struct btree_update *as, struct btree *b, struct bkey_packed *src, *dst, *n; struct bset *i; + /* + * XXX + * + * these updates must be journalled + * + * oops + */ + BUG_ON(btree_node_type(b) != BKEY_TYPE_BTREE); bch2_btree_node_iter_init(&node_iter, b, &k->k.p); @@ -1262,11 +1268,6 @@ static void btree_split_insert_keys(struct btree_update *as, struct btree *b, while (!bch2_keylist_empty(keys)) { k = bch2_keylist_front(keys); - BUG_ON(bch_keylist_u64s(keys) > - bch_btree_keys_u64s_remaining(as->c, b)); - BUG_ON(bkey_cmp(k->k.p, b->data->min_key) < 0); - BUG_ON(bkey_cmp(k->k.p, b->data->max_key) > 0); - bch2_insert_fixup_btree_ptr(as, b, iter, k, &node_iter); bch2_keylist_pop_front(keys); } diff --git a/libbcachefs/btree_update_interior.h b/libbcachefs/btree_update_interior.h index 0ac95dd8..aef8adf8 100644 --- a/libbcachefs/btree_update_interior.h +++ b/libbcachefs/btree_update_interior.h @@ -104,6 +104,10 @@ struct btree_update { struct btree *new_nodes[BTREE_MAX_DEPTH * 2 + GC_MERGE_NODES]; unsigned nr_new_nodes; + unsigned journal_u64s; + u64 journal_entries[ + (BKEY_BTREE_PTR_U64s_MAX + 1) * (BTREE_MAX_DEPTH - 1) * 2]; + /* Only here to reduce stack usage on recursive splits: */ struct keylist parent_keys; /* diff --git a/libbcachefs/btree_update_leaf.c b/libbcachefs/btree_update_leaf.c index da2b93b5..b305c2e4 100644 --- a/libbcachefs/btree_update_leaf.c +++ b/libbcachefs/btree_update_leaf.c @@ -309,7 +309,7 @@ btree_key_can_insert(struct btree_trans *trans, if (unlikely(btree_node_old_extent_overwrite(b))) return BTREE_INSERT_BTREE_NODE_FULL; - ret = !btree_node_is_extents(b) + ret = !(iter->flags & BTREE_ITER_IS_EXTENTS) ? BTREE_INSERT_OK : bch2_extent_can_insert(trans, iter, insert); if (ret) diff --git a/libbcachefs/extents.c b/libbcachefs/extents.c index 792c9c1e..3f66457d 100644 --- a/libbcachefs/extents.c +++ b/libbcachefs/extents.c @@ -215,6 +215,21 @@ void bch2_btree_ptr_to_text(struct printbuf *out, struct bch_fs *c, bch2_bkey_ptrs_to_text(out, c, k); } +void bch2_btree_ptr_v2_to_text(struct printbuf *out, struct bch_fs *c, + struct bkey_s_c k) +{ + struct bkey_s_c_btree_ptr_v2 bp = bkey_s_c_to_btree_ptr_v2(k); + + pr_buf(out, "seq %llu sectors %u written %u min_key ", + le64_to_cpu(bp.v->seq), + le16_to_cpu(bp.v->sectors), + le16_to_cpu(bp.v->sectors_written)); + + bch2_bpos_to_text(out, bp.v->min_key); + pr_buf(out, " "); + bch2_bkey_ptrs_to_text(out, c, k); +} + void bch2_btree_ptr_v2_compat(enum btree_id btree_id, unsigned version, unsigned big_endian, int write, struct bkey_s k) diff --git a/libbcachefs/extents.h b/libbcachefs/extents.h index 8ff2eac3..29b15365 100644 --- a/libbcachefs/extents.h +++ b/libbcachefs/extents.h @@ -371,6 +371,9 @@ const char *bch2_btree_ptr_invalid(const struct bch_fs *, struct bkey_s_c); void bch2_btree_ptr_debugcheck(struct bch_fs *, struct bkey_s_c); void bch2_btree_ptr_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); + +void bch2_btree_ptr_v2_to_text(struct printbuf *, struct bch_fs *, + struct bkey_s_c); void bch2_btree_ptr_v2_compat(enum btree_id, unsigned, unsigned, int, struct bkey_s); @@ -384,7 +387,7 @@ void bch2_btree_ptr_v2_compat(enum btree_id, unsigned, unsigned, #define bch2_bkey_ops_btree_ptr_v2 (struct bkey_ops) { \ .key_invalid = bch2_btree_ptr_invalid, \ .key_debugcheck = bch2_btree_ptr_debugcheck, \ - .val_to_text = bch2_btree_ptr_to_text, \ + .val_to_text = bch2_btree_ptr_v2_to_text, \ .swab = bch2_ptr_swab, \ .compat = bch2_btree_ptr_v2_compat, \ } diff --git a/libbcachefs/fs-io.c b/libbcachefs/fs-io.c index ec46be3b..0aa3afad 100644 --- a/libbcachefs/fs-io.c +++ b/libbcachefs/fs-io.c @@ -2512,10 +2512,8 @@ reassemble: bkey_on_stack_reassemble(©, c, k); if (insert && - bkey_cmp(bkey_start_pos(k.k), move_pos) < 0) { + bkey_cmp(bkey_start_pos(k.k), move_pos) < 0) bch2_cut_front(move_pos, copy.k); - bch2_btree_iter_set_pos(src, bkey_start_pos(©.k->k)); - } copy.k->k.p.offset += shift >> 9; bch2_btree_iter_set_pos(dst, bkey_start_pos(©.k->k)); @@ -2535,8 +2533,9 @@ reassemble: } bkey_init(&delete.k); - delete.k.p = src->pos; - bch2_key_resize(&delete.k, copy.k->k.size); + delete.k.p = copy.k->k.p; + delete.k.size = copy.k->k.size; + delete.k.p.offset -= shift >> 9; next_pos = insert ? bkey_start_pos(&delete.k) : delete.k.p; @@ -2557,6 +2556,8 @@ reassemble: BUG_ON(ret); } + bch2_btree_iter_set_pos(src, bkey_start_pos(&delete.k)); + ret = bch2_trans_update(&trans, src, &delete, trigger_flags) ?: bch2_trans_update(&trans, dst, copy.k, trigger_flags) ?: bch2_trans_commit(&trans, &disk_res, diff --git a/libbcachefs/fs.c b/libbcachefs/fs.c index f14f8805..1c89a1b2 100644 --- a/libbcachefs/fs.c +++ b/libbcachefs/fs.c @@ -142,8 +142,6 @@ retry: &inode->ei_journal_seq, BTREE_INSERT_NOUNLOCK| BTREE_INSERT_NOFAIL); - if (ret == -EINTR) - goto retry; /* * the btree node lock protects inode->ei_inode, not ei_update_lock; @@ -152,6 +150,11 @@ retry: if (!ret) bch2_inode_update_after_write(c, inode, &inode_u, fields); + bch2_trans_iter_put(&trans, iter); + + if (ret == -EINTR) + goto retry; + bch2_trans_exit(&trans); return ret < 0 ? ret : 0; } diff --git a/libbcachefs/recovery.c b/libbcachefs/recovery.c index 8cfae639..a4d0eec2 100644 --- a/libbcachefs/recovery.c +++ b/libbcachefs/recovery.c @@ -183,17 +183,12 @@ void bch2_btree_and_journal_iter_init_node_iter(struct btree_and_journal_iter *i struct journal_keys *journal_keys, struct btree *b) { - struct bpos start = b->data->min_key; - - if (btree_node_type_is_extents(b->btree_id)) - start = bkey_successor(start); - memset(iter, 0, sizeof(*iter)); iter->b = b; bch2_btree_node_iter_init_from_start(&iter->node_iter, iter->b); bch2_journal_iter_init(&iter->journal, journal_keys, - b->btree_id, b->level, start); + b->btree_id, b->level, b->data->min_key); } /* sort and dedup all keys in the journal: */