1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-01-16 15:11:52 +00:00

9439 ZFS double-free due to failure to dirty indirect block

illumos/illumos-gate@99a19144e8

Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Paul Dagnelie <pcd@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Author:     Matthew Ahrens <mahrens@delphix.com>
This commit is contained in:
Alexander Motin 2018-08-02 23:45:24 +00:00
parent 1ca9a2427b
commit 1bcb271c93
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor-sys/illumos/dist/; revision=337204
2 changed files with 20 additions and 4 deletions

View File

@ -1605,13 +1605,11 @@ dnode_free_range(dnode_t *dn, uint64_t off, uint64_t len, dmu_tx_t *tx)
if (off == 0 && len >= blksz) {
/*
* Freeing the whole block; fast-track this request.
* Note that we won't dirty any indirect blocks,
* which is fine because we will be freeing the entire
* file and thus all indirect blocks will be freed
* by free_children().
*/
blkid = 0;
nblks = 1;
if (dn->dn_nlevels > 1)
dnode_dirty_l1(dn, 0, tx);
goto done;
} else if (off >= blksz) {
/* Freeing past end-of-data */

View File

@ -263,6 +263,24 @@ free_children(dmu_buf_impl_t *db, uint64_t blkid, uint64_t nblks,
if (db->db_state != DB_CACHED)
(void) dbuf_read(db, NULL, DB_RF_MUST_SUCCEED);
/*
* If we modify this indirect block, and we are not freeing the
* dnode (!free_indirects), then this indirect block needs to get
* written to disk by dbuf_write(). If it is dirty, we know it will
* be written (otherwise, we would have incorrect on-disk state
* because the space would be freed but still referenced by the BP
* in this indirect block). Therefore we VERIFY that it is
* dirty.
*
* Our VERIFY covers some cases that do not actually have to be
* dirty, but the open-context code happens to dirty. E.g. if the
* blocks we are freeing are all holes, because in that case, we
* are only freeing part of this indirect block, so it is an
* ancestor of the first or last block to be freed. The first and
* last L1 indirect blocks are always dirtied by dnode_free_range().
*/
VERIFY(BP_GET_FILL(db->db_blkptr) == 0 || db->db_dirtycnt > 0);
dbuf_release_bp(db);
bp = db->db.db_data;