diff_cb() does not handle large dnodes

Trying to 'zfs diff' a snapshot with large dnodes will incorrectly try
to access its interior slots when dnodesize > sizeof(dnode_phys_t).
This is normally not an issue because the interior slots are
zero-filled, which report_dnode() handles calling
report_free_dnode_range(). However this is not the case for encrypted
large dnodes or filesystem using many SA based xattrs where the extra
data past the legacy dnode size boundary is interpreted as a
dnode_phys_t.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Tom Caputi <tcaputi@datto.com>
Reviewed-by: Ryan Moeller <ryan@ixsystems.com>
Signed-off-by: loli10K <ezomori.nozomu@gmail.com>
Closes #7678
Closes #8931
Closes #9343
This commit is contained in:
loli10K
2019-09-24 21:01:37 +02:00
committed by Tony Hutter
parent 8498a2f3f8
commit ec5d76e853
2 changed files with 15 additions and 4 deletions
+3 -2
View File
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
* Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
*/
#include <sys/dmu.h>
@@ -130,7 +131,7 @@ diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
dnode_phys_t *blk;
arc_buf_t *abuf;
arc_flags_t aflags = ARC_FLAG_WAIT;
int blksz = BP_GET_LSIZE(bp);
int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT;
int zio_flags = ZIO_FLAG_CANFAIL;
int i;
@@ -142,7 +143,7 @@ diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
return (SET_ERROR(EIO));
blk = abuf->b_data;
for (i = 0; i < blksz >> DNODE_SHIFT; i++) {
for (i = 0; i < epb; i += blk[i].dn_extra_slots + 1) {
uint64_t dnobj = (zb->zb_blkid <<
(DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i;
err = report_dnode(da, dnobj, blk+i);