mirror of https://github.com/wolfSSL/wolfBoot.git
gpt: parse MBR and GPT byte-wise so disk boot works big-endian
parent
9fc5f75234
commit
336ee7fd56
|
|
@ -26,12 +26,40 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Every field in an MBR, a GPT structure, a FAT32 BPB and an ext4 superblock
|
||||
* is little-endian ON DISK. Casting a sector to a packed struct and
|
||||
* dereferencing it is therefore only correct on a little-endian host, so all
|
||||
* on-disk values are read through these instead. One definition, used by the
|
||||
* partition layer and by the filesystem parsers alike. */
|
||||
static inline uint16_t gpt_le16(const uint8_t *p)
|
||||
{
|
||||
return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8));
|
||||
}
|
||||
|
||||
static inline uint32_t gpt_le32(const uint8_t *p)
|
||||
{
|
||||
return ((uint32_t)p[0]) | ((uint32_t)p[1] << 8) |
|
||||
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
||||
}
|
||||
|
||||
static inline uint64_t gpt_le64(const uint8_t *p)
|
||||
{
|
||||
return (uint64_t)gpt_le32(p) | ((uint64_t)gpt_le32(p + 4) << 32);
|
||||
}
|
||||
|
||||
|
||||
/* GPT Constants */
|
||||
#define GPT_SECTOR_SIZE 512 /* 0x200 */
|
||||
#define GPT_SIGNATURE 0x5452415020494645ULL /* "EFI PART" */
|
||||
#define GPT_PTYPE_PROTECTIVE 0xEE
|
||||
#define GPT_PART_NAME_SIZE 36
|
||||
#define GPT_MBR_ENTRY_START 0x01BE
|
||||
|
||||
/* MBR partition entry field offsets, shared by src/gpt.c and src/disk.c. */
|
||||
#define GPT_MBR_PTE_PTYPE 0x04
|
||||
#define GPT_MBR_PTE_LBA_FIRST 0x08
|
||||
#define GPT_MBR_PTE_LBA_SIZE 0x0C
|
||||
#define GPT_MBR_PTE_SIZE 0x10
|
||||
#define GPT_MBR_BOOTSIG_OFFSET 0x01FE
|
||||
#define GPT_MBR_BOOTSIG_VALUE 0xAA55
|
||||
#define GPT_PART_ENTRY_SIZE 256
|
||||
|
|
|
|||
37
src/disk.c
37
src/disk.c
|
|
@ -60,26 +60,33 @@ static struct disk_drive Drives[MAX_DISKS] = {0};
|
|||
static int disk_open_mbr(struct disk_drive *drive, const uint8_t *mbr_sector)
|
||||
{
|
||||
uint32_t i;
|
||||
const struct gpt_mbr_part_entry *pte;
|
||||
const uint8_t *pte;
|
||||
uint8_t ptype;
|
||||
uint32_t lba_first, lba_size;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
pte = (const struct gpt_mbr_part_entry *)(mbr_sector +
|
||||
GPT_MBR_ENTRY_START + (i * sizeof(struct gpt_mbr_part_entry)));
|
||||
/* MBR fields are little-endian on disk, so they are read byte-wise
|
||||
* rather than through a packed struct, which would only be correct
|
||||
* on a little-endian host. */
|
||||
pte = mbr_sector + GPT_MBR_ENTRY_START + (i * GPT_MBR_PTE_SIZE);
|
||||
ptype = pte[GPT_MBR_PTE_PTYPE];
|
||||
lba_first = gpt_le32(pte + GPT_MBR_PTE_LBA_FIRST);
|
||||
lba_size = gpt_le32(pte + GPT_MBR_PTE_LBA_SIZE);
|
||||
|
||||
/* Skip empty entries (type 0) and extended partition types */
|
||||
if (pte->ptype == 0x00 || pte->ptype == 0x05 || pte->ptype == 0x0F ||
|
||||
pte->ptype == 0x85) {
|
||||
if (ptype == 0x00 || ptype == 0x05 || ptype == 0x0F ||
|
||||
ptype == 0x85) {
|
||||
continue;
|
||||
}
|
||||
if (pte->lba_first == 0 || pte->lba_size == 0) {
|
||||
if (lba_first == 0 || lba_size == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t n = drive->n_parts;
|
||||
uint64_t start_bytes = (uint64_t)pte->lba_first * GPT_SECTOR_SIZE;
|
||||
uint64_t start_bytes = (uint64_t)lba_first * GPT_SECTOR_SIZE;
|
||||
uint64_t end_bytes = start_bytes +
|
||||
((uint64_t)pte->lba_size * GPT_SECTOR_SIZE) - 1;
|
||||
((uint64_t)lba_size * GPT_SECTOR_SIZE) - 1;
|
||||
|
||||
if (n >= MAX_PARTITIONS)
|
||||
break;
|
||||
|
|
@ -91,9 +98,9 @@ static int disk_open_mbr(struct disk_drive *drive, const uint8_t *mbr_sector)
|
|||
drive->n_parts++;
|
||||
|
||||
wolfBoot_printf(" MBR part %u: type=0x%02x, start=0x%x, "
|
||||
"size=%uMB\r\n", i + 1, pte->ptype,
|
||||
"size=%uMB\r\n", i + 1, ptype,
|
||||
(uint32_t)start_bytes,
|
||||
(uint32_t)(pte->lba_size / 2048));
|
||||
(uint32_t)(lba_size / 2048));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -248,11 +255,11 @@ int disk_open(int drv)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
const uint16_t *boot_sig = (const uint16_t *)(sector +
|
||||
GPT_MBR_BOOTSIG_OFFSET);
|
||||
|
||||
/* Check MBR boot signature (0xAA55) */
|
||||
if (*boot_sig != GPT_MBR_BOOTSIG_VALUE) {
|
||||
/* Check MBR boot signature (0xAA55). Read byte-wise: it is stored
|
||||
* little-endian on disk, so a uint16_t cast reads it swapped on a
|
||||
* big-endian host. */
|
||||
if (gpt_le16(sector + GPT_MBR_BOOTSIG_OFFSET) !=
|
||||
GPT_MBR_BOOTSIG_VALUE) {
|
||||
wolfBoot_printf("No valid partition table found\r\n");
|
||||
Drives[drv].is_open = 0;
|
||||
return -1;
|
||||
|
|
|
|||
111
src/gpt.c
111
src/gpt.c
|
|
@ -34,6 +34,25 @@
|
|||
|
||||
#include "gpt.h"
|
||||
|
||||
#define GPT_HDR_SIGNATURE 0x00
|
||||
#define GPT_HDR_REVISION 0x08
|
||||
#define GPT_HDR_SIZE 0x0C
|
||||
#define GPT_HDR_CRC32 0x10
|
||||
#define GPT_HDR_MAIN_LBA 0x18
|
||||
#define GPT_HDR_BACKUP_LBA 0x20
|
||||
#define GPT_HDR_FIRST_USABLE 0x28
|
||||
#define GPT_HDR_LAST_USABLE 0x30
|
||||
#define GPT_HDR_START_ARRAY 0x48
|
||||
#define GPT_HDR_N_PART 0x50
|
||||
#define GPT_HDR_ARRAY_SZ 0x54
|
||||
#define GPT_HDR_PART_CRC 0x58
|
||||
#define GPT_HDR_MIN_SIZE 0x5C
|
||||
|
||||
#define GPT_PE_TYPE 0x00
|
||||
#define GPT_PE_FIRST 0x20
|
||||
#define GPT_PE_LAST 0x28
|
||||
#define GPT_PE_NAME 0x38
|
||||
|
||||
void gpt_crc32_init(struct gpt_crc32_ctx *ctx)
|
||||
{
|
||||
if (ctx != NULL) {
|
||||
|
|
@ -82,8 +101,7 @@ uint32_t gpt_crc32_final(const struct gpt_crc32_ctx *ctx)
|
|||
*/
|
||||
int gpt_check_mbr_protective(const uint8_t *mbr_sector, uint32_t *gpt_lba)
|
||||
{
|
||||
const struct gpt_mbr_part_entry *pte;
|
||||
const uint16_t *boot_sig;
|
||||
const uint8_t *pte;
|
||||
uint32_t i;
|
||||
int found = 0;
|
||||
uint32_t lba = 0;
|
||||
|
|
@ -93,17 +111,16 @@ int gpt_check_mbr_protective(const uint8_t *mbr_sector, uint32_t *gpt_lba)
|
|||
}
|
||||
|
||||
/* Check boot signature at offset 0x1FE */
|
||||
boot_sig = (const uint16_t *)(mbr_sector + GPT_MBR_BOOTSIG_OFFSET);
|
||||
if (*boot_sig != GPT_MBR_BOOTSIG_VALUE) {
|
||||
if (gpt_le16(mbr_sector + GPT_MBR_BOOTSIG_OFFSET) !=
|
||||
GPT_MBR_BOOTSIG_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Scan all 4 MBR partition entries for protective GPT type (0xEE) */
|
||||
for (i = 0; i < 4; i++) {
|
||||
pte = (const struct gpt_mbr_part_entry *)(mbr_sector +
|
||||
GPT_MBR_ENTRY_START + (i * sizeof(struct gpt_mbr_part_entry)));
|
||||
if (pte->ptype == GPT_PTYPE_PROTECTIVE) {
|
||||
lba = pte->lba_first;
|
||||
pte = mbr_sector + GPT_MBR_ENTRY_START + (i * GPT_MBR_PTE_SIZE);
|
||||
if (pte[GPT_MBR_PTE_PTYPE] == GPT_PTYPE_PROTECTIVE) {
|
||||
lba = gpt_le32(pte + GPT_MBR_PTE_LBA_FIRST);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -132,35 +149,49 @@ int gpt_check_mbr_protective(const uint8_t *mbr_sector, uint32_t *gpt_lba)
|
|||
*/
|
||||
int gpt_parse_header(const uint8_t *sector, struct guid_ptable *hdr)
|
||||
{
|
||||
const struct guid_ptable *src;
|
||||
struct guid_ptable tmp;
|
||||
uint8_t tmp[GPT_SECTOR_SIZE];
|
||||
struct gpt_crc32_ctx crc;
|
||||
uint32_t hdr_size;
|
||||
|
||||
if (sector == NULL || hdr == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
src = (const struct guid_ptable *)sector;
|
||||
|
||||
/* Validate GPT signature */
|
||||
if (src->signature != GPT_SIGNATURE) {
|
||||
if (gpt_le64(sector + GPT_HDR_SIGNATURE) != GPT_SIGNATURE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (src->hdr_size < 0x5C || src->hdr_size > GPT_SECTOR_SIZE) {
|
||||
hdr_size = gpt_le32(sector + GPT_HDR_SIZE);
|
||||
if (hdr_size < GPT_HDR_MIN_SIZE || hdr_size > GPT_SECTOR_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&tmp, src, sizeof(tmp));
|
||||
tmp.hdr_crc32 = 0;
|
||||
/* The header CRC is computed over the on-disk bytes with its own field
|
||||
* zeroed, so it has to run on a copy of the raw sector rather than on a
|
||||
* host-order struct. Same stack cost as the struct copy it replaces. */
|
||||
memcpy(tmp, sector, sizeof(tmp));
|
||||
memset(tmp + GPT_HDR_CRC32, 0, 4);
|
||||
gpt_crc32_init(&crc);
|
||||
gpt_crc32_update(&crc, (const uint8_t *)&tmp, src->hdr_size);
|
||||
if (gpt_crc32_final(&crc) != src->hdr_crc32) {
|
||||
gpt_crc32_update(&crc, tmp, hdr_size);
|
||||
if (gpt_crc32_final(&crc) != gpt_le32(sector + GPT_HDR_CRC32)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy header to output */
|
||||
memcpy(hdr, src, sizeof(struct guid_ptable));
|
||||
/* Populate the output in HOST order. */
|
||||
memset(hdr, 0, sizeof(struct guid_ptable));
|
||||
hdr->signature = gpt_le64(sector + GPT_HDR_SIGNATURE);
|
||||
hdr->revision = gpt_le32(sector + GPT_HDR_REVISION);
|
||||
hdr->hdr_size = hdr_size;
|
||||
hdr->hdr_crc32 = gpt_le32(sector + GPT_HDR_CRC32);
|
||||
hdr->main_lba = gpt_le64(sector + GPT_HDR_MAIN_LBA);
|
||||
hdr->backup_lba = gpt_le64(sector + GPT_HDR_BACKUP_LBA);
|
||||
hdr->first_usable = gpt_le64(sector + GPT_HDR_FIRST_USABLE);
|
||||
hdr->last_usable = gpt_le64(sector + GPT_HDR_LAST_USABLE);
|
||||
hdr->start_array = gpt_le64(sector + GPT_HDR_START_ARRAY);
|
||||
hdr->n_part = gpt_le32(sector + GPT_HDR_N_PART);
|
||||
hdr->array_sz = gpt_le32(sector + GPT_HDR_ARRAY_SZ);
|
||||
hdr->part_crc = gpt_le32(sector + GPT_HDR_PART_CRC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -180,7 +211,8 @@ int gpt_parse_header(const uint8_t *sector, struct guid_ptable *hdr)
|
|||
int gpt_parse_partition(const uint8_t *entry_data, uint32_t entry_size,
|
||||
struct gpt_part_info *part)
|
||||
{
|
||||
const struct gpt_part_entry *pe;
|
||||
uint64_t first, last;
|
||||
uint32_t i;
|
||||
|
||||
if (entry_data == NULL || part == NULL) {
|
||||
return -1;
|
||||
|
|
@ -190,34 +222,41 @@ int gpt_parse_partition(const uint8_t *entry_data, uint32_t entry_size,
|
|||
return -1;
|
||||
}
|
||||
|
||||
pe = (const struct gpt_part_entry *)entry_data;
|
||||
|
||||
/* Check if partition entry is empty (type GUID is all zeros) */
|
||||
if (pe->type[0] == 0 && pe->type[1] == 0) {
|
||||
/* Check if partition entry is empty (type GUID is all zeros). The GUID
|
||||
* is compared as raw bytes, so its byte order does not matter. */
|
||||
if ((gpt_le64(entry_data + GPT_PE_TYPE) == 0) &&
|
||||
(gpt_le64(entry_data + GPT_PE_TYPE + 8) == 0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
first = gpt_le64(entry_data + GPT_PE_FIRST);
|
||||
last = gpt_le64(entry_data + GPT_PE_LAST);
|
||||
|
||||
/* Validate geometry */
|
||||
if (pe->first > pe->last) {
|
||||
if (first > last) {
|
||||
return -1;
|
||||
}
|
||||
/* LBA 0 is the protective MBR; no valid partition can end there */
|
||||
if (pe->last == 0) {
|
||||
if (last == 0) {
|
||||
return -1;
|
||||
}
|
||||
/* Reject extents whose byte offset would overflow uint64_t: (pe->last + 1)
|
||||
* must not wrap and (pe->last + 1) * GPT_SECTOR_SIZE must stay representable.
|
||||
* Without this, pe->last = UINT64_MAX yields part->end = UINT64_MAX, which
|
||||
* defeats the bounds check in disk_part_read(). pe->first <= pe->last, so
|
||||
* bounding pe->last also bounds the part->start multiply. */
|
||||
if (pe->last >= (UINT64_MAX / GPT_SECTOR_SIZE)) {
|
||||
/* Reject extents whose byte offset would overflow uint64_t: (last + 1)
|
||||
* must not wrap and (last + 1) * GPT_SECTOR_SIZE must stay representable.
|
||||
* Without this, last = UINT64_MAX yields part->end = UINT64_MAX, which
|
||||
* defeats the bounds check in disk_part_read(). first <= last, so
|
||||
* bounding last also bounds the part->start multiply. */
|
||||
if (last >= (UINT64_MAX / GPT_SECTOR_SIZE)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Extract partition info (convert LBA to byte offsets) */
|
||||
part->start = pe->first * GPT_SECTOR_SIZE;
|
||||
part->end = ((pe->last + 1) * GPT_SECTOR_SIZE) - 1;
|
||||
memcpy(part->name, pe->name, sizeof(part->name));
|
||||
part->start = first * GPT_SECTOR_SIZE;
|
||||
part->end = ((last + 1) * GPT_SECTOR_SIZE) - 1;
|
||||
/* The name is UTF-16LE on disk; store it in host order so
|
||||
* gpt_part_name_eq() can compare code units directly. */
|
||||
for (i = 0; i < GPT_PART_NAME_SIZE; i++) {
|
||||
part->name[i] = gpt_le16(entry_data + GPT_PE_NAME + (i * 2U));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,18 +86,67 @@ static uint32_t test_crc32(const uint8_t *data, uint32_t len)
|
|||
return ~crc;
|
||||
}
|
||||
|
||||
static void finalize_gpt_header_crc(struct guid_ptable *hdr)
|
||||
/* Every on-disk field is little-endian regardless of host byte order, so the
|
||||
* fixture writes bytes explicitly instead of assigning packed struct members.
|
||||
* Assigning members only produces correct on-disk bytes on a little-endian
|
||||
* host, which would make these tests unable to run big-endian. */
|
||||
static void d_put16(uint8_t *p, uint16_t v)
|
||||
{
|
||||
hdr->hdr_crc32 = 0;
|
||||
hdr->hdr_crc32 = test_crc32((const uint8_t *)hdr, hdr->hdr_size);
|
||||
p[0] = (uint8_t)(v & 0xFFU);
|
||||
p[1] = (uint8_t)((v >> 8) & 0xFFU);
|
||||
}
|
||||
|
||||
static void finalize_gpt_part_array_crc(struct guid_ptable *hdr)
|
||||
static void d_put32(uint8_t *p, uint32_t v)
|
||||
{
|
||||
uint32_t array_len = hdr->n_part * hdr->array_sz;
|
||||
uint8_t *array = fake_disk + (hdr->start_array * GPT_SECTOR_SIZE);
|
||||
p[0] = (uint8_t)(v & 0xFFU);
|
||||
p[1] = (uint8_t)((v >> 8) & 0xFFU);
|
||||
p[2] = (uint8_t)((v >> 16) & 0xFFU);
|
||||
p[3] = (uint8_t)((v >> 24) & 0xFFU);
|
||||
}
|
||||
|
||||
hdr->part_crc = test_crc32(array, array_len);
|
||||
static void d_put64(uint8_t *p, uint64_t v)
|
||||
{
|
||||
d_put32(p, (uint32_t)(v & 0xFFFFFFFFU));
|
||||
d_put32(p + 4, (uint32_t)((v >> 32) & 0xFFFFFFFFU));
|
||||
}
|
||||
|
||||
/* GPT on-disk offsets (see src/gpt.c) */
|
||||
#define D_HDR_SIGNATURE 0x00
|
||||
#define D_HDR_REVISION 0x08
|
||||
#define D_HDR_SIZE 0x0C
|
||||
#define D_HDR_CRC32 0x10
|
||||
#define D_HDR_START_ARRAY 0x48
|
||||
#define D_HDR_N_PART 0x50
|
||||
#define D_HDR_ARRAY_SZ 0x54
|
||||
#define D_HDR_PART_CRC 0x58
|
||||
#define D_PE_TYPE 0x00
|
||||
#define D_PE_UUID 0x10
|
||||
#define D_PE_FIRST 0x20
|
||||
#define D_PE_LAST 0x28
|
||||
#define D_PE_NAME 0x38
|
||||
|
||||
static void finalize_gpt_header_crc(uint8_t *hdr)
|
||||
{
|
||||
uint32_t hdr_size = 92;
|
||||
|
||||
d_put32(hdr + D_HDR_CRC32, 0);
|
||||
d_put32(hdr + D_HDR_CRC32, test_crc32(hdr, hdr_size));
|
||||
}
|
||||
|
||||
static uint32_t d_get32(const uint8_t *p)
|
||||
{
|
||||
return ((uint32_t)p[0]) | ((uint32_t)p[1] << 8) |
|
||||
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
|
||||
}
|
||||
|
||||
static void finalize_gpt_part_array_crc(uint8_t *hdr)
|
||||
{
|
||||
uint32_t start_array = d_get32(hdr + D_HDR_START_ARRAY);
|
||||
uint32_t n_part = d_get32(hdr + D_HDR_N_PART);
|
||||
uint32_t array_sz = d_get32(hdr + D_HDR_ARRAY_SZ);
|
||||
uint8_t *array = fake_disk + (start_array * GPT_SECTOR_SIZE);
|
||||
|
||||
d_put32(hdr + D_HDR_PART_CRC, test_crc32(array, n_part * array_sz));
|
||||
}
|
||||
|
||||
/* --- Helpers to build fake disk layouts --- */
|
||||
|
|
@ -106,60 +155,57 @@ static void finalize_gpt_part_array_crc(struct guid_ptable *hdr)
|
|||
* Uses memcpy to avoid unaligned-pointer warnings on packed structs. */
|
||||
static void write_utf16(void *dst, const char *ascii, unsigned int max)
|
||||
{
|
||||
uint16_t *p = (uint16_t *)dst;
|
||||
uint8_t *p = (uint8_t *)dst;
|
||||
unsigned int i;
|
||||
uint16_t val;
|
||||
|
||||
memset(dst, 0, max * sizeof(uint16_t));
|
||||
for (i = 0; i < max && ascii[i]; i++) {
|
||||
val = (uint16_t)ascii[i];
|
||||
memcpy(&p[i], &val, sizeof(val));
|
||||
d_put16(p + (i * 2U), (uint16_t)ascii[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Populate fake_disk with a valid protective-MBR + GPT header + N entries. */
|
||||
static void build_gpt_disk(void)
|
||||
{
|
||||
struct gpt_mbr_part_entry *mbr_entry;
|
||||
uint16_t *boot_sig;
|
||||
struct guid_ptable *gpt_hdr;
|
||||
struct gpt_part_entry *pe;
|
||||
uint8_t *mbr_entry;
|
||||
uint8_t *gpt_hdr;
|
||||
uint8_t *pe;
|
||||
|
||||
memset(fake_disk, 0, FAKE_DISK_SIZE);
|
||||
|
||||
/* --- Sector 0: MBR --- */
|
||||
mbr_entry = (struct gpt_mbr_part_entry *)(fake_disk + GPT_MBR_ENTRY_START);
|
||||
mbr_entry->ptype = GPT_PTYPE_PROTECTIVE; /* 0xEE */
|
||||
mbr_entry->lba_first = 1;
|
||||
mbr_entry->lba_size = 0xFFFFFFFF;
|
||||
mbr_entry = fake_disk + GPT_MBR_ENTRY_START;
|
||||
mbr_entry[0x04] = GPT_PTYPE_PROTECTIVE; /* 0xEE */
|
||||
d_put32(mbr_entry + 0x08, 1); /* lba_first */
|
||||
d_put32(mbr_entry + 0x0C, 0xFFFFFFFF); /* lba_size */
|
||||
|
||||
boot_sig = (uint16_t *)(fake_disk + GPT_MBR_BOOTSIG_OFFSET);
|
||||
*boot_sig = GPT_MBR_BOOTSIG_VALUE; /* 0xAA55 */
|
||||
d_put16(fake_disk + GPT_MBR_BOOTSIG_OFFSET, GPT_MBR_BOOTSIG_VALUE);
|
||||
|
||||
/* --- Sector 1: GPT header --- */
|
||||
gpt_hdr = (struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
gpt_hdr->signature = GPT_SIGNATURE;
|
||||
gpt_hdr->revision = 0x00010000;
|
||||
gpt_hdr->hdr_size = 92;
|
||||
gpt_hdr->start_array = 2; /* partition entries start at LBA 2 */
|
||||
gpt_hdr->n_part = 2;
|
||||
gpt_hdr->array_sz = 128; /* bytes per entry */
|
||||
gpt_hdr = fake_disk + GPT_SECTOR_SIZE;
|
||||
d_put64(gpt_hdr + D_HDR_SIGNATURE, GPT_SIGNATURE);
|
||||
d_put32(gpt_hdr + D_HDR_REVISION, 0x00010000);
|
||||
d_put32(gpt_hdr + D_HDR_SIZE, 92);
|
||||
d_put64(gpt_hdr + D_HDR_START_ARRAY, 2); /* entries start at LBA 2 */
|
||||
d_put32(gpt_hdr + D_HDR_N_PART, 2);
|
||||
d_put32(gpt_hdr + D_HDR_ARRAY_SZ, 128); /* bytes per entry */
|
||||
|
||||
/* --- Sector 2: Partition entries --- */
|
||||
/* Entry 0: name "boot" */
|
||||
pe = (struct gpt_part_entry *)(fake_disk + 2 * GPT_SECTOR_SIZE);
|
||||
pe->type[0] = 0x0001020304050607ULL; /* non-zero type GUID */
|
||||
pe->type[1] = 0x08090A0B0C0D0E0FULL;
|
||||
pe->first = PART0_OFF;
|
||||
pe->last = PART0_END;
|
||||
write_utf16(pe->name, "boot", GPT_PART_NAME_SIZE);
|
||||
pe = fake_disk + (2 * GPT_SECTOR_SIZE);
|
||||
d_put64(pe + D_PE_TYPE, 0x0001020304050607ULL); /* non-zero type GUID */
|
||||
d_put64(pe + D_PE_TYPE + 8, 0x08090A0B0C0D0E0FULL);
|
||||
d_put64(pe + D_PE_FIRST, PART0_OFF);
|
||||
d_put64(pe + D_PE_LAST, PART0_END);
|
||||
write_utf16(pe + D_PE_NAME, "boot", GPT_PART_NAME_SIZE);
|
||||
|
||||
/* Entry 1: name "rootfs" */
|
||||
pe = (struct gpt_part_entry *)(fake_disk + 2 * GPT_SECTOR_SIZE + 128);
|
||||
pe->type[0] = 0x1011121314151617ULL;
|
||||
pe->type[1] = 0x18191A1B1C1D1E1FULL;
|
||||
pe->first = PART1_OFF;
|
||||
pe->last = PART1_END;
|
||||
write_utf16(pe->name, "rootfs", GPT_PART_NAME_SIZE);
|
||||
pe = fake_disk + (2 * GPT_SECTOR_SIZE) + 128;
|
||||
d_put64(pe + D_PE_TYPE, 0x1011121314151617ULL);
|
||||
d_put64(pe + D_PE_TYPE + 8, 0x18191A1B1C1D1E1FULL);
|
||||
d_put64(pe + D_PE_FIRST, PART1_OFF);
|
||||
d_put64(pe + D_PE_LAST, PART1_END);
|
||||
write_utf16(pe + D_PE_NAME, "rootfs", GPT_PART_NAME_SIZE);
|
||||
|
||||
/* Fill partition data areas with known patterns for read tests.
|
||||
* GPT last LBA is inclusive, so partition spans
|
||||
|
|
@ -179,23 +225,23 @@ static void build_gpt_disk(void)
|
|||
*/
|
||||
static void build_mbr_disk(void)
|
||||
{
|
||||
struct gpt_mbr_part_entry *pte;
|
||||
uint8_t *pte;
|
||||
uint16_t *boot_sig;
|
||||
|
||||
memset(fake_disk, 0, FAKE_DISK_SIZE);
|
||||
|
||||
/* MBR entry 0: FAT32 LBA */
|
||||
pte = (struct gpt_mbr_part_entry *)(fake_disk + GPT_MBR_ENTRY_START);
|
||||
pte->ptype = 0x0C; /* FAT32 LBA */
|
||||
pte->lba_first = 16;
|
||||
pte->lba_size = 32;
|
||||
pte = (uint8_t *)(fake_disk + GPT_MBR_ENTRY_START);
|
||||
pte[0x04] = (uint8_t)(0x0C); /* FAT32 LBA */
|
||||
d_put32(pte + 0x08, 16);
|
||||
d_put32(pte + 0x0C, 32);
|
||||
|
||||
/* MBR entry 1: Linux */
|
||||
pte = (struct gpt_mbr_part_entry *)(fake_disk + GPT_MBR_ENTRY_START +
|
||||
pte = (uint8_t *)(fake_disk + GPT_MBR_ENTRY_START +
|
||||
sizeof(struct gpt_mbr_part_entry));
|
||||
pte->ptype = 0x83; /* Linux */
|
||||
pte->lba_first = 48;
|
||||
pte->lba_size = 64;
|
||||
pte[0x04] = (uint8_t)(0x83); /* Linux */
|
||||
d_put32(pte + 0x08, 48);
|
||||
d_put32(pte + 0x0C, 64);
|
||||
|
||||
boot_sig = (uint16_t *)(fake_disk + GPT_MBR_BOOTSIG_OFFSET);
|
||||
*boot_sig = GPT_MBR_BOOTSIG_VALUE;
|
||||
|
|
@ -240,23 +286,21 @@ START_TEST(test_gpt_parse_header)
|
|||
|
||||
/* Corrupt a header field without updating CRC */
|
||||
{
|
||||
struct guid_ptable *gpt_hdr =
|
||||
(struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
gpt_hdr->n_part = 3;
|
||||
uint8_t *gpt_hdr = (uint8_t *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
d_put32(gpt_hdr + D_HDR_N_PART, 3);
|
||||
ck_assert_int_eq(
|
||||
gpt_parse_header(fake_disk + GPT_SECTOR_SIZE, &hdr), -1);
|
||||
gpt_hdr->n_part = 2;
|
||||
d_put32(gpt_hdr + D_HDR_N_PART, 2);
|
||||
finalize_gpt_header_crc(gpt_hdr);
|
||||
}
|
||||
|
||||
/* Corrupt signature in the real header */
|
||||
{
|
||||
struct guid_ptable *gpt_hdr =
|
||||
(struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
gpt_hdr->signature = 0;
|
||||
uint8_t *gpt_hdr = (uint8_t *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
d_put64(gpt_hdr + D_HDR_SIGNATURE, 0);
|
||||
ck_assert_int_eq(
|
||||
gpt_parse_header(fake_disk + GPT_SECTOR_SIZE, &hdr), -1);
|
||||
gpt_hdr->signature = GPT_SIGNATURE;
|
||||
d_put64(gpt_hdr + D_HDR_SIGNATURE, GPT_SIGNATURE);
|
||||
finalize_gpt_header_crc(gpt_hdr);
|
||||
}
|
||||
|
||||
|
|
@ -342,13 +386,13 @@ END_TEST
|
|||
|
||||
START_TEST(test_disk_open_gpt_rejects_part_array_crc_mismatch)
|
||||
{
|
||||
struct gpt_part_entry *pe;
|
||||
uint8_t *pe;
|
||||
|
||||
build_gpt_disk();
|
||||
|
||||
pe = (struct gpt_part_entry *)(fake_disk + 2 * GPT_SECTOR_SIZE);
|
||||
pe->first = PART1_OFF;
|
||||
pe->last = PART1_END;
|
||||
pe = (uint8_t *)(fake_disk + 2 * GPT_SECTOR_SIZE);
|
||||
d_put64(pe + D_PE_FIRST, PART1_OFF);
|
||||
d_put64(pe + D_PE_LAST, PART1_END);
|
||||
|
||||
ck_assert_int_eq(disk_open(0), -1);
|
||||
ck_assert_int_eq(Drives[0].is_open, 0);
|
||||
|
|
@ -512,14 +556,14 @@ START_TEST(test_gpt_partition_end_inclusive)
|
|||
/* GPT spec: last LBA is inclusive. A partition with first=10, last=20
|
||||
* spans 11 sectors. End byte = (20+1)*512 - 1. */
|
||||
uint8_t entry[128];
|
||||
struct gpt_part_entry *pe = (struct gpt_part_entry *)entry;
|
||||
uint8_t *pe = (uint8_t *)entry;
|
||||
struct gpt_part_info info;
|
||||
|
||||
memset(entry, 0, sizeof(entry));
|
||||
pe->type[0] = 0x0001020304050607ULL;
|
||||
pe->type[1] = 0x08090A0B0C0D0E0FULL;
|
||||
pe->first = 10;
|
||||
pe->last = 20;
|
||||
d_put64(pe + D_PE_TYPE, 0x0001020304050607ULL);
|
||||
d_put64(pe + D_PE_TYPE + 8, 0x08090A0B0C0D0E0FULL);
|
||||
d_put64(pe + D_PE_FIRST, 10);
|
||||
d_put64(pe + D_PE_LAST, 20);
|
||||
|
||||
ck_assert_int_eq(gpt_parse_partition(entry, 128, &info), 0);
|
||||
ck_assert_uint_eq(info.start, 10 * 512);
|
||||
|
|
@ -532,13 +576,13 @@ END_TEST
|
|||
START_TEST(test_disk_open_failure_clears_is_open)
|
||||
{
|
||||
/* If GPT header parse fails, is_open must be reset to 0. */
|
||||
struct guid_ptable *gpt_hdr;
|
||||
uint8_t *gpt_hdr;
|
||||
|
||||
build_gpt_disk();
|
||||
|
||||
/* Corrupt the GPT header signature */
|
||||
gpt_hdr = (struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
gpt_hdr->signature = 0xDEADBEEF;
|
||||
gpt_hdr = (uint8_t *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
d_put64(gpt_hdr + D_HDR_SIGNATURE, 0xDEADBEEF);
|
||||
|
||||
ck_assert_int_eq(disk_open(0), -1);
|
||||
ck_assert_int_eq(Drives[0].is_open, 0);
|
||||
|
|
@ -550,14 +594,14 @@ START_TEST(test_gpt_parse_partition_last_zero)
|
|||
/* If first=0, last=0 with non-zero type GUID, (last+1)*512 - 1 would
|
||||
* be 511 but LBA 0 is the protective MBR — must reject. */
|
||||
uint8_t entry[128];
|
||||
struct gpt_part_entry *pe = (struct gpt_part_entry *)entry;
|
||||
uint8_t *pe = (uint8_t *)entry;
|
||||
struct gpt_part_info info;
|
||||
|
||||
memset(entry, 0, sizeof(entry));
|
||||
pe->type[0] = 0x0001020304050607ULL;
|
||||
pe->type[1] = 0x08090A0B0C0D0E0FULL;
|
||||
pe->first = 0;
|
||||
pe->last = 0;
|
||||
d_put64(pe + D_PE_TYPE, 0x0001020304050607ULL);
|
||||
d_put64(pe + D_PE_TYPE + 8, 0x08090A0B0C0D0E0FULL);
|
||||
d_put64(pe + D_PE_FIRST, 0);
|
||||
d_put64(pe + D_PE_LAST, 0);
|
||||
|
||||
ck_assert_int_eq(gpt_parse_partition(entry, 128, &info), -1);
|
||||
}
|
||||
|
|
@ -565,18 +609,18 @@ END_TEST
|
|||
|
||||
START_TEST(test_gpt_parse_partition_last_overflow)
|
||||
{
|
||||
/* pe->last = UINT64_MAX passes the first>last and last==0 guards, but
|
||||
* (pe->last + 1) * GPT_SECTOR_SIZE - 1 would wrap to UINT64_MAX, defeating
|
||||
/* last = UINT64_MAX passes the first>last and last==0 guards, but
|
||||
* (last + 1) * GPT_SECTOR_SIZE - 1 would wrap to UINT64_MAX, defeating
|
||||
* the bounds check in disk_part_read. Must be rejected. */
|
||||
uint8_t entry[128];
|
||||
struct gpt_part_entry *pe = (struct gpt_part_entry *)entry;
|
||||
uint8_t *pe = (uint8_t *)entry;
|
||||
struct gpt_part_info info;
|
||||
|
||||
memset(entry, 0, sizeof(entry));
|
||||
pe->type[0] = 0x0001020304050607ULL;
|
||||
pe->type[1] = 0x08090A0B0C0D0E0FULL;
|
||||
pe->first = 1;
|
||||
pe->last = 0xFFFFFFFFFFFFFFFFULL;
|
||||
d_put64(pe + D_PE_TYPE, 0x0001020304050607ULL);
|
||||
d_put64(pe + D_PE_TYPE + 8, 0x08090A0B0C0D0E0FULL);
|
||||
d_put64(pe + D_PE_FIRST, 1);
|
||||
d_put64(pe + D_PE_LAST, 0xFFFFFFFFFFFFFFFFULL);
|
||||
|
||||
ck_assert_int_eq(gpt_parse_partition(entry, 128, &info), -1);
|
||||
}
|
||||
|
|
@ -597,13 +641,13 @@ START_TEST(test_disk_open_mbr_bad_bootsig)
|
|||
{
|
||||
/* MBR disk without valid 0xAA55 boot signature and no protective 0xEE.
|
||||
* Falls through GPT check, then fails on boot_sig validation. */
|
||||
struct gpt_mbr_part_entry *pte;
|
||||
uint8_t *pte;
|
||||
|
||||
memset(fake_disk, 0, FAKE_DISK_SIZE);
|
||||
pte = (struct gpt_mbr_part_entry *)(fake_disk + GPT_MBR_ENTRY_START);
|
||||
pte->ptype = 0x0C;
|
||||
pte->lba_first = 16;
|
||||
pte->lba_size = 32;
|
||||
pte = (uint8_t *)(fake_disk + GPT_MBR_ENTRY_START);
|
||||
pte[0x04] = (uint8_t)(0x0C);
|
||||
d_put32(pte + 0x08, 16);
|
||||
d_put32(pte + 0x0C, 32);
|
||||
/* No boot signature set — 0x0000 instead of 0xAA55 */
|
||||
|
||||
ck_assert_int_eq(disk_open(0), -1);
|
||||
|
|
@ -615,12 +659,12 @@ START_TEST(test_disk_open_gpt_excess_partitions)
|
|||
{
|
||||
/* GPT header claims more partitions than MAX_PARTITIONS. disk_open
|
||||
* must cap n_parts to MAX_PARTITIONS. */
|
||||
struct guid_ptable *gpt_hdr;
|
||||
uint8_t *gpt_hdr;
|
||||
|
||||
build_gpt_disk();
|
||||
|
||||
gpt_hdr = (struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
gpt_hdr->n_part = MAX_PARTITIONS + 10;
|
||||
gpt_hdr = (uint8_t *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
d_put32(gpt_hdr + D_HDR_N_PART, MAX_PARTITIONS + 10);
|
||||
finalize_gpt_part_array_crc(gpt_hdr);
|
||||
finalize_gpt_header_crc(gpt_hdr);
|
||||
|
||||
|
|
@ -635,12 +679,12 @@ START_TEST(test_disk_open_gpt_large_array_sz)
|
|||
{
|
||||
/* GPT header with array_sz larger than GPT_PART_ENTRY_SIZE (256).
|
||||
* Loop must break immediately without reading entries. */
|
||||
struct guid_ptable *gpt_hdr;
|
||||
uint8_t *gpt_hdr;
|
||||
|
||||
build_gpt_disk();
|
||||
|
||||
gpt_hdr = (struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
gpt_hdr->array_sz = GPT_PART_ENTRY_SIZE + 1; /* 257 > 256 */
|
||||
gpt_hdr = (uint8_t *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
d_put32(gpt_hdr + D_HDR_ARRAY_SZ, GPT_PART_ENTRY_SIZE + 1); /* 257 > 256 */
|
||||
finalize_gpt_part_array_crc(gpt_hdr);
|
||||
finalize_gpt_header_crc(gpt_hdr);
|
||||
|
||||
|
|
@ -654,13 +698,13 @@ START_TEST(test_disk_open_gpt_rejects_huge_part_array)
|
|||
* enormous n_part * array_sz must be rejected before the partition-entry
|
||||
* CRC scan loop runs, otherwise it forces a pre-auth DoS via one disk
|
||||
* read per 512-byte chunk of the (here 8 MB) declared array. */
|
||||
struct guid_ptable *gpt_hdr;
|
||||
uint8_t *gpt_hdr;
|
||||
|
||||
build_gpt_disk();
|
||||
|
||||
gpt_hdr = (struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
gpt_hdr->n_part = 0x10000; /* 65536 entries ... */
|
||||
gpt_hdr->array_sz = 128; /* ... * 128 bytes = 8 MB */
|
||||
gpt_hdr = (uint8_t *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
d_put32(gpt_hdr + D_HDR_N_PART, 0x10000); /* 65536 entries ... */
|
||||
d_put32(gpt_hdr + D_HDR_ARRAY_SZ, 128); /* ... * 128 bytes = 8 MB */
|
||||
finalize_gpt_header_crc(gpt_hdr); /* attacker can always fix header CRC */
|
||||
|
||||
disk_read_count = 0;
|
||||
|
|
@ -680,14 +724,14 @@ START_TEST(test_disk_open_gpt_lba_no_overflow)
|
|||
* gpt_lba >= 0x800000: 512 * 0x800001 wraps to 0x200, silently
|
||||
* redirecting the read back to LBA 1 (the real GPT header) instead of
|
||||
* the out-of-range LBA the field actually names. */
|
||||
struct gpt_mbr_part_entry *mbr_entry;
|
||||
uint8_t *mbr_entry;
|
||||
|
||||
build_gpt_disk();
|
||||
|
||||
/* Point the protective entry at an LBA whose 512* product overflows a
|
||||
* 32-bit unsigned back to 0x200 (LBA 1). */
|
||||
mbr_entry = (struct gpt_mbr_part_entry *)(fake_disk + GPT_MBR_ENTRY_START);
|
||||
mbr_entry->lba_first = 0x800001;
|
||||
mbr_entry = (uint8_t *)(fake_disk + GPT_MBR_ENTRY_START);
|
||||
d_put32(mbr_entry + 0x08, 0x800001);
|
||||
|
||||
/* With correct 64-bit arithmetic the header read targets byte
|
||||
* 0x100000200, far past the fake disk, so disk_open must fail rather
|
||||
|
|
@ -701,19 +745,19 @@ START_TEST(test_disk_open_gpt_empty_entry_mid_table)
|
|||
{
|
||||
/* GPT header says 3 partitions but entry[1] has zeroed type GUID.
|
||||
* gpt_parse_partition returns -1 → loop breaks, only 1 partition found. */
|
||||
struct guid_ptable *gpt_hdr;
|
||||
struct gpt_part_entry *pe;
|
||||
uint8_t *gpt_hdr;
|
||||
uint8_t *pe;
|
||||
|
||||
build_gpt_disk();
|
||||
|
||||
gpt_hdr = (struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
gpt_hdr->n_part = 3;
|
||||
gpt_hdr = (uint8_t *)(fake_disk + GPT_SECTOR_SIZE);
|
||||
d_put32(gpt_hdr + D_HDR_N_PART, 3);
|
||||
finalize_gpt_header_crc(gpt_hdr);
|
||||
|
||||
/* Zero out entry 1's type GUID */
|
||||
pe = (struct gpt_part_entry *)(fake_disk + 2 * GPT_SECTOR_SIZE + 128);
|
||||
pe->type[0] = 0;
|
||||
pe->type[1] = 0;
|
||||
pe = (uint8_t *)(fake_disk + 2 * GPT_SECTOR_SIZE + 128);
|
||||
d_put64(pe + D_PE_TYPE, 0);
|
||||
d_put64(pe + D_PE_TYPE + 8, 0);
|
||||
finalize_gpt_part_array_crc(gpt_hdr);
|
||||
finalize_gpt_header_crc(gpt_hdr);
|
||||
|
||||
|
|
@ -724,30 +768,30 @@ END_TEST
|
|||
START_TEST(test_disk_open_mbr_zero_lba_entry)
|
||||
{
|
||||
/* MBR entry with lba_first=0 must be skipped. */
|
||||
struct gpt_mbr_part_entry *pte;
|
||||
uint8_t *pte;
|
||||
uint16_t *boot_sig;
|
||||
|
||||
memset(fake_disk, 0, FAKE_DISK_SIZE);
|
||||
|
||||
/* Entry 0: valid */
|
||||
pte = (struct gpt_mbr_part_entry *)(fake_disk + GPT_MBR_ENTRY_START);
|
||||
pte->ptype = 0x0C;
|
||||
pte->lba_first = 16;
|
||||
pte->lba_size = 32;
|
||||
pte = (uint8_t *)(fake_disk + GPT_MBR_ENTRY_START);
|
||||
pte[0x04] = (uint8_t)(0x0C);
|
||||
d_put32(pte + 0x08, 16);
|
||||
d_put32(pte + 0x0C, 32);
|
||||
|
||||
/* Entry 1: lba_first=0, should be skipped */
|
||||
pte = (struct gpt_mbr_part_entry *)(fake_disk + GPT_MBR_ENTRY_START +
|
||||
pte = (uint8_t *)(fake_disk + GPT_MBR_ENTRY_START +
|
||||
sizeof(struct gpt_mbr_part_entry));
|
||||
pte->ptype = 0x83;
|
||||
pte->lba_first = 0;
|
||||
pte->lba_size = 64;
|
||||
pte[0x04] = (uint8_t)(0x83);
|
||||
d_put32(pte + 0x08, 0);
|
||||
d_put32(pte + 0x0C, 64);
|
||||
|
||||
/* Entry 2: lba_size=0, should also be skipped */
|
||||
pte = (struct gpt_mbr_part_entry *)(fake_disk + GPT_MBR_ENTRY_START +
|
||||
pte = (uint8_t *)(fake_disk + GPT_MBR_ENTRY_START +
|
||||
2 * sizeof(struct gpt_mbr_part_entry));
|
||||
pte->ptype = 0x83;
|
||||
pte->lba_first = 48;
|
||||
pte->lba_size = 0;
|
||||
pte[0x04] = (uint8_t)(0x83);
|
||||
d_put32(pte + 0x08, 48);
|
||||
d_put32(pte + 0x0C, 0);
|
||||
|
||||
boot_sig = (uint16_t *)(fake_disk + GPT_MBR_BOOTSIG_OFFSET);
|
||||
*boot_sig = GPT_MBR_BOOTSIG_VALUE;
|
||||
|
|
@ -864,11 +908,10 @@ START_TEST(test_gpt_check_mbr_bad_bootsig)
|
|||
/* Valid MBR structure but corrupt boot signature */
|
||||
memset(sector, 0, sizeof(sector));
|
||||
{
|
||||
struct gpt_mbr_part_entry *pte =
|
||||
(struct gpt_mbr_part_entry *)(sector + GPT_MBR_ENTRY_START);
|
||||
pte->ptype = GPT_PTYPE_PROTECTIVE;
|
||||
pte->lba_first = 1;
|
||||
pte->lba_size = 0xFFFFFFFF;
|
||||
uint8_t *pte = (uint8_t *)(sector + GPT_MBR_ENTRY_START);
|
||||
pte[0x04] = (uint8_t)(GPT_PTYPE_PROTECTIVE);
|
||||
d_put32(pte + 0x08, 1);
|
||||
d_put32(pte + 0x0C, 0xFFFFFFFF);
|
||||
}
|
||||
/* boot sig left as 0x0000 — not 0xAA55 */
|
||||
|
||||
|
|
@ -879,14 +922,14 @@ END_TEST
|
|||
START_TEST(test_gpt_parse_partition_first_gt_last)
|
||||
{
|
||||
uint8_t entry[128];
|
||||
struct gpt_part_entry *pe = (struct gpt_part_entry *)entry;
|
||||
uint8_t *pe = (uint8_t *)entry;
|
||||
struct gpt_part_info info;
|
||||
|
||||
memset(entry, 0, sizeof(entry));
|
||||
pe->type[0] = 0x0001020304050607ULL;
|
||||
pe->type[1] = 0x08090A0B0C0D0E0FULL;
|
||||
pe->first = 100;
|
||||
pe->last = 50; /* first > last → invalid */
|
||||
d_put64(pe + D_PE_TYPE, 0x0001020304050607ULL);
|
||||
d_put64(pe + D_PE_TYPE + 8, 0x08090A0B0C0D0E0FULL);
|
||||
d_put64(pe + D_PE_FIRST, 100);
|
||||
d_put64(pe + D_PE_LAST, 50); /* first > last → invalid */
|
||||
|
||||
ck_assert_int_eq(gpt_parse_partition(entry, 128, &info), -1);
|
||||
}
|
||||
|
|
@ -895,14 +938,14 @@ END_TEST
|
|||
START_TEST(test_gpt_parse_partition_first_eq_last)
|
||||
{
|
||||
uint8_t entry[128];
|
||||
struct gpt_part_entry *pe = (struct gpt_part_entry *)entry;
|
||||
uint8_t *pe = (uint8_t *)entry;
|
||||
struct gpt_part_info info;
|
||||
|
||||
memset(entry, 0, sizeof(entry));
|
||||
pe->type[0] = 0x0001020304050607ULL;
|
||||
pe->type[1] = 0x08090A0B0C0D0E0FULL;
|
||||
pe->first = 5;
|
||||
pe->last = 5; /* first == last is a valid single-sector partition */
|
||||
d_put64(pe + D_PE_TYPE, 0x0001020304050607ULL);
|
||||
d_put64(pe + D_PE_TYPE + 8, 0x08090A0B0C0D0E0FULL);
|
||||
d_put64(pe + D_PE_FIRST, 5);
|
||||
d_put64(pe + D_PE_LAST, 5); /* first == last is a valid single-sector partition */
|
||||
|
||||
ck_assert_int_eq(gpt_parse_partition(entry, 128, &info), 0);
|
||||
ck_assert_uint_eq(info.start, 5 * GPT_SECTOR_SIZE);
|
||||
|
|
@ -995,6 +1038,79 @@ START_TEST(test_sfdisk_gpt_last_lba_access)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* Lock the on-disk field offsets. Everything below is written as explicit
|
||||
* little-endian bytes and then read back through the parser, so a change to
|
||||
* an offset, a width, or the byte order fails here rather than silently
|
||||
* mis-parsing a real disk. This is what guards the byte-wise conversion in
|
||||
* src/gpt.c against a future refactor. */
|
||||
START_TEST(test_gpt_header_field_offsets)
|
||||
{
|
||||
uint8_t sector[GPT_SECTOR_SIZE];
|
||||
struct guid_ptable hdr;
|
||||
|
||||
memset(sector, 0, sizeof(sector));
|
||||
d_put64(sector + D_HDR_SIGNATURE, GPT_SIGNATURE);
|
||||
d_put32(sector + D_HDR_REVISION, 0x00010000);
|
||||
d_put32(sector + D_HDR_SIZE, 92);
|
||||
d_put64(sector + D_HDR_START_ARRAY, 0x1122334455667788ULL);
|
||||
d_put32(sector + D_HDR_N_PART, 0x11223344);
|
||||
d_put32(sector + D_HDR_ARRAY_SZ, 128);
|
||||
finalize_gpt_header_crc(sector);
|
||||
|
||||
ck_assert_int_eq(gpt_parse_header(sector, &hdr), 0);
|
||||
/* Values come back in HOST order, whatever the host byte order is. */
|
||||
ck_assert_uint_eq(hdr.hdr_size, 92);
|
||||
ck_assert(hdr.signature == GPT_SIGNATURE);
|
||||
ck_assert_uint_eq(hdr.revision, 0x00010000U);
|
||||
ck_assert(hdr.start_array == 0x1122334455667788ULL);
|
||||
ck_assert_uint_eq(hdr.n_part, 0x11223344U);
|
||||
ck_assert_uint_eq(hdr.array_sz, 128U);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* A single flipped byte in the header must fail the CRC. */
|
||||
START_TEST(test_gpt_header_crc_rejects_tamper)
|
||||
{
|
||||
uint8_t sector[GPT_SECTOR_SIZE];
|
||||
struct guid_ptable hdr;
|
||||
|
||||
memset(sector, 0, sizeof(sector));
|
||||
d_put64(sector + D_HDR_SIGNATURE, GPT_SIGNATURE);
|
||||
d_put32(sector + D_HDR_SIZE, 92);
|
||||
d_put64(sector + D_HDR_START_ARRAY, 2);
|
||||
d_put32(sector + D_HDR_N_PART, 2);
|
||||
d_put32(sector + D_HDR_ARRAY_SZ, 128);
|
||||
finalize_gpt_header_crc(sector);
|
||||
ck_assert_int_eq(gpt_parse_header(sector, &hdr), 0);
|
||||
|
||||
/* Flip one bit in a field the CRC covers. */
|
||||
sector[D_HDR_N_PART] ^= 0x01;
|
||||
ck_assert_int_eq(gpt_parse_header(sector, &hdr), -1);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_gpt_partition_field_offsets)
|
||||
{
|
||||
uint8_t entry[128];
|
||||
struct gpt_part_info info;
|
||||
|
||||
memset(entry, 0, sizeof(entry));
|
||||
d_put64(entry + D_PE_TYPE, 0x0001020304050607ULL);
|
||||
d_put64(entry + D_PE_TYPE + 8, 0x08090A0B0C0D0E0FULL);
|
||||
d_put64(entry + D_PE_FIRST, 0x0000000000000022ULL);
|
||||
d_put64(entry + D_PE_LAST, 0x0000000000000041ULL);
|
||||
write_utf16(entry + D_PE_NAME, "rootfs", GPT_PART_NAME_SIZE);
|
||||
|
||||
ck_assert_int_eq(gpt_parse_partition(entry, sizeof(entry), &info), 0);
|
||||
ck_assert(info.start == 0x22ULL * GPT_SECTOR_SIZE);
|
||||
ck_assert(info.end == ((0x41ULL + 1U) * GPT_SECTOR_SIZE) - 1U);
|
||||
/* The name is UTF-16LE on disk and host-order after parsing. */
|
||||
ck_assert_int_eq(gpt_part_name_eq(info.name, "rootfs"), 1);
|
||||
ck_assert_int_eq(gpt_part_name_eq(info.name, "boot"), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
/* ============================================================
|
||||
* Suite setup
|
||||
* ============================================================ */
|
||||
|
|
@ -1050,6 +1166,9 @@ Suite *wolfboot_suite(void)
|
|||
tcase_add_test(tc_cov, test_gpt_parse_partition_first_gt_last);
|
||||
tcase_add_test(tc_cov, test_gpt_parse_partition_first_eq_last);
|
||||
tcase_add_test(tc_cov, test_gpt_part_name_eq_label_too_long);
|
||||
tcase_add_test(tc_cov, test_gpt_header_field_offsets);
|
||||
tcase_add_test(tc_cov, test_gpt_header_crc_rejects_tamper);
|
||||
tcase_add_test(tc_cov, test_gpt_partition_field_offsets);
|
||||
tcase_add_test(tc_cov, test_gpt_part_name_eq_not_null_terminated);
|
||||
suite_add_tcase(s, tc_cov);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue