Fix handling of GPT headers when size is > 92 bytes.
It is valid for an on-disk GPT header to report a header size which is greater than 92 bytes. Previously, we would read in the sector and copy only the 92 bytes that we know how to deal with before calculating the checksum for comparison. This meant that when we did the checksum, we overshot the buffer and took in random memory, so the checksum would fail. We now determine the size of the header and allocate enough space to preserve the entire on-disk contents. This allows us to be correctly calculate the checksum and be able to modify and write the header back to the disk, while preserving data that we might not understand. Reported by: Kris Weston Approved by: marcel@ MFC after: 2 weeks
This commit is contained in:
parent
f6eb382c79
commit
1c2dee3cc9
@ -73,7 +73,7 @@ enum gpt_state {
|
||||
struct g_part_gpt_table {
|
||||
struct g_part_table base;
|
||||
u_char mbr[MBRSIZE];
|
||||
struct gpt_hdr hdr;
|
||||
struct gpt_hdr *hdr;
|
||||
quad_t lba[GPT_ELT_COUNT];
|
||||
enum gpt_state state[GPT_ELT_COUNT];
|
||||
};
|
||||
@ -143,13 +143,12 @@ static struct uuid gpt_uuid_linux_swap = GPT_ENT_TYPE_LINUX_SWAP;
|
||||
static struct uuid gpt_uuid_mbr = GPT_ENT_TYPE_MBR;
|
||||
static struct uuid gpt_uuid_unused = GPT_ENT_TYPE_UNUSED;
|
||||
|
||||
static void
|
||||
static struct gpt_hdr *
|
||||
gpt_read_hdr(struct g_part_gpt_table *table, struct g_consumer *cp,
|
||||
enum gpt_elt elt, struct gpt_hdr *hdr)
|
||||
enum gpt_elt elt)
|
||||
{
|
||||
struct uuid uuid;
|
||||
struct gpt_hdr *buf, *hdr;
|
||||
struct g_provider *pp;
|
||||
char *buf;
|
||||
quad_t lba, last;
|
||||
int error;
|
||||
uint32_t crc, sz;
|
||||
@ -161,63 +160,75 @@ gpt_read_hdr(struct g_part_gpt_table *table, struct g_consumer *cp,
|
||||
buf = g_read_data(cp, table->lba[elt] * pp->sectorsize, pp->sectorsize,
|
||||
&error);
|
||||
if (buf == NULL)
|
||||
return;
|
||||
bcopy(buf, hdr, sizeof(*hdr));
|
||||
if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0)
|
||||
return;
|
||||
return (NULL);
|
||||
hdr = NULL;
|
||||
if (memcmp(buf->hdr_sig, GPT_HDR_SIG, sizeof(buf->hdr_sig)) != 0)
|
||||
goto fail;
|
||||
|
||||
table->state[elt] = GPT_STATE_CORRUPT;
|
||||
sz = le32toh(hdr->hdr_size);
|
||||
sz = le32toh(buf->hdr_size);
|
||||
if (sz < 92 || sz > pp->sectorsize)
|
||||
return;
|
||||
crc = le32toh(hdr->hdr_crc_self);
|
||||
hdr->hdr_crc_self = 0;
|
||||
if (crc32(hdr, sz) != crc)
|
||||
return;
|
||||
goto fail;
|
||||
|
||||
hdr = g_malloc(sz, M_WAITOK | M_ZERO);
|
||||
bcopy(buf, hdr, sz);
|
||||
hdr->hdr_size = sz;
|
||||
|
||||
crc = le32toh(buf->hdr_crc_self);
|
||||
buf->hdr_crc_self = 0;
|
||||
if (crc32(buf, sz) != crc)
|
||||
goto fail;
|
||||
hdr->hdr_crc_self = crc;
|
||||
|
||||
table->state[elt] = GPT_STATE_INVALID;
|
||||
hdr->hdr_revision = le32toh(hdr->hdr_revision);
|
||||
hdr->hdr_revision = le32toh(buf->hdr_revision);
|
||||
if (hdr->hdr_revision < 0x00010000)
|
||||
return;
|
||||
hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self);
|
||||
goto fail;
|
||||
hdr->hdr_lba_self = le64toh(buf->hdr_lba_self);
|
||||
if (hdr->hdr_lba_self != table->lba[elt])
|
||||
return;
|
||||
hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt);
|
||||
goto fail;
|
||||
hdr->hdr_lba_alt = le64toh(buf->hdr_lba_alt);
|
||||
|
||||
/* Check the managed area. */
|
||||
hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start);
|
||||
hdr->hdr_lba_start = le64toh(buf->hdr_lba_start);
|
||||
if (hdr->hdr_lba_start < 2 || hdr->hdr_lba_start >= last)
|
||||
return;
|
||||
hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end);
|
||||
goto fail;
|
||||
hdr->hdr_lba_end = le64toh(buf->hdr_lba_end);
|
||||
if (hdr->hdr_lba_end < hdr->hdr_lba_start || hdr->hdr_lba_end >= last)
|
||||
return;
|
||||
goto fail;
|
||||
|
||||
/* Check the table location and size of the table. */
|
||||
hdr->hdr_entries = le32toh(hdr->hdr_entries);
|
||||
hdr->hdr_entsz = le32toh(hdr->hdr_entsz);
|
||||
hdr->hdr_entries = le32toh(buf->hdr_entries);
|
||||
hdr->hdr_entsz = le32toh(buf->hdr_entsz);
|
||||
if (hdr->hdr_entries == 0 || hdr->hdr_entsz < 128 ||
|
||||
(hdr->hdr_entsz & 7) != 0)
|
||||
return;
|
||||
hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table);
|
||||
goto fail;
|
||||
hdr->hdr_lba_table = le64toh(buf->hdr_lba_table);
|
||||
if (hdr->hdr_lba_table < 2 || hdr->hdr_lba_table >= last)
|
||||
return;
|
||||
goto fail;
|
||||
if (hdr->hdr_lba_table >= hdr->hdr_lba_start &&
|
||||
hdr->hdr_lba_table <= hdr->hdr_lba_end)
|
||||
return;
|
||||
goto fail;
|
||||
lba = hdr->hdr_lba_table +
|
||||
(hdr->hdr_entries * hdr->hdr_entsz + pp->sectorsize - 1) /
|
||||
pp->sectorsize - 1;
|
||||
if (lba >= last)
|
||||
return;
|
||||
goto fail;
|
||||
if (lba >= hdr->hdr_lba_start && lba <= hdr->hdr_lba_end)
|
||||
return;
|
||||
goto fail;
|
||||
|
||||
table->state[elt] = GPT_STATE_OK;
|
||||
le_uuid_dec(&hdr->hdr_uuid, &uuid);
|
||||
hdr->hdr_uuid = uuid;
|
||||
hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table);
|
||||
le_uuid_dec(&buf->hdr_uuid, &hdr->hdr_uuid);
|
||||
hdr->hdr_crc_table = le32toh(buf->hdr_crc_table);
|
||||
|
||||
g_free(buf);
|
||||
return (hdr);
|
||||
|
||||
fail:
|
||||
if (hdr != NULL)
|
||||
g_free(hdr);
|
||||
g_free(buf);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static struct gpt_ent *
|
||||
@ -230,6 +241,9 @@ gpt_read_tbl(struct g_part_gpt_table *table, struct g_consumer *cp,
|
||||
unsigned int idx, sectors, tblsz;
|
||||
int error;
|
||||
|
||||
if (hdr == NULL)
|
||||
return (NULL);
|
||||
|
||||
pp = cp->provider;
|
||||
table->lba[elt] = hdr->hdr_lba_table;
|
||||
|
||||
@ -271,6 +285,9 @@ static int
|
||||
gpt_matched_hdrs(struct gpt_hdr *pri, struct gpt_hdr *sec)
|
||||
{
|
||||
|
||||
if (pri == NULL || sec == NULL)
|
||||
return (0);
|
||||
|
||||
if (!EQUUID(&pri->hdr_uuid, &sec->hdr_uuid))
|
||||
return (0);
|
||||
return ((pri->hdr_revision == sec->hdr_revision &&
|
||||
@ -427,17 +444,17 @@ g_part_gpt_create(struct g_part_table *basetable, struct g_part_parms *gpp)
|
||||
table->lba[GPT_ELT_SECHDR] = last;
|
||||
table->lba[GPT_ELT_SECTBL] = last - tblsz;
|
||||
|
||||
bcopy(GPT_HDR_SIG, table->hdr.hdr_sig, sizeof(table->hdr.hdr_sig));
|
||||
table->hdr.hdr_revision = GPT_HDR_REVISION;
|
||||
table->hdr.hdr_size = offsetof(struct gpt_hdr, padding);
|
||||
table->hdr.hdr_lba_start = 2 + tblsz;
|
||||
table->hdr.hdr_lba_end = last - tblsz - 1;
|
||||
kern_uuidgen(&table->hdr.hdr_uuid, 1);
|
||||
table->hdr.hdr_entries = basetable->gpt_entries;
|
||||
table->hdr.hdr_entsz = sizeof(struct gpt_ent);
|
||||
bcopy(GPT_HDR_SIG, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig));
|
||||
table->hdr->hdr_revision = GPT_HDR_REVISION;
|
||||
table->hdr->hdr_size = offsetof(struct gpt_hdr, padding);
|
||||
table->hdr->hdr_lba_start = 2 + tblsz;
|
||||
table->hdr->hdr_lba_end = last - tblsz - 1;
|
||||
kern_uuidgen(&table->hdr->hdr_uuid, 1);
|
||||
table->hdr->hdr_entries = basetable->gpt_entries;
|
||||
table->hdr->hdr_entsz = sizeof(struct gpt_ent);
|
||||
|
||||
basetable->gpt_first = table->hdr.hdr_lba_start;
|
||||
basetable->gpt_last = table->hdr.hdr_lba_end;
|
||||
basetable->gpt_first = table->hdr->hdr_lba_start;
|
||||
basetable->gpt_last = table->hdr->hdr_lba_end;
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -582,7 +599,7 @@ g_part_gpt_probe(struct g_part_table *table, struct g_consumer *cp)
|
||||
static int
|
||||
g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
{
|
||||
struct gpt_hdr prihdr, sechdr;
|
||||
struct gpt_hdr *prihdr, *sechdr;
|
||||
struct gpt_ent *tbl, *pritbl, *sectbl;
|
||||
struct g_provider *pp;
|
||||
struct g_part_gpt_table *table;
|
||||
@ -601,18 +618,18 @@ g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
g_free(buf);
|
||||
|
||||
/* Read the primary header and table. */
|
||||
gpt_read_hdr(table, cp, GPT_ELT_PRIHDR, &prihdr);
|
||||
prihdr = gpt_read_hdr(table, cp, GPT_ELT_PRIHDR);
|
||||
if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK) {
|
||||
pritbl = gpt_read_tbl(table, cp, GPT_ELT_PRITBL, &prihdr);
|
||||
pritbl = gpt_read_tbl(table, cp, GPT_ELT_PRITBL, prihdr);
|
||||
} else {
|
||||
table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING;
|
||||
pritbl = NULL;
|
||||
}
|
||||
|
||||
/* Read the secondary header and table. */
|
||||
gpt_read_hdr(table, cp, GPT_ELT_SECHDR, &sechdr);
|
||||
sechdr = gpt_read_hdr(table, cp, GPT_ELT_SECHDR);
|
||||
if (table->state[GPT_ELT_SECHDR] == GPT_STATE_OK) {
|
||||
sectbl = gpt_read_tbl(table, cp, GPT_ELT_SECTBL, &sechdr);
|
||||
sectbl = gpt_read_tbl(table, cp, GPT_ELT_SECTBL, sechdr);
|
||||
} else {
|
||||
table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING;
|
||||
sectbl = NULL;
|
||||
@ -635,13 +652,17 @@ g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
*/
|
||||
if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK &&
|
||||
table->state[GPT_ELT_SECHDR] == GPT_STATE_OK &&
|
||||
!gpt_matched_hdrs(&prihdr, &sechdr)) {
|
||||
!gpt_matched_hdrs(prihdr, sechdr)) {
|
||||
if (table->state[GPT_ELT_PRITBL] == GPT_STATE_OK) {
|
||||
table->state[GPT_ELT_SECHDR] = GPT_STATE_INVALID;
|
||||
table->state[GPT_ELT_SECTBL] = GPT_STATE_MISSING;
|
||||
g_free(sechdr);
|
||||
sechdr = NULL;
|
||||
} else {
|
||||
table->state[GPT_ELT_PRIHDR] = GPT_STATE_INVALID;
|
||||
table->state[GPT_ELT_PRITBL] = GPT_STATE_MISSING;
|
||||
g_free(prihdr);
|
||||
prihdr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -651,6 +672,8 @@ g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
printf("GEOM: %s: using the secondary instead -- recovery "
|
||||
"strongly advised.\n", pp->name);
|
||||
table->hdr = sechdr;
|
||||
if (prihdr != NULL)
|
||||
g_free(prihdr);
|
||||
tbl = sectbl;
|
||||
if (pritbl != NULL)
|
||||
g_free(pritbl);
|
||||
@ -662,14 +685,16 @@ g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
"suggested.\n", pp->name);
|
||||
}
|
||||
table->hdr = prihdr;
|
||||
if (sechdr != NULL)
|
||||
g_free(sechdr);
|
||||
tbl = pritbl;
|
||||
if (sectbl != NULL)
|
||||
g_free(sectbl);
|
||||
}
|
||||
|
||||
basetable->gpt_first = table->hdr.hdr_lba_start;
|
||||
basetable->gpt_last = table->hdr.hdr_lba_end;
|
||||
basetable->gpt_entries = table->hdr.hdr_entries;
|
||||
basetable->gpt_first = table->hdr->hdr_lba_start;
|
||||
basetable->gpt_last = table->hdr->hdr_lba_end;
|
||||
basetable->gpt_entries = table->hdr->hdr_entries;
|
||||
|
||||
for (index = basetable->gpt_entries - 1; index >= 0; index--) {
|
||||
if (EQUUID(&tbl[index].ent_type, &gpt_uuid_unused))
|
||||
@ -727,7 +752,7 @@ g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
|
||||
pp = cp->provider;
|
||||
table = (struct g_part_gpt_table *)basetable;
|
||||
tlbsz = (table->hdr.hdr_entries * table->hdr.hdr_entsz +
|
||||
tlbsz = (table->hdr->hdr_entries * table->hdr->hdr_entsz +
|
||||
pp->sectorsize - 1) / pp->sectorsize;
|
||||
|
||||
/* Write the PMBR */
|
||||
@ -741,21 +766,21 @@ g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
/* Allocate space for the header and entries. */
|
||||
buf = g_malloc((tlbsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO);
|
||||
|
||||
memcpy(buf, table->hdr.hdr_sig, sizeof(table->hdr.hdr_sig));
|
||||
le32enc(buf + 8, table->hdr.hdr_revision);
|
||||
le32enc(buf + 12, table->hdr.hdr_size);
|
||||
le64enc(buf + 40, table->hdr.hdr_lba_start);
|
||||
le64enc(buf + 48, table->hdr.hdr_lba_end);
|
||||
le_uuid_enc(buf + 56, &table->hdr.hdr_uuid);
|
||||
le32enc(buf + 80, table->hdr.hdr_entries);
|
||||
le32enc(buf + 84, table->hdr.hdr_entsz);
|
||||
memcpy(buf, table->hdr->hdr_sig, sizeof(table->hdr->hdr_sig));
|
||||
le32enc(buf + 8, table->hdr->hdr_revision);
|
||||
le32enc(buf + 12, table->hdr->hdr_size);
|
||||
le64enc(buf + 40, table->hdr->hdr_lba_start);
|
||||
le64enc(buf + 48, table->hdr->hdr_lba_end);
|
||||
le_uuid_enc(buf + 56, &table->hdr->hdr_uuid);
|
||||
le32enc(buf + 80, table->hdr->hdr_entries);
|
||||
le32enc(buf + 84, table->hdr->hdr_entsz);
|
||||
|
||||
LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) {
|
||||
if (baseentry->gpe_deleted)
|
||||
continue;
|
||||
entry = (struct g_part_gpt_entry *)baseentry;
|
||||
index = baseentry->gpe_index - 1;
|
||||
bp = buf + pp->sectorsize + table->hdr.hdr_entsz * index;
|
||||
bp = buf + pp->sectorsize + table->hdr->hdr_entsz * index;
|
||||
le_uuid_enc(bp, &entry->ent.ent_type);
|
||||
le_uuid_enc(bp + 16, &entry->ent.ent_uuid);
|
||||
le64enc(bp + 32, entry->ent.ent_lba_start);
|
||||
@ -766,7 +791,7 @@ g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
}
|
||||
|
||||
crc = crc32(buf + pp->sectorsize,
|
||||
table->hdr.hdr_entries * table->hdr.hdr_entsz);
|
||||
table->hdr->hdr_entries * table->hdr->hdr_entsz);
|
||||
le32enc(buf + 88, crc);
|
||||
|
||||
/* Write primary meta-data. */
|
||||
@ -774,7 +799,7 @@ g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
le64enc(buf + 24, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_self. */
|
||||
le64enc(buf + 32, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_alt. */
|
||||
le64enc(buf + 72, table->lba[GPT_ELT_PRITBL]); /* hdr_lba_table. */
|
||||
crc = crc32(buf, table->hdr.hdr_size);
|
||||
crc = crc32(buf, table->hdr->hdr_size);
|
||||
le32enc(buf + 16, crc);
|
||||
|
||||
error = g_write_data(cp, table->lba[GPT_ELT_PRITBL] * pp->sectorsize,
|
||||
@ -791,7 +816,7 @@ g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
le64enc(buf + 24, table->lba[GPT_ELT_SECHDR]); /* hdr_lba_self. */
|
||||
le64enc(buf + 32, table->lba[GPT_ELT_PRIHDR]); /* hdr_lba_alt. */
|
||||
le64enc(buf + 72, table->lba[GPT_ELT_SECTBL]); /* hdr_lba_table. */
|
||||
crc = crc32(buf, table->hdr.hdr_size);
|
||||
crc = crc32(buf, table->hdr->hdr_size);
|
||||
le32enc(buf + 16, crc);
|
||||
|
||||
error = g_write_data(cp, table->lba[GPT_ELT_SECTBL] * pp->sectorsize,
|
||||
|
Loading…
Reference in New Issue
Block a user