From 4b0a6a047c0230b31adf6339f831c993f3b4ac06 Mon Sep 17 00:00:00 2001 From: krw Date: Sun, 2 Jan 2022 17:26:14 +0000 Subject: [PATCH] Stop writing big-endian checksums into the little-endian GPT header fields gh_csum and gh_part_csum. Constrain kernel to accepting only correct little-endian checksums. Temporarily allow fdisk(8) to read either endian GPTs so that big-endian GPTs can be made correct by a simple 'fdisk -e' && 'w'. Fixes inter-architecture, inter-OS GPT portability and GPT fdisk(8) on big-endian architectures. Broken since initial GPT implementation. Suggestions and ok kettenis@ --- sbin/fdisk/gpt.c | 42 ++++++++++++++++++++++++------------------ sys/kern/subr_disk.c | 19 ++++++++++--------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/sbin/fdisk/gpt.c b/sbin/fdisk/gpt.c index eb23259bde9..d8354810310 100644 --- a/sbin/fdisk/gpt.c +++ b/sbin/fdisk/gpt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gpt.c,v 1.55 2021/12/29 00:04:45 krw Exp $ */ +/* $OpenBSD: gpt.c,v 1.56 2022/01/02 17:26:14 krw Exp $ */ /* * Copyright (c) 2015 Markus Muller * Copyright (c) 2015 Kenneth R Westerback @@ -116,7 +116,7 @@ get_header(const uint64_t sector) char *secbuf; uint64_t partlastlba, partslen, lba_end; int partspersec; - uint32_t orig_gh_csum, new_gh_csum; + uint32_t gh_csum; secbuf = DISK_readsectors(sector, 1); if (secbuf == NULL) @@ -161,14 +161,15 @@ get_header(const uint64_t sector) return -1; } - orig_gh_csum = gh.gh_csum; + gh_csum = gh.gh_csum; gh.gh_csum = 0; - new_gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size)); - gh.gh_csum = orig_gh_csum; - if (new_gh_csum != letoh32(gh.gh_csum)) { + gh.gh_csum = htole32(crc32((unsigned char *)&gh, letoh32(gh.gh_size))); + if (gh_csum != gh.gh_csum) { DPRINTF("gpt header checksum: expected 0x%x, got 0x%x\n", - new_gh_csum, letoh32(gh.gh_csum)); - return -1; + letoh32(gh.gh_csum), letoh32(gh_csum)); + /* Accept wrong-endian checksum. */ + if (swap32(gh_csum) != gh.gh_csum) + return -1; } /* XXX Assume part_num * part_size is multiple of secsize. */ @@ -220,7 +221,7 @@ get_partition_table(void) { char *secbuf; uint64_t gpbytes, gpsectors; - uint32_t checksum, partspersec; + uint32_t gh_part_csum, partspersec; DPRINTF("gpt partition table being read from LBA %llu\n", letoh64(gh.gh_part_lba)); @@ -242,11 +243,15 @@ get_partition_table(void) memcpy(&gp, secbuf, gpbytes); free(secbuf); - checksum = crc32((unsigned char *)&gp, gpbytes); - if (checksum != letoh32(gh.gh_part_csum)) { + gh_part_csum = gh.gh_part_csum; + gh.gh_part_csum = htole32(crc32((unsigned char *)&gp, gpbytes)); + if (gh_part_csum != gh.gh_part_csum) { DPRINTF("gpt partition table checksum: expected 0x%x, " - "got 0x%x\n", checksum, letoh32(gh.gh_part_csum)); - return -1; + "got 0x%x\n", letoh32(gh.gh_part_csum), + letoh32(gh_part_csum)); + /* Accept wrong-endian checksum. */ + if (swap32(gh_part_csum) != gh.gh_part_csum) + return -1; } return 0; @@ -455,8 +460,9 @@ add_partition(const uint8_t *beuuid, const char *name, uint64_t sectors) goto done; uuid_enc_le(&gp[pn].gp_guid, &uuid); - gh.gh_part_csum = crc32((unsigned char *)&gp, sizeof(gp)); - gh.gh_csum = crc32((unsigned char *)&gh, sizeof(gh)); + gh.gh_part_csum = htole32(crc32((unsigned char *)&gp, sizeof(gp))); + gh.gh_csum = 0; + gh.gh_csum = htole32(crc32((unsigned char *)&gh, sizeof(gh))); return 0; @@ -620,9 +626,9 @@ GPT_write(void) gh.gh_lba_self = htole64(prigh); gh.gh_lba_alt = htole64(altgh); gh.gh_part_lba = htole64(prigp); - gh.gh_part_csum = crc32((unsigned char *)&gp, gpbytes); + gh.gh_part_csum = htole32(crc32((unsigned char *)&gp, gpbytes)); gh.gh_csum = 0; - gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size)); + gh.gh_csum = htole32(crc32((unsigned char *)&gh, letoh32(gh.gh_size))); secbuf = DISK_readsectors(prigh, 1); if (secbuf == NULL) @@ -638,7 +644,7 @@ GPT_write(void) gh.gh_lba_alt = htole64(prigh); gh.gh_part_lba = htole64(altgp); gh.gh_csum = 0; - gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size)); + gh.gh_csum = htole32(crc32((unsigned char *)&gh, letoh32(gh.gh_size))); secbuf = DISK_readsectors(altgh, 1); if (secbuf == NULL) diff --git a/sys/kern/subr_disk.c b/sys/kern/subr_disk.c index 9673efe365f..a879af7f0ce 100644 --- a/sys/kern/subr_disk.c +++ b/sys/kern/subr_disk.c @@ -1,4 +1,4 @@ -/* $OpenBSD: subr_disk.c,v 1.247 2021/12/22 22:20:13 bluhm Exp $ */ +/* $OpenBSD: subr_disk.c,v 1.248 2022/01/02 17:26:14 krw Exp $ */ /* $NetBSD: subr_disk.c,v 1.17 1996/03/16 23:17:08 christos Exp $ */ /* @@ -623,7 +623,7 @@ gpt_chk_hdr(struct gpt_header *gh, struct disklabel *lp) { uint64_t ghpartlba; uint64_t ghlbaend, ghlbastart; - uint32_t orig_gh_csum; + uint32_t gh_csum; uint32_t ghsize, ghpartsize, ghpartspersec; if (letoh64(gh->gh_sig) != GPTSIGNATURE) @@ -642,11 +642,11 @@ gpt_chk_hdr(struct gpt_header *gh, struct disklabel *lp) if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) return (EINVAL); - orig_gh_csum = gh->gh_csum; + gh_csum = gh->gh_csum; gh->gh_csum = 0; - gh->gh_csum = crc32(0, (unsigned char *)gh, ghsize); + gh->gh_csum = htole32(crc32(0, (unsigned char *)gh, ghsize)); - if (orig_gh_csum != gh->gh_csum) + if (gh_csum != gh->gh_csum) return (EINVAL); if (ghlbastart >= DL_GETDSIZE(lp) || @@ -676,11 +676,12 @@ gpt_chk_hdr(struct gpt_header *gh, struct disklabel *lp) int gpt_chk_parts(struct gpt_header *gh, struct gpt_partition *gp) { - u_int32_t checksum; - checksum = crc32(0, (unsigned char *)gp, - letoh32(gh->gh_part_num) * letoh32(gh->gh_part_size)); + u_int32_t gh_part_csum; - if (checksum != gh->gh_part_csum) + gh_part_csum = htole32(crc32(0, (unsigned char *)gp, + letoh32(gh->gh_part_num) * letoh32(gh->gh_part_size))); + + if (gh_part_csum != gh->gh_part_csum) return (EINVAL); return 0; -- 2.20.1