From 73aac4aa410121ddcc08bd7fd15d987caf101584 Mon Sep 17 00:00:00 2001 From: Ned Bass Date: Tue, 25 Jul 2017 18:52:40 -0700 Subject: [PATCH] Some additional send stream validity checking Check in the DMU whether an object record in a send stream being received contains an unsupported dnode slot count, and return an error if it does. Failure to catch an unsupported dnode slot count would result in a panic when the SPA attempts to increment the reference count for the large_dnode feature and the pool has the feature disabled. This is not normally an issue for a well-formed send stream which would have the DMU_BACKUP_FEATURE_LARGE_DNODE flag set if it contains large dnodes, so it will be rejected as unsupported if the required feature is disabled. This change adds a missing object record field validation. Add missing stream feature flag checks in dmu_recv_resume_begin_check(). Consolidate repetitive comment blocks in dmu_recv_begin_check(). Update zstreamdump to print the dnode slot count (dn_slots) for an object record when running in verbose mode. Reviewed-by: Brian Behlendorf Reviewed-by: Giuseppe Di Natale Reviewed by: Matthew Ahrens Signed-off-by: Ned Bass Closes #6396 --- cmd/zstreamdump/zstreamdump.c | 6 ++++-- module/zfs/dmu_send.c | 26 ++++++++++++++++++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/cmd/zstreamdump/zstreamdump.c b/cmd/zstreamdump/zstreamdump.c index b2f7bfdff..2fe95dfb6 100644 --- a/cmd/zstreamdump/zstreamdump.c +++ b/cmd/zstreamdump/zstreamdump.c @@ -422,12 +422,14 @@ main(int argc, char *argv[]) } if (verbose) { (void) printf("OBJECT object = %llu type = %u " - "bonustype = %u blksz = %u bonuslen = %u\n", + "bonustype = %u blksz = %u bonuslen = %u " + "dn_slots = %u\n", (u_longlong_t)drro->drr_object, drro->drr_type, drro->drr_bonustype, drro->drr_blksz, - drro->drr_bonuslen); + drro->drr_bonuslen, + drro->drr_dn_slots); } if (drro->drr_bonuslen > 0) { (void) ssread(buf, diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c index 7162355d4..eb27b5e71 100644 --- a/module/zfs/dmu_send.c +++ b/module/zfs/dmu_send.c @@ -1428,17 +1428,12 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx) /* * The receiving code doesn't know how to translate large blocks * to smaller ones, so the pool must have the LARGE_BLOCKS - * feature enabled if the stream has LARGE_BLOCKS. + * feature enabled if the stream has LARGE_BLOCKS. Same with + * large dnodes. */ if ((featureflags & DMU_BACKUP_FEATURE_LARGE_BLOCKS) && !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_LARGE_BLOCKS)) return (SET_ERROR(ENOTSUP)); - - /* - * The receiving code doesn't know how to translate large dnodes - * to smaller ones, so the pool must have the LARGE_DNODE - * feature enabled if the stream has LARGE_DNODE. - */ if ((featureflags & DMU_BACKUP_FEATURE_LARGE_DNODE) && !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_LARGE_DNODE)) return (SET_ERROR(ENOTSUP)); @@ -1678,6 +1673,19 @@ dmu_recv_resume_begin_check(void *arg, dmu_tx_t *tx) !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_LZ4_COMPRESS)) return (SET_ERROR(ENOTSUP)); + /* + * The receiving code doesn't know how to translate large blocks + * to smaller ones, so the pool must have the LARGE_BLOCKS + * feature enabled if the stream has LARGE_BLOCKS. Same with + * large dnodes. + */ + if ((featureflags & DMU_BACKUP_FEATURE_LARGE_BLOCKS) && + !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_LARGE_BLOCKS)) + return (SET_ERROR(ENOTSUP)); + if ((featureflags & DMU_BACKUP_FEATURE_LARGE_DNODE) && + !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_LARGE_DNODE)) + return (SET_ERROR(ENOTSUP)); + (void) snprintf(recvname, sizeof (recvname), "%s/%s", tofs, recv_clone_name); @@ -2121,7 +2129,9 @@ receive_object(struct receive_writer_arg *rwa, struct drr_object *drro, drro->drr_blksz < SPA_MINBLOCKSIZE || drro->drr_blksz > spa_maxblocksize(dmu_objset_spa(rwa->os)) || drro->drr_bonuslen > - DN_BONUS_SIZE(spa_maxdnodesize(dmu_objset_spa(rwa->os)))) { + DN_BONUS_SIZE(spa_maxdnodesize(dmu_objset_spa(rwa->os))) || + drro->drr_dn_slots > + (spa_maxdnodesize(dmu_objset_spa(rwa->os)) >> DNODE_SHIFT)) { return (SET_ERROR(EINVAL)); }