6905188 panic: kernel heap corruption when doing "zfs rename -r"
authorMatthew Ahrens <Matthew.Ahrens@Sun.COM>
Mon, 30 Nov 2009 11:36:36 -0800
changeset 11209 462283cb4096
parent 11208 2efe153f06fe
child 11210 646209cf7ff1
6905188 panic: kernel heap corruption when doing "zfs rename -r"
usr/src/cmd/zdb/zdb.c
usr/src/cmd/ztest/ztest.c
usr/src/uts/common/fs/zfs/dmu_objset.c
usr/src/uts/common/fs/zfs/dmu_send.c
usr/src/uts/common/fs/zfs/dsl_dataset.c
usr/src/uts/common/fs/zfs/sys/dmu.h
usr/src/uts/common/fs/zfs/sys/dmu_objset.h
usr/src/uts/common/fs/zfs/sys/dsl_dataset.h
usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h
usr/src/uts/common/fs/zfs/sys/zil.h
usr/src/uts/common/fs/zfs/zfs_ioctl.c
usr/src/uts/common/fs/zfs/zil.c
--- a/usr/src/cmd/zdb/zdb.c	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/cmd/zdb/zdb.c	Mon Nov 30 11:36:36 2009 -0800
@@ -1607,7 +1607,7 @@
 
 /*ARGSUSED*/
 static int
-dump_one_dir(char *dsname, void *arg)
+dump_one_dir(const char *dsname, void *arg)
 {
 	int error;
 	objset_t *os;
--- a/usr/src/cmd/ztest/ztest.c	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/cmd/ztest/ztest.c	Mon Nov 30 11:36:36 2009 -0800
@@ -2714,7 +2714,7 @@
 
 /* ARGSUSED */
 static int
-ztest_objset_destroy_cb(char *name, void *arg)
+ztest_objset_destroy_cb(const char *name, void *arg)
 {
 	objset_t *os;
 	dmu_object_info_t doi;
--- a/usr/src/uts/common/fs/zfs/dmu_objset.c	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/uts/common/fs/zfs/dmu_objset.c	Mon Nov 30 11:36:36 2009 -0800
@@ -779,7 +779,7 @@
 }
 
 static int
-dmu_objset_snapshot_one(char *name, void *arg)
+dmu_objset_snapshot_one(const char *name, void *arg)
 {
 	struct snaparg *sn = arg;
 	objset_t *os;
@@ -1315,7 +1315,7 @@
 }
 
 struct findarg {
-	int (*func)(char *, void *);
+	int (*func)(const char *, void *);
 	void *arg;
 };
 
@@ -1324,7 +1324,7 @@
 findfunc(spa_t *spa, uint64_t dsobj, const char *dsname, void *arg)
 {
 	struct findarg *fa = arg;
-	return (fa->func((char *)dsname, fa->arg));
+	return (fa->func(dsname, fa->arg));
 }
 
 /*
@@ -1332,7 +1332,8 @@
  * Perhaps change all callers to use dmu_objset_find_spa()?
  */
 int
-dmu_objset_find(char *name, int func(char *, void *), void *arg, int flags)
+dmu_objset_find(char *name, int func(const char *, void *), void *arg,
+    int flags)
 {
 	struct findarg fa;
 	fa.func = func;
@@ -1446,7 +1447,7 @@
 
 /* ARGSUSED */
 int
-dmu_objset_prefetch(char *name, void *arg)
+dmu_objset_prefetch(const char *name, void *arg)
 {
 	dsl_dataset_t *ds;
 
--- a/usr/src/uts/common/fs/zfs/dmu_send.c	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/uts/common/fs/zfs/dmu_send.c	Mon Nov 30 11:36:36 2009 -0800
@@ -758,12 +758,12 @@
  * guid of the dataset with the referenced data.
  */
 int
-find_ds_by_guid(char *name, void *arg)
+find_ds_by_guid(const char *name, void *arg)
 {
+	avl_tree_t *guid_map = arg;
 	dsl_dataset_t *ds, *snapds;
-	avl_tree_t *guid_map = arg;
 	guid_map_entry_t *gmep;
-	dsl_pool_t	*dp;
+	dsl_pool_t *dp;
 	int err;
 	uint64_t lastobj, firstobj;
 
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c	Mon Nov 30 11:36:36 2009 -0800
@@ -855,7 +855,7 @@
 };
 
 static int
-dsl_snapshot_destroy_one(char *name, void *arg)
+dsl_snapshot_destroy_one(const char *name, void *arg)
 {
 	struct destroyarg *da = arg;
 	dsl_dataset_t *ds;
@@ -2191,47 +2191,42 @@
 };
 
 static int
-dsl_snapshot_rename_one(char *name, void *arg)
+dsl_snapshot_rename_one(const char *name, void *arg)
 {
 	struct renamesnaparg *ra = arg;
 	dsl_dataset_t *ds = NULL;
-	char *cp;
+	char *snapname;
 	int err;
 
-	cp = name + strlen(name);
-	*cp = '@';
-	(void) strcpy(cp + 1, ra->oldsnap);
+	snapname = kmem_asprintf("%s@%s", name, ra->oldsnap);
+	(void) strlcpy(ra->failed, snapname, sizeof (ra->failed));
 
 	/*
 	 * For recursive snapshot renames the parent won't be changing
 	 * so we just pass name for both the to/from argument.
 	 */
-	err = zfs_secpolicy_rename_perms(name, name, CRED());
-	if (err == ENOENT) {
-		return (0);
-	} else if (err) {
-		(void) strcpy(ra->failed, name);
-		return (err);
+	err = zfs_secpolicy_rename_perms(snapname, snapname, CRED());
+	if (err != 0) {
+		strfree(snapname);
+		return (err == ENOENT ? 0 : err);
 	}
 
 #ifdef _KERNEL
 	/*
 	 * For all filesystems undergoing rename, we'll need to unmount it.
 	 */
-	(void) zfs_unmount_snap(name, NULL);
+	(void) zfs_unmount_snap(snapname, NULL);
 #endif
-	err = dsl_dataset_hold(name, ra->dstg, &ds);
-	*cp = '\0';
-	if (err == ENOENT) {
-		return (0);
-	} else if (err) {
-		(void) strcpy(ra->failed, name);
-		return (err);
+	err = dsl_dataset_hold(snapname, ra->dstg, &ds);
+	if (err != 0) {
+		strfree(snapname);
+		return (err == ENOENT ? 0 : err);
 	}
 
 	dsl_sync_task_create(ra->dstg, dsl_dataset_snapshot_rename_check,
 	    dsl_dataset_snapshot_rename_sync, ds, ra->newsnap, 0);
 
+	strfree(snapname);
 	return (0);
 }
 
@@ -2243,7 +2238,7 @@
 	dsl_sync_task_t *dst;
 	spa_t *spa;
 	char *cp, *fsname = spa_strdup(oldname);
-	int len = strlen(oldname);
+	int len = strlen(oldname) + 1;
 
 	/* truncate the snapshot name to get the fsname */
 	cp = strchr(fsname, '@');
@@ -2251,7 +2246,7 @@
 
 	err = spa_open(fsname, &spa, FTAG);
 	if (err) {
-		kmem_free(fsname, len + 1);
+		kmem_free(fsname, len);
 		return (err);
 	}
 	ra = kmem_alloc(sizeof (struct renamesnaparg), KM_SLEEP);
@@ -2263,7 +2258,7 @@
 
 	err = dmu_objset_find(fsname, dsl_snapshot_rename_one, ra,
 	    DS_FIND_CHILDREN);
-	kmem_free(fsname, len + 1);
+	kmem_free(fsname, len);
 
 	if (err == 0) {
 		err = dsl_sync_task_group_wait(ra->dstg);
@@ -2274,14 +2269,15 @@
 		dsl_dataset_t *ds = dst->dst_arg1;
 		if (dst->dst_err) {
 			dsl_dir_name(ds->ds_dir, ra->failed);
-			(void) strcat(ra->failed, "@");
-			(void) strcat(ra->failed, ra->newsnap);
+			(void) strlcat(ra->failed, "@", sizeof (ra->failed));
+			(void) strlcat(ra->failed, ra->newsnap,
+			    sizeof (ra->failed));
 		}
 		dsl_dataset_rele(ds, ra->dstg);
 	}
 
 	if (err)
-		(void) strcpy(oldname, ra->failed);
+		(void) strlcpy(oldname, ra->failed, sizeof (ra->failed));
 
 	dsl_sync_task_group_destroy(ra->dstg);
 	kmem_free(ra, sizeof (struct renamesnaparg));
@@ -2290,7 +2286,7 @@
 }
 
 static int
-dsl_valid_rename(char *oldname, void *arg)
+dsl_valid_rename(const char *oldname, void *arg)
 {
 	int delta = *(int *)arg;
 
@@ -3349,7 +3345,7 @@
 }
 
 static int
-dsl_dataset_user_hold_one(char *dsname, void *arg)
+dsl_dataset_user_hold_one(const char *dsname, void *arg)
 {
 	struct dsl_ds_holdarg *ha = arg;
 	dsl_dataset_t *ds;
@@ -3367,7 +3363,7 @@
 	} else if (error == ENOENT && ha->recursive) {
 		error = 0;
 	} else {
-		(void) strcpy(ha->failed, dsname);
+		(void) strlcpy(ha->failed, dsname, sizeof (ha->failed));
 	}
 	return (error);
 }
@@ -3420,7 +3416,7 @@
 		error = ENOENT;
 
 	if (error)
-		(void) strcpy(dsname, ha->failed);
+		(void) strlcpy(dsname, ha->failed, sizeof (ha->failed));
 
 	dsl_sync_task_group_destroy(ha->dstg);
 	kmem_free(ha, sizeof (struct dsl_ds_holdarg));
@@ -3545,7 +3541,7 @@
 }
 
 static int
-dsl_dataset_user_release_one(char *dsname, void *arg)
+dsl_dataset_user_release_one(const char *dsname, void *arg)
 {
 	struct dsl_ds_holdarg *ha = arg;
 	struct dsl_ds_releasearg *ra;
@@ -3562,7 +3558,7 @@
 	strfree(name);
 	if (error == ENOENT && ha->recursive)
 		return (0);
-	(void) strcpy(ha->failed, dsname);
+	(void) strlcpy(ha->failed, dsname, sizeof (ha->failed));
 	if (error)
 		return (error);
 
@@ -3655,7 +3651,7 @@
 		error = ENOENT;
 
 	if (error)
-		(void) strcpy(dsname, ha->failed);
+		(void) strlcpy(dsname, ha->failed, sizeof (ha->failed));
 
 	dsl_sync_task_group_destroy(ha->dstg);
 	kmem_free(ha, sizeof (struct dsl_ds_holdarg));
@@ -3729,7 +3725,7 @@
  */
 /* ARGSUSED */
 int
-dsl_destroy_inconsistent(char *dsname, void *arg)
+dsl_destroy_inconsistent(const char *dsname, void *arg)
 {
 	dsl_dataset_t *ds;
 
--- a/usr/src/uts/common/fs/zfs/sys/dmu.h	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/uts/common/fs/zfs/sys/dmu.h	Mon Nov 30 11:36:36 2009 -0800
@@ -178,7 +178,7 @@
     boolean_t recursive);
 int dmu_objset_rename(const char *name, const char *newname,
     boolean_t recursive);
-int dmu_objset_find(char *name, int func(char *, void *), void *arg,
+int dmu_objset_find(char *name, int func(const char *, void *), void *arg,
     int flags);
 void dmu_objset_byteswap(void *buf, size_t size);
 
--- a/usr/src/uts/common/fs/zfs/sys/dmu_objset.h	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/uts/common/fs/zfs/sys/dmu_objset.h	Mon Nov 30 11:36:36 2009 -0800
@@ -129,11 +129,11 @@
 void dmu_objset_space(objset_t *os, uint64_t *refdbytesp, uint64_t *availbytesp,
     uint64_t *usedobjsp, uint64_t *availobjsp);
 uint64_t dmu_objset_fsid_guid(objset_t *os);
-int dmu_objset_find(char *name, int func(char *, void *), void *arg,
+int dmu_objset_find(char *name, int func(const char *, void *), void *arg,
     int flags);
 int dmu_objset_find_spa(spa_t *spa, const char *name,
     int func(spa_t *, uint64_t, const char *, void *), void *arg, int flags);
-int dmu_objset_prefetch(char *name, void *arg);
+int dmu_objset_prefetch(const char *name, void *arg);
 void dmu_objset_byteswap(void *buf, size_t size);
 int dmu_objset_evict_dbufs(objset_t *os);
 timestruc_t dmu_objset_snap_cmtime(objset_t *os);
--- a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h	Mon Nov 30 11:36:36 2009 -0800
@@ -240,7 +240,7 @@
 int dsl_dataset_set_reservation(const char *dsname, zprop_source_t source,
     uint64_t reservation);
 
-int dsl_destroy_inconsistent(char *dsname, void *arg);
+int dsl_destroy_inconsistent(const char *dsname, void *arg);
 
 #ifdef ZFS_DEBUG
 #define	dprintf_ds(ds, fmt, ...) do { \
--- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h	Mon Nov 30 11:36:36 2009 -0800
@@ -270,7 +270,7 @@
     const char *to, cred_t *cr);
 extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr);
 extern int zfs_busy(void);
-extern int zfs_unmount_snap(char *, void *);
+extern int zfs_unmount_snap(const char *, void *);
 
 #endif	/* _KERNEL */
 
--- a/usr/src/uts/common/fs/zfs/sys/zil.h	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/uts/common/fs/zfs/sys/zil.h	Mon Nov 30 11:36:36 2009 -0800
@@ -401,9 +401,9 @@
 
 extern void	zil_commit(zilog_t *zilog, uint64_t seq, uint64_t oid);
 
-extern int	zil_vdev_offline(char *osname, void *txarg);
-extern int	zil_claim(char *osname, void *txarg);
-extern int	zil_check_log_chain(char *osname, void *txarg);
+extern int	zil_vdev_offline(const char *osname, void *txarg);
+extern int	zil_claim(const char *osname, void *txarg);
+extern int	zil_check_log_chain(const char *osname, void *txarg);
 extern void	zil_sync(zilog_t *zilog, dmu_tx_t *tx);
 extern void	zil_clean(zilog_t *zilog);
 
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Mon Nov 30 11:36:36 2009 -0800
@@ -2831,20 +2831,15 @@
 }
 
 int
-zfs_unmount_snap(char *name, void *arg)
+zfs_unmount_snap(const char *name, void *arg)
 {
 	vfs_t *vfsp = NULL;
 
 	if (arg) {
 		char *snapname = arg;
-		int len = strlen(name) + strlen(snapname) + 2;
-		char *buf = kmem_alloc(len, KM_SLEEP);
-
-		(void) strcpy(buf, name);
-		(void) strcat(buf, "@");
-		(void) strcat(buf, snapname);
-		vfsp = zfs_get_vfs(buf);
-		kmem_free(buf, len);
+		char *fullname = kmem_asprintf("%s@%s", name, snapname);
+		vfsp = zfs_get_vfs(fullname);
+		strfree(fullname);
 	} else if (strchr(name, '@')) {
 		vfsp = zfs_get_vfs(name);
 	}
--- a/usr/src/uts/common/fs/zfs/zil.c	Mon Nov 30 15:30:52 2009 -0500
+++ b/usr/src/uts/common/fs/zfs/zil.c	Mon Nov 30 11:36:36 2009 -0800
@@ -529,7 +529,7 @@
 }
 
 int
-zil_claim(char *osname, void *txarg)
+zil_claim(const char *osname, void *txarg)
 {
 	dmu_tx_t *tx = txarg;
 	uint64_t first_txg = dmu_tx_get_txg(tx);
@@ -587,7 +587,7 @@
  * Any other error (no device or read failure) returns an error.
  */
 int
-zil_check_log_chain(char *osname, void *tx)
+zil_check_log_chain(const char *osname, void *tx)
 {
 	zilog_t *zilog;
 	objset_t *os;
@@ -1676,7 +1676,7 @@
 
 /* ARGSUSED */
 int
-zil_vdev_offline(char *osname, void *arg)
+zil_vdev_offline(const char *osname, void *arg)
 {
 	objset_t *os;
 	zilog_t *zilog;