Bug 144962

Summary: [geom] panic when accessing GPT disk with a large number of entries
Product: Base System Reporter: Rebecca Cran <bcran>
Component: kernAssignee: Andrey V. Elsukov <ae>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 9.0-CURRENT   
Hardware: Any   
OS: Any   

Description Rebecca Cran freebsd_committer freebsd_triage 2010-03-22 18:50:02 UTC
After creating a GPT with an excessively large number of entries, FreeBSD then is unable to access the disk without running out of kernel virtual memory.

For example, after running "gpart create -s gpt -n 1000000 da0" then accessing the disk, the system will the panic with "kmem_map too small".

How-To-Repeat: Run "gpart create -s gpt -n 1000000 da0" then reboot
Comment 1 Bruce Cran freebsd_committer freebsd_triage 2010-03-22 19:02:27 UTC
Responsible Changed
From-To: freebsd-bugs->freebsd-geom

Over to maintainer(s).
Comment 2 Alexander Best 2010-03-23 00:09:01 UTC
tried this too under HEAD (r205390):

`gpart create -s gpt -n 1000000 /dev/da0` gave me "gpart: provider: No space
left on device" so i did `gpart create -s gpt -n 1000000 /dev/da0` instead
which worked. reattaching the device caused no problem.

when i tried to create a label with `glabel label usb da0` i got:

GEOM: da0: the secondary GPT table is corrupt or invalid.
GEOM: da0: using the primary only -- recovery suggested.
GEOM: da0: the secondary GPT table is corrupt or invalid.
GEOM: da0: using the primary only -- recovery suggested.
GEOM: label/usb: the secondary GPT table is corrupt or invalid.
GEOM: label/usb: using the primary only -- recovery suggested.

seems -n 1000000 is not enough to fill up kernel virtual memory in my case.

-- 
Alexander Best
Comment 3 Alexander Best 2010-03-23 00:19:20 UTC
sorry. the command i used was `gpart create -s gpt -n 100000 /dev/da0`.

-- 
Alexander Best
Comment 4 brucec 2010-03-23 07:46:22 UTC
I've just recreated the panic - it seems 10,000,000 entries are 
required to fill virtual memory, not 1,000,000. It takes a very long 
time for gpart to create them, but on reboot after the disk is detected 
the system will wait for a couple of minutes before panicing.

-- 
Bruce Cran
Comment 5 Andrey V. Elsukov freebsd_committer freebsd_triage 2011-01-18 07:32:31 UTC
Responsible Changed
From-To: freebsd-geom->ae

Take it.
Comment 6 dfilter service freebsd_committer freebsd_triage 2011-01-18 09:53:00 UTC
Author: ae
Date: Tue Jan 18 09:52:53 2011
New Revision: 217531
URL: http://svn.freebsd.org/changeset/base/217531

Log:
  Limit maximum number of GPT entries to 4k. It is most realistic value
  and can prevent kernel memory exhausting when big value is specified
  from command line.
  
  Split reading and writing operation to several iteration to do not
  trigger KASSERT when data length is greater than MAXPHYS.
  
  PR:             kern/144962, kern/147851
  MFC after:      2 weeks

Modified:
  head/sys/geom/part/g_part_gpt.c

Modified: head/sys/geom/part/g_part_gpt.c
==============================================================================
--- head/sys/geom/part/g_part_gpt.c	Tue Jan 18 06:24:52 2011	(r217530)
+++ head/sys/geom/part/g_part_gpt.c	Tue Jan 18 09:52:53 2011	(r217531)
@@ -134,7 +134,7 @@ static struct g_part_scheme g_part_gpt_s
 	sizeof(struct g_part_gpt_table),
 	.gps_entrysz = sizeof(struct g_part_gpt_entry),
 	.gps_minent = 128,
-	.gps_maxent = INT_MAX,
+	.gps_maxent = 4096,
 	.gps_bootcodesz = MBRSIZE,
 };
 G_PART_SCHEME_DECLARE(g_part_gpt);
@@ -317,7 +317,7 @@ gpt_read_tbl(struct g_part_gpt_table *ta
 	struct g_provider *pp;
 	struct gpt_ent *ent, *tbl;
 	char *buf, *p;
-	unsigned int idx, sectors, tblsz;
+	unsigned int idx, sectors, tblsz, size;
 	int error;
 
 	if (hdr == NULL)
@@ -329,11 +329,19 @@ gpt_read_tbl(struct g_part_gpt_table *ta
 	table->state[elt] = GPT_STATE_MISSING;
 	tblsz = hdr->hdr_entries * hdr->hdr_entsz;
 	sectors = (tblsz + pp->sectorsize - 1) / pp->sectorsize;
-	buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, 
-	    sectors * pp->sectorsize, &error);
-	if (buf == NULL)
-		return (NULL);
-
+	buf = g_malloc(sectors * pp->sectorsize, M_WAITOK | M_ZERO);
+	for (idx = 0; idx < sectors; idx += MAXPHYS / pp->sectorsize) {
+		size = (sectors - idx > MAXPHYS / pp->sectorsize) ?  MAXPHYS:
+		    (sectors - idx) * pp->sectorsize;
+		p = g_read_data(cp, (table->lba[elt] + idx) * pp->sectorsize,
+		    size, &error);
+		if (p == NULL) {
+			g_free(buf);
+			return (NULL);
+		}
+		bcopy(p, buf + idx * pp->sectorsize, size);
+		g_free(p);
+	}
 	table->state[elt] = GPT_STATE_CORRUPT;
 	if (crc32(buf, tblsz) != hdr->hdr_crc_table) {
 		g_free(buf);
@@ -986,10 +994,15 @@ g_part_gpt_write(struct g_part_table *ba
 	crc = crc32(buf, table->hdr->hdr_size);
 	le32enc(buf + 16, crc);
 
-	error = g_write_data(cp, table->lba[GPT_ELT_PRITBL] * pp->sectorsize,
-	    buf + pp->sectorsize, tblsz * pp->sectorsize);
-	if (error)
-		goto out;
+	for (index = 0; index < tblsz; index += MAXPHYS / pp->sectorsize) {
+		error = g_write_data(cp,
+		    (table->lba[GPT_ELT_PRITBL] + index) * pp->sectorsize,
+		    buf + (index + 1) * pp->sectorsize,
+		    (tblsz - index > MAXPHYS / pp->sectorsize) ? MAXPHYS:
+		    (tblsz - index) * pp->sectorsize);
+		if (error)
+			goto out;
+	}
 	error = g_write_data(cp, table->lba[GPT_ELT_PRIHDR] * pp->sectorsize,
 	    buf, pp->sectorsize);
 	if (error)
@@ -1003,10 +1016,15 @@ g_part_gpt_write(struct g_part_table *ba
 	crc = crc32(buf, table->hdr->hdr_size);
 	le32enc(buf + 16, crc);
 
-	error = g_write_data(cp, table->lba[GPT_ELT_SECTBL] * pp->sectorsize,
-	    buf + pp->sectorsize, tblsz * pp->sectorsize);
-	if (error)
-		goto out;
+	for (index = 0; index < tblsz; index += MAXPHYS / pp->sectorsize) {
+		error = g_write_data(cp,
+		    (table->lba[GPT_ELT_SECTBL] + index) * pp->sectorsize,
+		    buf + (index + 1) * pp->sectorsize,
+		    (tblsz - index > MAXPHYS / pp->sectorsize) ? MAXPHYS:
+		    (tblsz - index) * pp->sectorsize);
+		if (error)
+			goto out;
+	}
 	error = g_write_data(cp, table->lba[GPT_ELT_SECHDR] * pp->sectorsize,
 	    buf, pp->sectorsize);
 
_______________________________________________
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 7 Andrey V. Elsukov freebsd_committer freebsd_triage 2011-01-18 10:10:37 UTC
State Changed
From-To: open->patched

Patched in head/.
Comment 8 dfilter service freebsd_committer freebsd_triage 2011-02-01 09:27:38 UTC
Author: ae
Date: Tue Feb  1 09:27:28 2011
New Revision: 218162
URL: http://svn.freebsd.org/changeset/base/218162

Log:
  MFC r217531:
    Limit maximum number of GPT entries to 4k. It is most realistic value
    and can prevent kernel memory exhausting when big value is specified
    from command line.
  
    Split reading and writing operation to several iterations to do not
    trigger KASSERT when data length is greater than MAXPHYS.
  
    PR:             kern/144962, kern/147851

Modified:
  stable/8/sys/geom/part/g_part_gpt.c
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)

Modified: stable/8/sys/geom/part/g_part_gpt.c
==============================================================================
--- stable/8/sys/geom/part/g_part_gpt.c	Tue Feb  1 09:27:24 2011	(r218161)
+++ stable/8/sys/geom/part/g_part_gpt.c	Tue Feb  1 09:27:28 2011	(r218162)
@@ -134,7 +134,7 @@ static struct g_part_scheme g_part_gpt_s
 	sizeof(struct g_part_gpt_table),
 	.gps_entrysz = sizeof(struct g_part_gpt_entry),
 	.gps_minent = 128,
-	.gps_maxent = INT_MAX,
+	.gps_maxent = 4096,
 	.gps_bootcodesz = MBRSIZE,
 };
 G_PART_SCHEME_DECLARE(g_part_gpt);
@@ -317,7 +317,7 @@ gpt_read_tbl(struct g_part_gpt_table *ta
 	struct g_provider *pp;
 	struct gpt_ent *ent, *tbl;
 	char *buf, *p;
-	unsigned int idx, sectors, tblsz;
+	unsigned int idx, sectors, tblsz, size;
 	int error;
 
 	if (hdr == NULL)
@@ -329,11 +329,19 @@ gpt_read_tbl(struct g_part_gpt_table *ta
 	table->state[elt] = GPT_STATE_MISSING;
 	tblsz = hdr->hdr_entries * hdr->hdr_entsz;
 	sectors = (tblsz + pp->sectorsize - 1) / pp->sectorsize;
-	buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, 
-	    sectors * pp->sectorsize, &error);
-	if (buf == NULL)
-		return (NULL);
-
+	buf = g_malloc(sectors * pp->sectorsize, M_WAITOK | M_ZERO);
+	for (idx = 0; idx < sectors; idx += MAXPHYS / pp->sectorsize) {
+		size = (sectors - idx > MAXPHYS / pp->sectorsize) ?  MAXPHYS:
+		    (sectors - idx) * pp->sectorsize;
+		p = g_read_data(cp, (table->lba[elt] + idx) * pp->sectorsize,
+		    size, &error);
+		if (p == NULL) {
+			g_free(buf);
+			return (NULL);
+		}
+		bcopy(p, buf + idx * pp->sectorsize, size);
+		g_free(p);
+	}
 	table->state[elt] = GPT_STATE_CORRUPT;
 	if (crc32(buf, tblsz) != hdr->hdr_crc_table) {
 		g_free(buf);
@@ -986,10 +994,15 @@ g_part_gpt_write(struct g_part_table *ba
 	crc = crc32(buf, table->hdr->hdr_size);
 	le32enc(buf + 16, crc);
 
-	error = g_write_data(cp, table->lba[GPT_ELT_PRITBL] * pp->sectorsize,
-	    buf + pp->sectorsize, tblsz * pp->sectorsize);
-	if (error)
-		goto out;
+	for (index = 0; index < tblsz; index += MAXPHYS / pp->sectorsize) {
+		error = g_write_data(cp,
+		    (table->lba[GPT_ELT_PRITBL] + index) * pp->sectorsize,
+		    buf + (index + 1) * pp->sectorsize,
+		    (tblsz - index > MAXPHYS / pp->sectorsize) ? MAXPHYS:
+		    (tblsz - index) * pp->sectorsize);
+		if (error)
+			goto out;
+	}
 	error = g_write_data(cp, table->lba[GPT_ELT_PRIHDR] * pp->sectorsize,
 	    buf, pp->sectorsize);
 	if (error)
@@ -1003,10 +1016,15 @@ g_part_gpt_write(struct g_part_table *ba
 	crc = crc32(buf, table->hdr->hdr_size);
 	le32enc(buf + 16, crc);
 
-	error = g_write_data(cp, table->lba[GPT_ELT_SECTBL] * pp->sectorsize,
-	    buf + pp->sectorsize, tblsz * pp->sectorsize);
-	if (error)
-		goto out;
+	for (index = 0; index < tblsz; index += MAXPHYS / pp->sectorsize) {
+		error = g_write_data(cp,
+		    (table->lba[GPT_ELT_SECTBL] + index) * pp->sectorsize,
+		    buf + (index + 1) * pp->sectorsize,
+		    (tblsz - index > MAXPHYS / pp->sectorsize) ? MAXPHYS:
+		    (tblsz - index) * pp->sectorsize);
+		if (error)
+			goto out;
+	}
 	error = g_write_data(cp, table->lba[GPT_ELT_SECHDR] * pp->sectorsize,
 	    buf, pp->sectorsize);
 
_______________________________________________
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 9 Andrey V. Elsukov freebsd_committer freebsd_triage 2011-02-05 14:36:16 UTC
State Changed
From-To: patched->closed

Fixed in head/ and stable/8.