Bug 186574 - [zfs] zpool history hangs (infinite loop)
Summary: [zfs] zpool history hangs (infinite loop)
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 10.0-RELEASE
Hardware: Any Any
: Normal Affects Only Me
Assignee: Xin LI
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-02-09 05:20 UTC by lorne
Modified: 2014-04-28 07:31 UTC (History)
0 users

See Also:


Attachments
libzfs_pool-dynamic-history-buffer (1.26 KB, text/plain)
2014-02-09 05:20 UTC, lorne
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description lorne 2014-02-09 05:20:00 UTC
Running "zpool history zroot" on my FreeBSD 10-RELEASE machine results
in zpool entering an infinite loop.

Fix: This appears to be caused by having a history record larger than
libzfs's HIS_BUF_LEN.

zpool_get_history reads HIS_BUF_LEN sized chunks and parses entire
records at a time. When an entire record isn't contained within a
single chunk, zpool_get_history correctly concludes the result was
truncated. The next read is aligned such that any truncated record
will be at the beginning of the buffer. zpool_get_history then loops
with the assumption this will read the entire record. When a record
larger than HIS_BUF_LEN is encountered, it will always be truncated,
even if it's at the start of the buffer, and zpool_get_history will
loop indefinitely.

With a warning patched in:

  History for 'zroot':
  HIS_BUF_LEN (0x20000) too small to fit record of length 0x239c8

With a dynamically increasing buffer the history is successfully
printed.

I don't believe the particular record is malformed. The slightly
redacted output of running strings over the data is here:
https://gist.github.com/thefloweringash/1fd434541c05fc091b10

Patch attached to introduce a dynamically sized buffer. This solves
the problem for me. However I don't know how HIS_BUF_LEN was chosen,
and could be patching a symptom.
How-To-Repeat: 
Run "zpool history" on an affected pool.
Comment 1 Mark Linimon freebsd_committer freebsd_triage 2014-02-10 04:26:16 UTC
Responsible Changed
From-To: freebsd-bugs->freebsd-fs

Over to maintainer(s).
Comment 2 dfilter service freebsd_committer freebsd_triage 2014-04-14 19:38:18 UTC
Author: delphij
Date: Mon Apr 14 18:38:14 2014
New Revision: 264467
URL: http://svnweb.freebsd.org/changeset/base/264467

Log:
  Take into account when zpool history block grows exceeding 128KB in zpool(8)
  and zdb(8) by growing the buffer on demand with a cap of 1GB (specified in
  spa_history_create_obj()).
  
  PR:		bin/186574
  Submitted by:	Andrew Childs <lorne cons org nz> (with changes)
  MFC after:	2 weeks

Modified:
  head/cddl/contrib/opensolaris/cmd/zdb/zdb.c
  head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c

Modified: head/cddl/contrib/opensolaris/cmd/zdb/zdb.c
==============================================================================
--- head/cddl/contrib/opensolaris/cmd/zdb/zdb.c	Mon Apr 14 18:14:09 2014	(r264466)
+++ head/cddl/contrib/opensolaris/cmd/zdb/zdb.c	Mon Apr 14 18:38:14 2014	(r264467)
@@ -929,11 +929,16 @@ dump_dtl(vdev_t *vd, int indent)
 		dump_dtl(vd->vdev_child[c], indent + 4);
 }
 
+/* from spa_history.c: spa_history_create_obj() */
+#define	HIS_BUF_LEN_DEF	(128 << 10)
+#define	HIS_BUF_LEN_MAX	(1 << 30)
+
 static void
 dump_history(spa_t *spa)
 {
 	nvlist_t **events = NULL;
-	char buf[SPA_MAXBLOCKSIZE];
+	char *buf = NULL;
+	uint64_t bufsize = HIS_BUF_LEN_DEF;
 	uint64_t resid, len, off = 0;
 	uint_t num = 0;
 	int error;
@@ -942,8 +947,11 @@ dump_history(spa_t *spa)
 	char tbuf[30];
 	char internalstr[MAXPATHLEN];
 
+	if ((buf = malloc(bufsize)) == NULL)
+		(void) fprintf(stderr, "Unable to read history: "
+		    "out of memory\n");
 	do {
-		len = sizeof (buf);
+		len = bufsize;
 
 		if ((error = spa_history_get(spa, &off, &len, buf)) != 0) {
 			(void) fprintf(stderr, "Unable to read history: "
@@ -953,9 +961,26 @@ dump_history(spa_t *spa)
 
 		if (zpool_history_unpack(buf, len, &resid, &events, &num) != 0)
 			break;
-
 		off -= resid;
+
+		/*
+		 * If the history block is too big, double the buffer
+		 * size and try again.
+		 */
+		if (resid == len) {
+			free(buf);
+			buf = NULL;
+
+			bufsize <<= 1;
+			if ((bufsize >= HIS_BUF_LEN_MAX) ||
+			    ((buf = malloc(bufsize)) == NULL)) {
+				(void) fprintf(stderr, "Unable to read history: "
+				    "out of memory\n");
+				return;
+			}
+		}
 	} while (len != 0);
+	free(buf);
 
 	(void) printf("\nHistory:\n");
 	for (int i = 0; i < num; i++) {

Modified: head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
==============================================================================
--- head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c	Mon Apr 14 18:14:09 2014	(r264466)
+++ head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c	Mon Apr 14 18:38:14 2014	(r264467)
@@ -3744,7 +3744,9 @@ zpool_history_unpack(char *buf, uint64_t
 	return (0);
 }
 
-#define	HIS_BUF_LEN	(128*1024)
+/* from spa_history.c: spa_history_create_obj() */
+#define	HIS_BUF_LEN_DEF	(128 << 10)
+#define	HIS_BUF_LEN_MAX	(1 << 30)
 
 /*
  * Retrieve the command history of a pool.
@@ -3752,21 +3754,24 @@ zpool_history_unpack(char *buf, uint64_t
 int
 zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp)
 {
-	char buf[HIS_BUF_LEN];
+	char *buf = NULL;
+	uint64_t bufsize = HIS_BUF_LEN_DEF;
 	uint64_t off = 0;
 	nvlist_t **records = NULL;
 	uint_t numrecords = 0;
 	int err, i;
 
+	if ((buf = malloc(bufsize)) == NULL)
+		return (ENOMEM);
 	do {
-		uint64_t bytes_read = sizeof (buf);
+		uint64_t bytes_read = bufsize;
 		uint64_t leftover;
 
 		if ((err = get_history(zhp, buf, &off, &bytes_read)) != 0)
 			break;
 
 		/* if nothing else was read in, we're at EOF, just return */
-		if (!bytes_read)
+		if (bytes_read == 0)
 			break;
 
 		if ((err = zpool_history_unpack(buf, bytes_read,
@@ -3774,8 +3779,25 @@ zpool_get_history(zpool_handle_t *zhp, n
 			break;
 		off -= leftover;
 
+		/*
+		 * If the history block is too big, double the buffer
+		 * size and try again.
+		 */
+		if (leftover == bytes_read) {
+			free(buf);
+			buf = NULL;
+
+			bufsize <<= 1;
+			if ((bufsize >= HIS_BUF_LEN_MAX) ||
+			    ((buf = malloc(bufsize)) == NULL)) {
+				err = ENOMEM;
+				break;
+			}
+		}
+
 		/* CONSTCOND */
 	} while (1);
+	free(buf);
 
 	if (!err) {
 		verify(nvlist_alloc(nvhisp, NV_UNIQUE_NAME, 0) == 0);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Comment 3 dfilter service freebsd_committer freebsd_triage 2014-04-28 07:11:07 UTC
Author: delphij
Date: Mon Apr 28 06:11:03 2014
New Revision: 265039
URL: http://svnweb.freebsd.org/changeset/base/265039

Log:
  MFC r264467:
  
  Take into account when zpool history block grows exceeding 128KB in zpool(8)
  and zdb(8) by growing the buffer on demand with a cap of 1GB (specified in
  spa_history_create_obj()).
  
  PR:		bin/186574
  Submitted by:	Andrew Childs <lorne cons org nz> (with changes)

Modified:
  stable/10/cddl/contrib/opensolaris/cmd/zdb/zdb.c
  stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/cddl/contrib/opensolaris/cmd/zdb/zdb.c
==============================================================================
--- stable/10/cddl/contrib/opensolaris/cmd/zdb/zdb.c	Mon Apr 28 05:39:20 2014	(r265038)
+++ stable/10/cddl/contrib/opensolaris/cmd/zdb/zdb.c	Mon Apr 28 06:11:03 2014	(r265039)
@@ -929,11 +929,16 @@ dump_dtl(vdev_t *vd, int indent)
 		dump_dtl(vd->vdev_child[c], indent + 4);
 }
 
+/* from spa_history.c: spa_history_create_obj() */
+#define	HIS_BUF_LEN_DEF	(128 << 10)
+#define	HIS_BUF_LEN_MAX	(1 << 30)
+
 static void
 dump_history(spa_t *spa)
 {
 	nvlist_t **events = NULL;
-	char buf[SPA_MAXBLOCKSIZE];
+	char *buf = NULL;
+	uint64_t bufsize = HIS_BUF_LEN_DEF;
 	uint64_t resid, len, off = 0;
 	uint_t num = 0;
 	int error;
@@ -942,8 +947,11 @@ dump_history(spa_t *spa)
 	char tbuf[30];
 	char internalstr[MAXPATHLEN];
 
+	if ((buf = malloc(bufsize)) == NULL)
+		(void) fprintf(stderr, "Unable to read history: "
+		    "out of memory\n");
 	do {
-		len = sizeof (buf);
+		len = bufsize;
 
 		if ((error = spa_history_get(spa, &off, &len, buf)) != 0) {
 			(void) fprintf(stderr, "Unable to read history: "
@@ -953,9 +961,26 @@ dump_history(spa_t *spa)
 
 		if (zpool_history_unpack(buf, len, &resid, &events, &num) != 0)
 			break;
-
 		off -= resid;
+
+		/*
+		 * If the history block is too big, double the buffer
+		 * size and try again.
+		 */
+		if (resid == len) {
+			free(buf);
+			buf = NULL;
+
+			bufsize <<= 1;
+			if ((bufsize >= HIS_BUF_LEN_MAX) ||
+			    ((buf = malloc(bufsize)) == NULL)) {
+				(void) fprintf(stderr, "Unable to read history: "
+				    "out of memory\n");
+				return;
+			}
+		}
 	} while (len != 0);
+	free(buf);
 
 	(void) printf("\nHistory:\n");
 	for (int i = 0; i < num; i++) {

Modified: stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
==============================================================================
--- stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c	Mon Apr 28 05:39:20 2014	(r265038)
+++ stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c	Mon Apr 28 06:11:03 2014	(r265039)
@@ -3744,7 +3744,9 @@ zpool_history_unpack(char *buf, uint64_t
 	return (0);
 }
 
-#define	HIS_BUF_LEN	(128*1024)
+/* from spa_history.c: spa_history_create_obj() */
+#define	HIS_BUF_LEN_DEF	(128 << 10)
+#define	HIS_BUF_LEN_MAX	(1 << 30)
 
 /*
  * Retrieve the command history of a pool.
@@ -3752,21 +3754,24 @@ zpool_history_unpack(char *buf, uint64_t
 int
 zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp)
 {
-	char buf[HIS_BUF_LEN];
+	char *buf = NULL;
+	uint64_t bufsize = HIS_BUF_LEN_DEF;
 	uint64_t off = 0;
 	nvlist_t **records = NULL;
 	uint_t numrecords = 0;
 	int err, i;
 
+	if ((buf = malloc(bufsize)) == NULL)
+		return (ENOMEM);
 	do {
-		uint64_t bytes_read = sizeof (buf);
+		uint64_t bytes_read = bufsize;
 		uint64_t leftover;
 
 		if ((err = get_history(zhp, buf, &off, &bytes_read)) != 0)
 			break;
 
 		/* if nothing else was read in, we're at EOF, just return */
-		if (!bytes_read)
+		if (bytes_read == 0)
 			break;
 
 		if ((err = zpool_history_unpack(buf, bytes_read,
@@ -3774,8 +3779,25 @@ zpool_get_history(zpool_handle_t *zhp, n
 			break;
 		off -= leftover;
 
+		/*
+		 * If the history block is too big, double the buffer
+		 * size and try again.
+		 */
+		if (leftover == bytes_read) {
+			free(buf);
+			buf = NULL;
+
+			bufsize <<= 1;
+			if ((bufsize >= HIS_BUF_LEN_MAX) ||
+			    ((buf = malloc(bufsize)) == NULL)) {
+				err = ENOMEM;
+				break;
+			}
+		}
+
 		/* CONSTCOND */
 	} while (1);
+	free(buf);
 
 	if (!err) {
 		verify(nvlist_alloc(nvhisp, NV_UNIQUE_NAME, 0) == 0);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Comment 4 dfilter service freebsd_committer freebsd_triage 2014-04-28 07:11:47 UTC
Author: delphij
Date: Mon Apr 28 06:11:44 2014
New Revision: 265040
URL: http://svnweb.freebsd.org/changeset/base/265040

Log:
  MFC r264467:
  
  Take into account when zpool history block grows exceeding 128KB in zpool(8)
  and zdb(8) by growing the buffer on demand with a cap of 1GB (specified in
  spa_history_create_obj()).
  
  PR:		bin/186574
  Submitted by:	Andrew Childs <lorne cons org nz> (with changes)

Modified:
  stable/9/cddl/contrib/opensolaris/cmd/zdb/zdb.c
  stable/9/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
Directory Properties:
  stable/9/cddl/contrib/opensolaris/   (props changed)
  stable/9/cddl/contrib/opensolaris/lib/libzfs/   (props changed)

Modified: stable/9/cddl/contrib/opensolaris/cmd/zdb/zdb.c
==============================================================================
--- stable/9/cddl/contrib/opensolaris/cmd/zdb/zdb.c	Mon Apr 28 06:11:03 2014	(r265039)
+++ stable/9/cddl/contrib/opensolaris/cmd/zdb/zdb.c	Mon Apr 28 06:11:44 2014	(r265040)
@@ -929,11 +929,16 @@ dump_dtl(vdev_t *vd, int indent)
 		dump_dtl(vd->vdev_child[c], indent + 4);
 }
 
+/* from spa_history.c: spa_history_create_obj() */
+#define	HIS_BUF_LEN_DEF	(128 << 10)
+#define	HIS_BUF_LEN_MAX	(1 << 30)
+
 static void
 dump_history(spa_t *spa)
 {
 	nvlist_t **events = NULL;
-	char buf[SPA_MAXBLOCKSIZE];
+	char *buf = NULL;
+	uint64_t bufsize = HIS_BUF_LEN_DEF;
 	uint64_t resid, len, off = 0;
 	uint_t num = 0;
 	int error;
@@ -942,8 +947,11 @@ dump_history(spa_t *spa)
 	char tbuf[30];
 	char internalstr[MAXPATHLEN];
 
+	if ((buf = malloc(bufsize)) == NULL)
+		(void) fprintf(stderr, "Unable to read history: "
+		    "out of memory\n");
 	do {
-		len = sizeof (buf);
+		len = bufsize;
 
 		if ((error = spa_history_get(spa, &off, &len, buf)) != 0) {
 			(void) fprintf(stderr, "Unable to read history: "
@@ -953,9 +961,26 @@ dump_history(spa_t *spa)
 
 		if (zpool_history_unpack(buf, len, &resid, &events, &num) != 0)
 			break;
-
 		off -= resid;
+
+		/*
+		 * If the history block is too big, double the buffer
+		 * size and try again.
+		 */
+		if (resid == len) {
+			free(buf);
+			buf = NULL;
+
+			bufsize <<= 1;
+			if ((bufsize >= HIS_BUF_LEN_MAX) ||
+			    ((buf = malloc(bufsize)) == NULL)) {
+				(void) fprintf(stderr, "Unable to read history: "
+				    "out of memory\n");
+				return;
+			}
+		}
 	} while (len != 0);
+	free(buf);
 
 	(void) printf("\nHistory:\n");
 	for (int i = 0; i < num; i++) {

Modified: stable/9/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
==============================================================================
--- stable/9/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c	Mon Apr 28 06:11:03 2014	(r265039)
+++ stable/9/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c	Mon Apr 28 06:11:44 2014	(r265040)
@@ -3736,7 +3736,9 @@ zpool_history_unpack(char *buf, uint64_t
 	return (0);
 }
 
-#define	HIS_BUF_LEN	(128*1024)
+/* from spa_history.c: spa_history_create_obj() */
+#define	HIS_BUF_LEN_DEF	(128 << 10)
+#define	HIS_BUF_LEN_MAX	(1 << 30)
 
 /*
  * Retrieve the command history of a pool.
@@ -3744,21 +3746,24 @@ zpool_history_unpack(char *buf, uint64_t
 int
 zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp)
 {
-	char buf[HIS_BUF_LEN];
+	char *buf = NULL;
+	uint64_t bufsize = HIS_BUF_LEN_DEF;
 	uint64_t off = 0;
 	nvlist_t **records = NULL;
 	uint_t numrecords = 0;
 	int err, i;
 
+	if ((buf = malloc(bufsize)) == NULL)
+		return (ENOMEM);
 	do {
-		uint64_t bytes_read = sizeof (buf);
+		uint64_t bytes_read = bufsize;
 		uint64_t leftover;
 
 		if ((err = get_history(zhp, buf, &off, &bytes_read)) != 0)
 			break;
 
 		/* if nothing else was read in, we're at EOF, just return */
-		if (!bytes_read)
+		if (bytes_read == 0)
 			break;
 
 		if ((err = zpool_history_unpack(buf, bytes_read,
@@ -3766,8 +3771,25 @@ zpool_get_history(zpool_handle_t *zhp, n
 			break;
 		off -= leftover;
 
+		/*
+		 * If the history block is too big, double the buffer
+		 * size and try again.
+		 */
+		if (leftover == bytes_read) {
+			free(buf);
+			buf = NULL;
+
+			bufsize <<= 1;
+			if ((bufsize >= HIS_BUF_LEN_MAX) ||
+			    ((buf = malloc(bufsize)) == NULL)) {
+				err = ENOMEM;
+				break;
+			}
+		}
+
 		/* CONSTCOND */
 	} while (1);
+	free(buf);
 
 	if (!err) {
 		verify(nvlist_alloc(nvhisp, NV_UNIQUE_NAME, 0) == 0);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Comment 5 dfilter service freebsd_committer freebsd_triage 2014-04-28 07:12:18 UTC
Author: delphij
Date: Mon Apr 28 06:12:15 2014
New Revision: 265041
URL: http://svnweb.freebsd.org/changeset/base/265041

Log:
  MFC r264467:
  
  Take into account when zpool history block grows exceeding 128KB in zpool(8)
  and zdb(8) by growing the buffer on demand with a cap of 1GB (specified in
  spa_history_create_obj()).
  
  PR:		bin/186574
  Submitted by:	Andrew Childs <lorne cons org nz> (with changes)

Modified:
  stable/8/cddl/contrib/opensolaris/cmd/zdb/zdb.c
  stable/8/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
Directory Properties:
  stable/8/cddl/contrib/opensolaris/   (props changed)
  stable/8/cddl/contrib/opensolaris/lib/libzfs/   (props changed)

Modified: stable/8/cddl/contrib/opensolaris/cmd/zdb/zdb.c
==============================================================================
--- stable/8/cddl/contrib/opensolaris/cmd/zdb/zdb.c	Mon Apr 28 06:11:44 2014	(r265040)
+++ stable/8/cddl/contrib/opensolaris/cmd/zdb/zdb.c	Mon Apr 28 06:12:15 2014	(r265041)
@@ -929,11 +929,16 @@ dump_dtl(vdev_t *vd, int indent)
 		dump_dtl(vd->vdev_child[c], indent + 4);
 }
 
+/* from spa_history.c: spa_history_create_obj() */
+#define	HIS_BUF_LEN_DEF	(128 << 10)
+#define	HIS_BUF_LEN_MAX	(1 << 30)
+
 static void
 dump_history(spa_t *spa)
 {
 	nvlist_t **events = NULL;
-	char buf[SPA_MAXBLOCKSIZE];
+	char *buf = NULL;
+	uint64_t bufsize = HIS_BUF_LEN_DEF;
 	uint64_t resid, len, off = 0;
 	uint_t num = 0;
 	int error;
@@ -942,8 +947,11 @@ dump_history(spa_t *spa)
 	char tbuf[30];
 	char internalstr[MAXPATHLEN];
 
+	if ((buf = malloc(bufsize)) == NULL)
+		(void) fprintf(stderr, "Unable to read history: "
+		    "out of memory\n");
 	do {
-		len = sizeof (buf);
+		len = bufsize;
 
 		if ((error = spa_history_get(spa, &off, &len, buf)) != 0) {
 			(void) fprintf(stderr, "Unable to read history: "
@@ -953,9 +961,26 @@ dump_history(spa_t *spa)
 
 		if (zpool_history_unpack(buf, len, &resid, &events, &num) != 0)
 			break;
-
 		off -= resid;
+
+		/*
+		 * If the history block is too big, double the buffer
+		 * size and try again.
+		 */
+		if (resid == len) {
+			free(buf);
+			buf = NULL;
+
+			bufsize <<= 1;
+			if ((bufsize >= HIS_BUF_LEN_MAX) ||
+			    ((buf = malloc(bufsize)) == NULL)) {
+				(void) fprintf(stderr, "Unable to read history: "
+				    "out of memory\n");
+				return;
+			}
+		}
 	} while (len != 0);
+	free(buf);
 
 	(void) printf("\nHistory:\n");
 	for (int i = 0; i < num; i++) {

Modified: stable/8/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
==============================================================================
--- stable/8/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c	Mon Apr 28 06:11:44 2014	(r265040)
+++ stable/8/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c	Mon Apr 28 06:12:15 2014	(r265041)
@@ -3736,7 +3736,9 @@ zpool_history_unpack(char *buf, uint64_t
 	return (0);
 }
 
-#define	HIS_BUF_LEN	(128*1024)
+/* from spa_history.c: spa_history_create_obj() */
+#define	HIS_BUF_LEN_DEF	(128 << 10)
+#define	HIS_BUF_LEN_MAX	(1 << 30)
 
 /*
  * Retrieve the command history of a pool.
@@ -3744,21 +3746,24 @@ zpool_history_unpack(char *buf, uint64_t
 int
 zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp)
 {
-	char buf[HIS_BUF_LEN];
+	char *buf = NULL;
+	uint64_t bufsize = HIS_BUF_LEN_DEF;
 	uint64_t off = 0;
 	nvlist_t **records = NULL;
 	uint_t numrecords = 0;
 	int err, i;
 
+	if ((buf = malloc(bufsize)) == NULL)
+		return (ENOMEM);
 	do {
-		uint64_t bytes_read = sizeof (buf);
+		uint64_t bytes_read = bufsize;
 		uint64_t leftover;
 
 		if ((err = get_history(zhp, buf, &off, &bytes_read)) != 0)
 			break;
 
 		/* if nothing else was read in, we're at EOF, just return */
-		if (!bytes_read)
+		if (bytes_read == 0)
 			break;
 
 		if ((err = zpool_history_unpack(buf, bytes_read,
@@ -3766,8 +3771,25 @@ zpool_get_history(zpool_handle_t *zhp, n
 			break;
 		off -= leftover;
 
+		/*
+		 * If the history block is too big, double the buffer
+		 * size and try again.
+		 */
+		if (leftover == bytes_read) {
+			free(buf);
+			buf = NULL;
+
+			bufsize <<= 1;
+			if ((bufsize >= HIS_BUF_LEN_MAX) ||
+			    ((buf = malloc(bufsize)) == NULL)) {
+				err = ENOMEM;
+				break;
+			}
+		}
+
 		/* CONSTCOND */
 	} while (1);
+	free(buf);
 
 	if (!err) {
 		verify(nvlist_alloc(nvhisp, NV_UNIQUE_NAME, 0) == 0);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Comment 6 Xin LI freebsd_committer freebsd_triage 2014-04-28 07:31:27 UTC
State Changed
From-To: open->closed

A slightly changed version of patch committed and merged. 


Comment 7 Xin LI freebsd_committer freebsd_triage 2014-04-28 07:31:27 UTC
Responsible Changed
From-To: freebsd-fs->delphij

Take.  Thanks for your submission!