OpenZFS 9443 - panic when scrub a v10 pool
authorMatthew Ahrens <mahrens@delphix.com>
Mon, 5 Feb 2018 18:06:18 +0000 (10:06 -0800)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 4 May 2018 17:47:10 +0000 (10:47 -0700)
While expanding stored pools, we ran into a panic using an old pool.

Steps to reproduce:

    $ sudo zpool create -o version=2 test c2t1d0
    $ sudo cp /etc/passwd /test/foo
    $ sudo zpool attach test c2t1d0 c2t2d0

We'll get this panic:

    ffffff000fc0e5e0 unix:real_mode_stop_cpu_stage2_end+b27c ()
    ffffff000fc0e6f0 unix:trap+dc8 ()
    ffffff000fc0e700 unix:cmntrap+e6 ()
    ffffff000fc0e860 zfs:dsl_scan_visitds+1ff ()
    ffffff000fc0ea20 zfs:dsl_scan_visit+fe ()
    ffffff000fc0ea80 zfs:dsl_scan_sync+1b3 ()
    ffffff000fc0eb60 zfs:spa_sync+435 ()
    ffffff000fc0ec20 zfs:txg_sync_thread+23f ()
    ffffff000fc0ec30 unix:thread_start+8 ()

The problem is a bad trap accessing a NULL pointer. We're looking for
the dp_origin_snap of a dsl_pool_t, but version 2 didn't have that. The
system will go into a reboot loop at this point, and the dump won't be
accessible except by removing the cache file from within the recovery
environment.

This impacts any sort of scrub or resilver on version <11 pools, e.g.:

    $ zpool create -o version=10 test c2t1d0
    $ zpool scrub test

Authored by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Serapheim Dimitropoulos <serapheim.dimitro@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Andriy Gapon <avg@FreeBSD.org>
Reviewed by: Igor Kozhukhov <igor@dilos.org>
Approved by: Dan McDonald <danmcd@joyent.com>
Ported-by: Brian Behlendorf <behlendorf1@llnl.gov>
OpenZFS-issue: https://www.illumos.org/issues/9443
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/010eed29
Closes #7501

module/zfs/dsl_scan.c

index b87b4d5558f5cbc2c80b73a47c12c83318b72eba..c19a1b75cd64e309d1855ee15c868c185a14519d 100644 (file)
@@ -2166,7 +2166,8 @@ dsl_scan_visitds(dsl_scan_t *scn, uint64_t dsobj, dmu_tx_t *tx)
         * block-sharing rules don't apply to it.
         */
        if (!dsl_dataset_is_snapshot(ds) &&
-           ds->ds_dir != dp->dp_origin_snap->ds_dir) {
+           (dp->dp_origin_snap == NULL ||
+           ds->ds_dir != dp->dp_origin_snap->ds_dir)) {
                objset_t *os;
                if (dmu_objset_from_ds(ds, &os) != 0) {
                        goto out;