From 4c6e0bd576fb88053d37b00ef579ea963450fc96 Mon Sep 17 00:00:00 2001 From: Brian Fjeldstad Date: Fri, 28 Aug 2026 19:12:19 +0000 Subject: [PATCH] mkcosi: recognize arm64 CIH images, not just amd64 isCIHImage() only accepted the amd64 discoverable-partition-spec GUIDs for ROOT and HASH-A/HASH-B, so any arm64 CIH image was silently misclassified as non-CIH and fell back to generic (non-CIH) metadata population -- not a hard failure, but a loss of CIH-specific metadata (e.g. usrhash extraction) for arm64 images. Change cihPartitionDef.TypeGUID (single value) to TypeGUIDs (a list), and accept either the amd64 or arm64 GUID for ROOT and HASH-A/HASH-B, reusing the arch-specific constants already defined in this package's metadata.go (PartitionTypeRootAmd64/Arm64, PartitionTypeUsrAmd64Verity/ Arm64Verity). Deliberately does not key this off runtime.GOARCH: that reflects the architecture of whatever host is running mkcosi, not the architecture of the image being inspected, and mkcosi runs natively (unlike e.g. Image Customizer, which can run under container/binfmt emulation matching the target arch). Matching against both known GUID sets directly is correct regardless of host/target arch relationship. isCIHImage() itself is refactored to key partMap by partition name only (GPT partition names are already unique within a CIH image) and check the actual type GUID against the acceptable set for that name, rather than keying by name+typeGUID as before. No behavior change for amd64 images or for any other partition. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/cmd/mkcosi/generator/cih.go | 91 ++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/tools/cmd/mkcosi/generator/cih.go b/tools/cmd/mkcosi/generator/cih.go index fc719cf1a..022c8bf44 100644 --- a/tools/cmd/mkcosi/generator/cih.go +++ b/tools/cmd/mkcosi/generator/cih.go @@ -29,9 +29,16 @@ import ( // cihPartitionDef describes one expected partition in a CIH image. type cihPartitionDef struct { - Name string // GPT partition name (e.g. "USR-A") - TypeGUID string // Partition type GUID, lowercase - UUID string // Unique partition GUID, lowercase; empty means "don't check" + Name string // GPT partition name (e.g. "USR-A") + TypeGUIDs []string // Acceptable partition type GUIDs, lowercase. Most + // entries have exactly one; ROOT and HASH-A/HASH-B carry both the + // amd64 and arm64 discoverable-partition-spec GUIDs, since a CIH + // image built for either architecture is valid and the image being + // inspected here was not necessarily produced on a host of the same + // architecture (e.g. cross-arch builds, or a build/inspection tool + // running natively rather than under arch-emulation). Do not key + // this off runtime.GOARCH -- see cih.go's isCIHImage doc comment. + UUID string // Unique partition GUID, lowercase; empty means "don't check" } // cihRequiredPartitions lists the partitions that must be present (by name and @@ -40,20 +47,29 @@ type cihPartitionDef struct { // partition UUIDs vary across builds and are not checked. // HASH-A and HASH-B are optional — images without them are still valid CIH. var cihRequiredPartitions = []cihPartitionDef{ - {Name: "EFI-SYSTEM", TypeGUID: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"}, - {Name: "USR-A", TypeGUID: "5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6", UUID: "7130c94a-213a-4e5a-8e26-6cce9662f132"}, - {Name: "USR-B", TypeGUID: "5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6", UUID: "e03dd35c-7c2d-4a47-b3fe-27f15780a57c"}, - {Name: "OEM", TypeGUID: "0fc63daf-8483-4772-8e79-3d69d8477de4"}, - {Name: "ROOT", TypeGUID: "4f68bce3-e8cd-4db1-96e7-fbcaf984b709"}, + {Name: "EFI-SYSTEM", TypeGUIDs: []string{"c12a7328-f81f-11d2-ba4b-00a0c93ec93b"}}, + {Name: "USR-A", TypeGUIDs: []string{"5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6"}, UUID: "7130c94a-213a-4e5a-8e26-6cce9662f132"}, + {Name: "USR-B", TypeGUIDs: []string{"5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6"}, UUID: "e03dd35c-7c2d-4a47-b3fe-27f15780a57c"}, + {Name: "OEM", TypeGUIDs: []string{"0fc63daf-8483-4772-8e79-3d69d8477de4"}}, + {Name: "ROOT", TypeGUIDs: []string{ + string(metadata.PartitionTypeRootAmd64), + string(metadata.PartitionTypeRootArm64), + }}, } // cihOptionalPartitions lists partitions that may or may not be present. // When present, both name+typeGUID and UUID must match. var cihOptionalPartitions = []cihPartitionDef{ - {Name: "BIOS-BOOT", TypeGUID: "21686148-6449-6e6f-744e-656564454649"}, - {Name: "OEM-CONFIG", TypeGUID: "c95dc21a-df0e-4340-8d7b-26cbfa9a03e0"}, - {Name: "HASH-A", TypeGUID: "77ff5f63-e7b6-4633-acf4-1565b864c0e6", UUID: "b736baf1-cdb4-4535-beba-ddaaa30ad7b7"}, - {Name: "HASH-B", TypeGUID: "77ff5f63-e7b6-4633-acf4-1565b864c0e6", UUID: "35bdf78b-c453-4661-98e6-f834f534ef5b"}, + {Name: "BIOS-BOOT", TypeGUIDs: []string{"21686148-6449-6e6f-744e-656564454649"}}, + {Name: "OEM-CONFIG", TypeGUIDs: []string{"c95dc21a-df0e-4340-8d7b-26cbfa9a03e0"}}, + {Name: "HASH-A", TypeGUIDs: []string{ + string(metadata.PartitionTypeUsrAmd64Verity), + string(metadata.PartitionTypeUsrArm64Verity), + }, UUID: "b736baf1-cdb4-4535-beba-ddaaa30ad7b7"}, + {Name: "HASH-B", TypeGUIDs: []string{ + string(metadata.PartitionTypeUsrAmd64Verity), + string(metadata.PartitionTypeUsrArm64Verity), + }, UUID: "35bdf78b-c453-4661-98e6-f834f534ef5b"}, } // cihMountPointByName maps CIH partition names to their logical mount points. @@ -72,37 +88,58 @@ var cihMountPointByName = map[string]string{ // name+typeGUID; those with a non-empty UUID are also verified by UUID. // Optional partitions (HASH-A/HASH-B) are validated when present. func isCIHImage(parsedGPT *gpt.ParsedGPT) bool { - type partKey struct { - name string + type partVal struct { typeGUID string + uuid string } - // Map name+typeGUID -> partition UUID for checking. - partMap := make(map[partKey]string, len(parsedGPT.Partitions)) + // Map partition name -> (typeGUID, UUID) for checking. GPT partition + // names are unique within a CIH image, so this does not need to be + // keyed by typeGUID as well. + partMap := make(map[string]partVal, len(parsedGPT.Partitions)) for _, p := range parsedGPT.Partitions { - partMap[partKey{ - name: p.GetName(), + partMap[p.GetName()] = partVal{ typeGUID: strings.ToLower(p.PartitionTypeGUID.String()), - }] = strings.ToLower(p.UniquePartitionGUID.String()) + uuid: strings.ToLower(p.UniquePartitionGUID.String()), + } + } + + // matchesTypeGUID reports whether actual is one of the acceptable type + // GUIDs for a partition definition. Some logical partitions (ROOT, + // HASH-A, HASH-B) accept either the amd64 or arm64 discoverable- + // partition-spec GUID: a CIH image is valid for either architecture, + // and the architecture of the image under inspection is not assumed + // to match the architecture of whatever is running this check. + matchesTypeGUID := func(def cihPartitionDef, actual string) bool { + for _, want := range def.TypeGUIDs { + if actual == want { + return true + } + } + return false } // All required partitions must be present with matching name+typeGUID. // Those with a specified UUID must also match. for _, req := range cihRequiredPartitions { - key := partKey{name: req.Name, typeGUID: req.TypeGUID} - actualUUID, found := partMap[key] - if !found { + actual, found := partMap[req.Name] + if !found || !matchesTypeGUID(req, actual.typeGUID) { return false } - if req.UUID != "" && actualUUID != req.UUID { + if req.UUID != "" && actual.uuid != req.UUID { return false } } - // Optional partitions: if present, their UUID must match. + // Optional partitions: if present, their type GUID and UUID must match. for _, opt := range cihOptionalPartitions { - key := partKey{name: opt.Name, typeGUID: opt.TypeGUID} - actualUUID, found := partMap[key] - if found && opt.UUID != "" && actualUUID != opt.UUID { + actual, found := partMap[opt.Name] + if !found { + continue + } + if !matchesTypeGUID(opt, actual.typeGUID) { + continue + } + if opt.UUID != "" && actual.uuid != opt.UUID { return false } }