--- old/usr/src/uts/common/fs/nfs/nfs3_srv.c	Tue Oct 15 13:53:54 2019
+++ new/usr/src/uts/common/fs/nfs/nfs3_srv.c	Tue Oct 15 13:53:54 2019
@@ -411,6 +411,7 @@
 	 * location of the public filehandle.
 	 */
 	if (exi != NULL && (exi->exi_export.ex_flags & EX_PUBLIC)) {
+		ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
 		dvp = ZONE_ROOTVP();
 		VN_HOLD(dvp);
 
@@ -479,6 +480,7 @@
 		publicfh_flag = TRUE;
 
 		exi_rele(exi);
+		exi = NULL;
 
 		error = rfs_publicfh_mclookup(name, dvp, cr, &vp,
 		    &exi, &sec);
@@ -562,7 +564,6 @@
 	va.va_mask = AT_ALL;
 	vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
 
-	exi_rele(exi);
 	VN_RELE(vp);
 
 	resp->status = NFS3_OK;
@@ -581,6 +582,7 @@
 	    cred_t *, cr, vnode_t *, dvp, struct exportinfo *, exi,
 	    LOOKUP3res *, resp);
 	VN_RELE(dvp);
+	exi_rele(exi);
 
 	return;
 
@@ -2674,6 +2676,7 @@
 		goto err1;
 	}
 
+	ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
 	error = VOP_RMDIR(vp, name, ZONE_ROOTVP(), cr, NULL, 0);
 
 	if (name != args->object.name)
--- old/usr/src/uts/common/fs/nfs/nfs4_srv.c	Tue Oct 15 13:53:54 2019
+++ new/usr/src/uts/common/fs/nfs/nfs4_srv.c	Tue Oct 15 13:53:54 2019
@@ -953,7 +953,7 @@
 {
 	int error, different_export = 0;
 	vnode_t *dvp, *vp;
-	struct exportinfo *exi = NULL;
+	struct exportinfo *exi;
 	fid_t fid;
 	uint_t count, i;
 	secinfo4 *resok_val;
@@ -964,6 +964,8 @@
 	nfs_export_t *ne = nfs_get_export();
 
 	dvp = cs->vp;
+	exi = cs->exi;
+	ASSERT(exi != NULL);
 	dotdot = (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0');
 
 	/*
@@ -971,13 +973,13 @@
 	 * root of a filesystem, or above an export point.
 	 */
 	if (dotdot) {
-
+		ASSERT3U(exi->exi_zoneid, ==, curzone->zone_id);
 		/*
 		 * If dotdotting at the root of a filesystem, then
 		 * need to traverse back to the mounted-on filesystem
 		 * and do the dotdot lookup there.
 		 */
-		if ((cs->vp->v_flag & VROOT) || VN_IS_CURZONEROOT(cs->vp)) {
+		if ((dvp->v_flag & VROOT) || VN_IS_CURZONEROOT(dvp)) {
 
 			/*
 			 * If at the system root, then can
@@ -989,7 +991,7 @@
 			/*
 			 * Traverse back to the mounted-on filesystem
 			 */
-			dvp = untraverse(cs->vp);
+			dvp = untraverse(dvp);
 
 			/*
 			 * Set the different_export flag so we remember
@@ -1003,7 +1005,7 @@
 			 * If dotdotting above an export point then set
 			 * the different_export to get new export info.
 			 */
-			different_export = nfs_exported(cs->exi, cs->vp);
+			different_export = nfs_exported(exi, dvp);
 		}
 	}
 
@@ -1022,9 +1024,9 @@
 	 * check whether this vnode is visible.
 	 */
 	if (!different_export &&
-	    (PSEUDO(cs->exi) || ! is_exported_sec(cs->nfsflavor, cs->exi) ||
+	    (PSEUDO(exi) || !is_exported_sec(cs->nfsflavor, exi) ||
 	    cs->access & CS_ACCESS_LIMITED)) {
-		if (! nfs_visible(cs->exi, vp, &different_export)) {
+		if (! nfs_visible(exi, vp, &different_export)) {
 			VN_RELE(vp);
 			return (puterrno4(ENOENT));
 		}
@@ -1066,6 +1068,7 @@
 			return (puterrno4(error));
 		}
 
+		/* We'll need to reassign "exi". */
 		if (dotdot)
 			exi = nfs_vptoexi(NULL, vp, cs->cr, &walk, NULL, TRUE);
 		else
@@ -1086,8 +1089,6 @@
 				return (puterrno4(EACCES));
 			}
 		}
-	} else {
-		exi = cs->exi;
 	}
 	ASSERT(exi != NULL);
 
@@ -2718,7 +2719,8 @@
 	 * export point.
 	 */
 	if (dotdot) {
-
+		ASSERT(cs->exi != NULL);
+		ASSERT3U(cs->exi->exi_zoneid, ==, curzone->zone_id);
 		/*
 		 * If dotdotting at the root of a filesystem, then
 		 * need to traverse back to the mounted-on filesystem
--- old/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c	Tue Oct 15 13:53:54 2019
+++ new/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c	Tue Oct 15 13:53:54 2019
@@ -135,7 +135,12 @@
 	struct statvfs64 sb;
 
 	rfs4_init_compound_state(&cs);
-	cs.vp = ZONE_ROOTVP();
+	/*
+	 * This is global state checking, called once. We might be in
+	 * non-global-zone context here (say a modload happens from a zone
+	 * process) so in this case, we want the global-zone root vnode.
+	 */
+	cs.vp = rootvp;
 	cs.fh.nfs_fh4_val = NULL;
 	cs.cr = kcred;
 
@@ -1303,7 +1308,14 @@
 	vp = sarg->cs->vp;
 	sarg->mntdfid_set = FALSE;
 
-	/* VROOT object or zone's root, must untraverse */
+	/*
+	 * VROOT object or zone's root, must untraverse.
+	 *
+	 * NOTE: Not doing reality checks on curzone vs. compound
+	 * state vnode because it will mismatch once at initialization
+	 * if a non-global-zone triggers the module load, BUT in that case
+	 * the vp is literally "/" which has VROOT set.
+	 */
 	if ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp)) {
 
 		/* extra hold for vp since untraverse might rele */
@@ -1318,7 +1330,7 @@
 		 * another getattr -- just use the one in sarg.
 		 */
 		if (VN_CMP(vp, stubvp)) {
-			ASSERT(VN_CMP(vp, ZONE_ROOTVP()));
+			ASSERT(VN_IS_CURZONEROOT(vp));
 			vap = sarg->vap;
 		} else {
 			va.va_mask = AT_NODEID;
--- old/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c	Tue Oct 15 13:53:55 2019
+++ new/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c	Tue Oct 15 13:53:55 2019
@@ -166,10 +166,11 @@
 	exi->exi_visible = vis_head;
 	exi->exi_count = 1;
 	/* Caller will set exi_zone... */
+	/* XXX KEBE SAYS Uncomment me or fix in the caller */
+	/* exi->exi_zoneid = ne->ne_globals->nfs_zoneid; */
 	exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag &
 	    VSW_VOLATILEDEV) ? 1 : 0;
 	mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL);
-	exi->exi_zoneid = ne->ne_globals->nfs_zoneid;
 
 	/*
 	 * Build up the template fhandle
@@ -660,6 +661,7 @@
 		if (error)
 			break;
 
+		ASSERT3U(exip->exi_zoneid, ==, curzone->zone_id);
 		/*
 		 * The root of the file system, or the zone's root for
 		 * in-zone NFS service needs special handling
@@ -696,6 +698,7 @@
 				 */
 				new_exi = pseudo_exportfs(ne, vp, &fid,
 				    vis_head, NULL);
+				/* XXX KEBE SAYS NUKE ME */
 				new_exi->exi_zone = exip->exi_zone;
 				vis_head = NULL;
 			}
@@ -880,6 +883,7 @@
 			mutex_exit(&nfs_exi_id_lock);
 			export_unlink(ne, tnode->tree_exi);
 			exi_rele(tnode->tree_exi);
+			tnode->tree_exi = NULL;
 		}
 
 		/* Release visible in parent's exportinfo */
@@ -907,6 +911,8 @@
  * Traverse backward across mountpoint from the
  * root vnode of a filesystem to its mounted-on
  * vnode.
+ *
+ * Callers to this function have confirmed the use of curzone is safe here.
  */
 vnode_t *
 untraverse(vnode_t *vp)
@@ -963,7 +969,7 @@
  *
  * If d is shared, then c will be put into a's visible list.
  * Note: visible list is per filesystem and is attached to the
- * VROOT exportinfo.
+ * VROOT exportinfo.  Returned exi does NOT have a new hold.
  */
 struct exportinfo *
 get_root_export(struct exportinfo *exip)
--- old/usr/src/uts/common/fs/nfs/nfs_export.c	Tue Oct 15 13:53:55 2019
+++ new/usr/src/uts/common/fs/nfs/nfs_export.c	Tue Oct 15 13:53:55 2019
@@ -719,7 +719,7 @@
  * Add or remove the newly exported or unexported security flavors of the
  * given exportinfo from its ancestors upto the system root.
  */
-void
+static void
 srv_secinfo_treeclimb(nfs_export_t *ne, exportinfo_t *exip, secinfo_t *sec,
     int seccnt, bool_t isadd)
 {
@@ -726,6 +726,7 @@
 	treenode_t *tnode;
 
 	ASSERT(RW_WRITE_HELD(&ne->exported_lock));
+	ASSERT3U(exip->exi_zoneid, ==, curzone->zone_id);
 
 	/*
 	 * exi_tree can be null for the zone root
@@ -816,7 +817,6 @@
 	exportinfo_t **bckt;
 
 	ASSERT(RW_WRITE_HELD(&ne->exported_lock));
-	ASSERT(exi->exi_zoneid == ne->ne_globals->nfs_zoneid);
 
 	bckt = &ne->exptable[exptablehash(&exi->exi_fsid, &exi->exi_fid)];
 	exp_hash_link(exi, fid_hash, bckt);
@@ -900,6 +900,7 @@
 {
 	int i;
 	nfs_export_t *ne;
+	zone_t *zone;
 
 	ne = kmem_zalloc(sizeof (*ne), KM_SLEEP);
 
@@ -924,9 +925,16 @@
 	ne->exi_root->exi_count = 1;
 	mutex_init(&ne->exi_root->exi_lock, NULL, MUTEX_DEFAULT, NULL);
 
-	ASSERT(curzone->zone_id == ng->nfs_zoneid);
-	ne->exi_root->exi_vp = ZONE_ROOTVP();
-	ne->exi_root->exi_zoneid = ng->nfs_zoneid;
+	/*
+	 * Because we cannot:
+	 *	ASSERT(curzone->zone_id == ng->nfs_zoneid);
+	 * We grab the zone pointer explicitly (like netstacks do) and
+	 * set the rootvp here.
+	 */
+	zone = zone_find_by_id_nolock(ng->nfs_zoneid);
+	ne->exi_root->exi_vp = zone->zone_rootvp;
+	ne->exi_root->exi_zone = zone;	/* XXX KEBE SAYS lose me, and... */
+	/* ne->exi_root->exi_zoneid = ng->nfs_zoneid; */ /* use me instead! */
 
 	/*
 	 * Fill in ne->exi_rootfid later, in nfs_export_get_rootfid
@@ -1920,7 +1928,7 @@
 		exi->exi_visible = NULL;
 
 		/* interconnect the existing treenode with the new exportinfo */
-		newexi->exi_zone = exi->exi_zone;
+		newexi->exi_zone = exi->exi_zone; /* XXX KEBE SAYS LOSE ME */
 		newexi->exi_tree = exi->exi_tree;
 		newexi->exi_tree->tree_exi = newexi;
 
@@ -2189,6 +2197,7 @@
 		 * If we're at the root of this filesystem, then
 		 * it's time to stop (with failure).
 		 */
+		ASSERT3P(vp->v_vfsp->vfs_zone, ==, curzone);
 		if ((vp->v_flag & VROOT) || VN_IS_CURZONEROOT(vp)) {
 			error = EINVAL;
 			break;
--- old/usr/src/uts/common/fs/nfs/nfs_server.c	Tue Oct 15 13:53:55 2019
+++ new/usr/src/uts/common/fs/nfs/nfs_server.c	Tue Oct 15 13:53:55 2019
@@ -2947,11 +2947,13 @@
 			/* Release the reference on the old exi value */
 			ASSERT(*exi != NULL);
 			exi_rele(*exi);
+			*exi = NULL;
 
 			if (error = nfs_check_vpexi(mc_dvp, *vpp, kcred, exi)) {
 				VN_RELE(*vpp);
 				goto publicfh_done;
 			}
+			/* Have a new *exi */
 		}
 	}
 
@@ -2978,6 +2980,8 @@
 	struct pathname pn;
 	int error;
 
+	ASSERT3U(crgetzoneid(cr), ==, curzone->zone_id);
+
 	/*
 	 * If pathname starts with '/', then set startdvp to root.
 	 */
--- old/usr/src/uts/common/fs/nfs/nfs_srv.c	Tue Oct 15 13:53:55 2019
+++ new/usr/src/uts/common/fs/nfs/nfs_srv.c	Tue Oct 15 13:53:55 2019
@@ -535,6 +535,7 @@
 		publicfh_flag = TRUE;
 
 		exi_rele(exi);
+		exi = NULL;
 
 		error = rfs_publicfh_mclookup(name, dvp, cr, &vp, &exi,
 		    &sec);
--- old/usr/src/uts/common/sys/zone.h	Tue Oct 15 13:53:55 2019
+++ new/usr/src/uts/common/sys/zone.h	Tue Oct 15 13:53:55 2019
@@ -934,6 +934,12 @@
 
 /*
  * The root vnode of the current zone.
+ *
+ * NOTE: It may be necessary (initialization time for file sharing where an
+ * NGZ loads a file-sharing kernel module that does zsd initialization) to NOT
+ * use this macro. One should ASSERT() that curzone == active ZSD (an
+ * ASSERTion that's not always true at ZSD initialization time) during regular
+ * use of this macro.
  */
 #define	ZONE_ROOTVP()	(curzone->zone_rootvp)
 
@@ -942,6 +948,7 @@
  * (i.e. VROOT may not be set on zone->zone_rootvp) we need to not assume it.
  * This macro helps in checking if a vnode is the current zone's rootvp.
  * NOTE:  Using the VN_ prefix, even though it's defined here in zone.h.
+ * NOTE2: See above warning about ZONE_ROOTVP().
  */
 #define	VN_IS_CURZONEROOT(vp)	(VN_CMP(vp, ZONE_ROOTVP()))
 
