From b7dc4bcaab32bd5669ce25f85bb2a374bd30c89d Mon Sep 17 00:00:00 2001 From: Amit Barve Date: Wed, 29 Oct 2025 12:29:23 -0400 Subject: [PATCH 1/2] Set disk identifier for confidential UVM's scratch Confidential UVMs boot with a scratch that is completely empty (not even a partition table on it). The UVM formats it as a part of the boot flow. However, the UVM needs a way to correctly identify and distinguish between the scratch & boot VHDs. We do this by setting a pre-defined GUID in the VirtualDiskIdentifier field of the VHD metadata. A fixed pre-defined GUID will be set in the metadata of every UVM's scratch. Signed-off-by: Amit Barve --- go.mod | 2 +- go.sum | 4 +- internal/uvm/create_wcow.go | 17 +++ test/go.mod | 2 +- test/go.sum | 4 +- .../Microsoft/go-winio/.golangci.yml | 4 + .../github.com/Microsoft/go-winio/backup.go | 1 - .../Microsoft/go-winio/backuptar/tar.go | 1 - vendor/github.com/Microsoft/go-winio/file.go | 1 - .../github.com/Microsoft/go-winio/fileinfo.go | 1 - .../github.com/Microsoft/go-winio/hvsock.go | 1 - vendor/github.com/Microsoft/go-winio/pipe.go | 1 - .../go-winio/pkg/bindfilter/bind_filter.go | 9 +- .../Microsoft/go-winio/pkg/etw/eventdata.go | 1 - .../Microsoft/go-winio/pkg/etw/eventopt.go | 1 - .../Microsoft/go-winio/pkg/etw/fieldopt.go | 1 - .../Microsoft/go-winio/pkg/etw/newprovider.go | 2 - .../pkg/etw/newprovider_unsupported.go | 1 - .../Microsoft/go-winio/pkg/etw/provider.go | 1 - .../go-winio/pkg/etw/providerglobal.go | 1 - .../Microsoft/go-winio/pkg/etw/ptr64_32.go | 2 - .../Microsoft/go-winio/pkg/etw/ptr64_64.go | 2 - .../Microsoft/go-winio/pkg/etw/wrapper_32.go | 2 - .../Microsoft/go-winio/pkg/etw/wrapper_64.go | 2 - .../Microsoft/go-winio/pkg/etwlogrus/hook.go | 1 - .../go-winio/pkg/guid/guid_nonwindows.go | 1 - .../go-winio/pkg/guid/guid_windows.go | 1 - .../Microsoft/go-winio/pkg/process/process.go | 1 - .../Microsoft/go-winio/pkg/process/syscall.go | 1 - .../Microsoft/go-winio/privilege.go | 1 - .../github.com/Microsoft/go-winio/reparse.go | 1 - vendor/github.com/Microsoft/go-winio/sd.go | 1 - .../github.com/Microsoft/go-winio/vhd/vhd.go | 106 +++++++++++++++++- .../Microsoft/go-winio/vhd/zvhd_windows.go | 18 +++ vendor/modules.txt | 4 +- 35 files changed, 156 insertions(+), 44 deletions(-) diff --git a/go.mod b/go.mod index a6836010b5..fd5e1abd1d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.0 require ( github.com/Microsoft/cosesign1go v1.4.0 github.com/Microsoft/didx509go v0.0.3 - github.com/Microsoft/go-winio v0.6.2 + github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 github.com/blang/semver/v4 v4.0.0 github.com/cenkalti/backoff/v4 v4.3.0 github.com/containerd/cgroups/v3 v3.0.5 diff --git a/go.sum b/go.sum index 95930d318b..4514bc4a05 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/Microsoft/cosesign1go v1.4.0 h1:VdiqzsilEE6t1GQi98I/h0WpVFM7AyMEeyP8u github.com/Microsoft/cosesign1go v1.4.0/go.mod h1:1La/HcGw19rRLhPW0S6u55K6LKfti+GQSgGCtrfhVe8= github.com/Microsoft/didx509go v0.0.3 h1:n/owuFOXVzCEzSyzivMEolKEouBm9G0NrEDgoTekM8A= github.com/Microsoft/didx509go v0.0.3/go.mod h1:wWt+iQsLzn3011+VfESzznLIp/Owhuj7rLF7yLglYbk= -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 h1:0kQAzHq8vLs7Pptv+7TxjdETLf/nIqJpIB4oC6Ba4vY= +github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= diff --git a/internal/uvm/create_wcow.go b/internal/uvm/create_wcow.go index 4f6f118610..752d726c2d 100644 --- a/internal/uvm/create_wcow.go +++ b/internal/uvm/create_wcow.go @@ -12,6 +12,7 @@ import ( "github.com/Microsoft/go-winio" "github.com/Microsoft/go-winio/pkg/guid" + "github.com/Microsoft/go-winio/vhd" "github.com/pkg/errors" "github.com/sirupsen/logrus" "go.opencensus.io/trace" @@ -31,6 +32,18 @@ import ( "github.com/Microsoft/hcsshim/pkg/securitypolicy" ) +var ( + // A predefined GUID for UtilityVMs to identify a scratch VHD that is completely empty/unformatted. + // This GUID is set in the metadata of the VHD and thus can be reliably used to identify the disk. + // a7b3c5d1-4e2f-4a8b-9c6d-1e3f5a7b9c2d + unformattedScratchIdentifier = &guid.GUID{ + Data1: 0xa7b3c5d1, + Data2: 0x4e2f, + Data3: 0x4a8b, + Data4: [8]byte{0x9c, 0x6d, 0x1e, 0x3f, 0x5a, 0x7b, 0x9c, 0x2d}, + } +) + type ConfidentialWCOWOptions struct { GuestStateFilePath string // The vmgs file path SecurityPolicyEnabled bool // Set when there is a security policy to apply on actual SNP hardware, use this rathen than checking the string length @@ -406,6 +419,10 @@ func prepareSecurityConfigDoc(ctx context.Context, uvm *UtilityVM, opts *Options return nil, errors.Wrap(err, "failed to grant vm access to scratch VHD") } + if err = vhd.SetVirtualDiskIdentifier(opts.BootFiles.BlockCIMFiles.ScratchVHDPath, *unformattedScratchIdentifier); err != nil { + return nil, fmt.Errorf("set scratch VHD identifier: %w", err) + } + // boot depends on scratch being attached at LUN 0, it MUST ALWAYS remain at LUN 0 doc.VirtualMachine.Devices.Scsi[guestrequest.ScsiControllerGuids[0]].Attachments["0"] = hcsschema.Attachment{ Path: opts.BootFiles.BlockCIMFiles.ScratchVHDPath, diff --git a/test/go.mod b/test/go.mod index a91edcbb11..b16b063313 100644 --- a/test/go.mod +++ b/test/go.mod @@ -3,7 +3,7 @@ module github.com/Microsoft/hcsshim/test go 1.23.0 require ( - github.com/Microsoft/go-winio v0.6.2 + github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 github.com/Microsoft/hcsshim v0.13.0 github.com/containerd/cgroups/v3 v3.0.5 github.com/containerd/containerd/api v1.9.0 diff --git a/test/go.sum b/test/go.sum index 0248604b65..4442c62275 100644 --- a/test/go.sum +++ b/test/go.sum @@ -6,8 +6,8 @@ github.com/Microsoft/cosesign1go v1.4.0 h1:VdiqzsilEE6t1GQi98I/h0WpVFM7AyMEeyP8u github.com/Microsoft/cosesign1go v1.4.0/go.mod h1:1La/HcGw19rRLhPW0S6u55K6LKfti+GQSgGCtrfhVe8= github.com/Microsoft/didx509go v0.0.3 h1:n/owuFOXVzCEzSyzivMEolKEouBm9G0NrEDgoTekM8A= github.com/Microsoft/didx509go v0.0.3/go.mod h1:wWt+iQsLzn3011+VfESzznLIp/Owhuj7rLF7yLglYbk= -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 h1:0kQAzHq8vLs7Pptv+7TxjdETLf/nIqJpIB4oC6Ba4vY= +github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= diff --git a/vendor/github.com/Microsoft/go-winio/.golangci.yml b/vendor/github.com/Microsoft/go-winio/.golangci.yml index faedfe937a..924b5ce596 100644 --- a/vendor/github.com/Microsoft/go-winio/.golangci.yml +++ b/vendor/github.com/Microsoft/go-winio/.golangci.yml @@ -59,6 +59,10 @@ issues: text: "^directive `//nolint:errorlint` should provide explanation" source: '[=|!]= io.EOF' + - linters: + - gosec + text: "^G115: integer overflow conversion" + linters-settings: exhaustive: diff --git a/vendor/github.com/Microsoft/go-winio/backup.go b/vendor/github.com/Microsoft/go-winio/backup.go index b54341daac..7c2e4ba233 100644 --- a/vendor/github.com/Microsoft/go-winio/backup.go +++ b/vendor/github.com/Microsoft/go-winio/backup.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package winio diff --git a/vendor/github.com/Microsoft/go-winio/backuptar/tar.go b/vendor/github.com/Microsoft/go-winio/backuptar/tar.go index 7f852bbf81..a4d2202f9c 100644 --- a/vendor/github.com/Microsoft/go-winio/backuptar/tar.go +++ b/vendor/github.com/Microsoft/go-winio/backuptar/tar.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package backuptar diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go index fe82a180db..f38213763e 100644 --- a/vendor/github.com/Microsoft/go-winio/file.go +++ b/vendor/github.com/Microsoft/go-winio/file.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package winio diff --git a/vendor/github.com/Microsoft/go-winio/fileinfo.go b/vendor/github.com/Microsoft/go-winio/fileinfo.go index c860eb9917..01cd891e84 100644 --- a/vendor/github.com/Microsoft/go-winio/fileinfo.go +++ b/vendor/github.com/Microsoft/go-winio/fileinfo.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package winio diff --git a/vendor/github.com/Microsoft/go-winio/hvsock.go b/vendor/github.com/Microsoft/go-winio/hvsock.go index c4fdd9d4ae..d4960f5b0a 100644 --- a/vendor/github.com/Microsoft/go-winio/hvsock.go +++ b/vendor/github.com/Microsoft/go-winio/hvsock.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package winio diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go index a2da6639d0..58c68e9522 100644 --- a/vendor/github.com/Microsoft/go-winio/pipe.go +++ b/vendor/github.com/Microsoft/go-winio/pipe.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package winio diff --git a/vendor/github.com/Microsoft/go-winio/pkg/bindfilter/bind_filter.go b/vendor/github.com/Microsoft/go-winio/pkg/bindfilter/bind_filter.go index 7c7f145f44..f3e9d7b29e 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/bindfilter/bind_filter.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/bindfilter/bind_filter.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package bindfilter @@ -109,7 +108,7 @@ func GetBindMappings(volumePath string) ([]BindMapping, error) { } if outBuffSize < 12 { - return nil, fmt.Errorf("invalid buffer returned") + return nil, errors.New("invalid buffer returned") } result := buf[:outBuffSize] @@ -185,7 +184,7 @@ func decodeEntry(buffer []byte) (string, error) { func getTargetsFromBuffer(buffer []byte, offset, count int) ([]string, error) { if len(buffer) < offset+count*6 { - return nil, fmt.Errorf("invalid buffer") + return nil, errors.New("invalid buffer") } targets := make([]string, count) @@ -193,7 +192,7 @@ func getTargetsFromBuffer(buffer []byte, offset, count int) ([]string, error) { entryBuf := buffer[offset+i*8 : offset+i*8+8] tgt := *(*mappingTargetEntry)(unsafe.Pointer(&entryBuf[0])) if len(buffer) < int(tgt.TargetRootOffset)+int(tgt.TargetRootLength) { - return nil, fmt.Errorf("invalid buffer") + return nil, errors.New("invalid buffer") } decoded, err := decodeEntry(buffer[tgt.TargetRootOffset : tgt.TargetRootOffset+tgt.TargetRootLength]) if err != nil { @@ -259,7 +258,7 @@ func getFinalPath(pth string) (string, error) { func getBindMappingFromBuffer(buffer []byte, entry mappingEntry) (BindMapping, error) { if len(buffer) < int(entry.VirtRootOffset)+int(entry.VirtRootLength) { - return BindMapping{}, fmt.Errorf("invalid buffer") + return BindMapping{}, errors.New("invalid buffer") } src, err := decodeEntry(buffer[entry.VirtRootOffset : entry.VirtRootOffset+entry.VirtRootLength]) diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdata.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdata.go index f971cc77b5..7ac6b50414 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdata.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdata.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package etw diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/eventopt.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventopt.go index 73403220c9..9c06fef824 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etw/eventopt.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventopt.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package etw diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/fieldopt.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/fieldopt.go index 57114d8da3..2798e979ff 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etw/fieldopt.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/fieldopt.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package etw diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider.go index 3669b4f783..0c3e301db8 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider.go @@ -1,6 +1,4 @@ //go:build windows && (amd64 || arm64 || 386) -// +build windows -// +build amd64 arm64 386 package etw diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider_unsupported.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider_unsupported.go index e0057cfe0d..18f9dfef08 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider_unsupported.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider_unsupported.go @@ -1,5 +1,4 @@ //go:build windows && arm -// +build windows,arm package etw diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/provider.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/provider.go index 8174bff1b0..089808a929 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etw/provider.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/provider.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package etw diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/providerglobal.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/providerglobal.go index 0a1d90dda0..831697a659 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etw/providerglobal.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/providerglobal.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package etw diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_32.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_32.go index 26c9f1948a..e89da492c7 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_32.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_32.go @@ -1,6 +1,4 @@ //go:build windows && (386 || arm) -// +build windows -// +build 386 arm package etw diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_64.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_64.go index 1524c643fd..d991938872 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_64.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_64.go @@ -1,6 +1,4 @@ //go:build windows && (amd64 || arm64) -// +build windows -// +build amd64 arm64 package etw diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_32.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_32.go index 14c4998420..eba93fd79b 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_32.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_32.go @@ -1,6 +1,4 @@ //go:build windows && (386 || arm) -// +build windows -// +build 386 arm package etw diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_64.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_64.go index 8cfe2e8cab..8f53ad7bcf 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_64.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_64.go @@ -1,6 +1,4 @@ //go:build windows && (amd64 || arm64) -// +build windows -// +build amd64 arm64 package etw diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etwlogrus/hook.go b/vendor/github.com/Microsoft/go-winio/pkg/etwlogrus/hook.go index 76f6239a54..71f658ed99 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/etwlogrus/hook.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/etwlogrus/hook.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package etwlogrus diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go index 805bd35484..b933821a54 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go @@ -1,5 +1,4 @@ //go:build !windows -// +build !windows package guid diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go index 27e45ee5cc..4aa95a7068 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package guid diff --git a/vendor/github.com/Microsoft/go-winio/pkg/process/process.go b/vendor/github.com/Microsoft/go-winio/pkg/process/process.go index 873d24e97a..b1305e02ce 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/process/process.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/process/process.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package process diff --git a/vendor/github.com/Microsoft/go-winio/pkg/process/syscall.go b/vendor/github.com/Microsoft/go-winio/pkg/process/syscall.go index 3d47dd7a1e..1bff3bc6dc 100644 --- a/vendor/github.com/Microsoft/go-winio/pkg/process/syscall.go +++ b/vendor/github.com/Microsoft/go-winio/pkg/process/syscall.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package process diff --git a/vendor/github.com/Microsoft/go-winio/privilege.go b/vendor/github.com/Microsoft/go-winio/privilege.go index d9b90b6e86..747eefd326 100644 --- a/vendor/github.com/Microsoft/go-winio/privilege.go +++ b/vendor/github.com/Microsoft/go-winio/privilege.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package winio diff --git a/vendor/github.com/Microsoft/go-winio/reparse.go b/vendor/github.com/Microsoft/go-winio/reparse.go index 67d1a104a6..b03e517f3c 100644 --- a/vendor/github.com/Microsoft/go-winio/reparse.go +++ b/vendor/github.com/Microsoft/go-winio/reparse.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package winio diff --git a/vendor/github.com/Microsoft/go-winio/sd.go b/vendor/github.com/Microsoft/go-winio/sd.go index c3685e98e1..7834c6adcb 100644 --- a/vendor/github.com/Microsoft/go-winio/sd.go +++ b/vendor/github.com/Microsoft/go-winio/sd.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package winio diff --git a/vendor/github.com/Microsoft/go-winio/vhd/vhd.go b/vendor/github.com/Microsoft/go-winio/vhd/vhd.go index b54cad1127..7305cb8879 100644 --- a/vendor/github.com/Microsoft/go-winio/vhd/vhd.go +++ b/vendor/github.com/Microsoft/go-winio/vhd/vhd.go @@ -1,11 +1,14 @@ //go:build windows -// +build windows package vhd import ( + "bytes" + "encoding/binary" "fmt" + "strings" "syscall" + "unsafe" "github.com/Microsoft/go-winio/pkg/guid" "golang.org/x/sys/windows" @@ -18,6 +21,8 @@ import ( //sys attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (win32err error) = virtdisk.AttachVirtualDisk //sys detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (win32err error) = virtdisk.DetachVirtualDisk //sys getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (win32err error) = virtdisk.GetVirtualDiskPhysicalPath +//sys getVirtualDiskInformation(handle syscall.Handle, bufferSize *uint32, info *virtualDiskInfo, sizeUsed *uint32) (win32err error) = virtdisk.GetVirtualDiskInformation +//sys setVirtualDiskInformation(handle syscall.Handle, info *virtualDiskInfo) (win32err error) = virtdisk.SetVirtualDiskInformation type ( CreateVirtualDiskFlag uint32 @@ -86,6 +91,20 @@ type AttachVirtualDiskParameters struct { Version2 AttachVersion2 } +// `virtualDiskInfo` struct is used to represent both GET_VIRTUAL_DISK_INFO +// (https://learn.microsoft.com/en-us/windows/win32/api/virtdisk/ns-virtdisk-get_virtual_disk_info) +// and SET_VIRTUAL_DISK_INFO +// (https://learn.microsoft.com/en-us/windows/win32/api/virtdisk/ns-virtdisk-set_virtual_disk_info) +// win32 types. Both of these win32 types have the same size and a very similar +// structure. These types use tagged unions which aren't directly supported in Go, so we +// keep this type unexported, and provide a cleaner interface to our callers by parsing +// the data buffer for the right type. +type virtualDiskInfo struct { + version uint32 + _ [4]byte // padding + data [24]byte // union of various types +} + const ( //revive:disable-next-line:var-naming ALL_CAPS VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 0x3 @@ -143,6 +162,34 @@ const ( // Flags for detaching a VHD. DetachVirtualDiskFlagNone DetachVirtualDiskFlag = 0x0 + + // Flags for setting information about a VHD - these should remain unexported as we provide APIs to directly get/set a particular field. + setVirtualDiskInfoUnspecified uint32 = 0x0 + setVirtualDiskInfoParentPath uint32 = 0x1 + setVirtualDiskInfoIdentifier uint32 = 0x2 + setVirtualDiskInfoParentPathWithDepth uint32 = 0x3 + setVirtualDiskInfoPhysicalSectorSize uint32 = 0x4 + setVirtualDiskInfoVirtualDiskID uint32 = 0x5 + setVirtualDiskInfoChangeTrackingState uint32 = 0x6 + setVirtualDiskInfoParentLocator uint32 = 0x7 + + // Flags for getting information about a VHD - these should remain unexported as we provide APIs to directly get/set a particular field. + getVirtualDiskInfoUnspecified uint32 = 0x0 + getVirtualDiskInfoSize uint32 = 0x1 + getVirtualDiskInfoIdentifier uint32 = 0x2 + getVirtualDiskInfoParentLocation uint32 = 0x3 + getVirtualDiskInfoParentIdentifier uint32 = 0x4 + getVirtualDiskInfoParentTimestamp uint32 = 0x5 + getVirtualDiskInfoVirtualStorageType uint32 = 0x6 + getVirtualDiskInfoProviderSubtype uint32 = 0x7 + getVirtualDiskInfoIs4kAligned uint32 = 0x8 + getVirtualDiskInfoPhysicalDisk uint32 = 0x9 + getVirtualDiskInfoVHDPhysicalSectorSize uint32 = 0xA + getVirtualDiskInfoSmallestSafeVirtualSize uint32 = 0xB + getVirtualDiskInfoFragmentation uint32 = 0xC + getVirtualDiskInfoIsLoaded uint32 = 0xD + getVirtualDiskInfoVirtualDiskID uint32 = 0xE + getVirtualDiskInfoChangeTrackingState uint32 = 0xF ) // CreateVhdx is a helper function to create a simple vhdx file at the given path using @@ -375,3 +422,60 @@ func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error } return nil } + +// SetVirtualDiskIdentifier sets the virtual disk identifier for the specified virtual disk. +func SetVirtualDiskIdentifier(vhdPath string, identifier guid.GUID) error { + handle, err := OpenVirtualDisk(vhdPath, VirtualDiskAccessNone, OpenVirtualDiskFlagNone) + if err != nil { + return fmt.Errorf("failed to open %s: %w", vhdPath, err) + } + defer syscall.Close(handle) + + info := &virtualDiskInfo{ + version: setVirtualDiskInfoIdentifier, + } + if strings.HasSuffix(vhdPath, ".vhdx") { + // VHDx requires a different version to set disk id + info.version = setVirtualDiskInfoVirtualDiskID + } + + if _, err := binary.Encode(info.data[:], binary.LittleEndian, identifier); err != nil { + return fmt.Errorf("failed to serialize virtual disk identifier: %w", err) + } + + if err := setVirtualDiskInformation(handle, info); err != nil { + return fmt.Errorf("failed to set virtual disk identifier: %w", err) + } + return nil +} + +// GetVirtualDiskIdentifier retrieves the virtual disk identifier for the specified virtual disk. +func GetVirtualDiskIdentifier(vhdPath string) (guid.GUID, error) { + handle, err := OpenVirtualDisk(vhdPath, VirtualDiskAccessNone, OpenVirtualDiskFlagNone) + if err != nil { + return guid.GUID{}, fmt.Errorf("failed to open %s: %w", vhdPath, err) + } + defer syscall.Close(handle) + + info := &virtualDiskInfo{ + version: getVirtualDiskInfoIdentifier, + } + if strings.HasSuffix(vhdPath, ".vhdx") { + // VHDx requires a different version to get disk id + info.version = getVirtualDiskInfoVirtualDiskID + } + + var sizeUsed uint32 + bufferSize := uint32(unsafe.Sizeof(*info)) + if err := getVirtualDiskInformation(handle, &bufferSize, info, &sizeUsed); err != nil { + return guid.GUID{}, fmt.Errorf("failed to get virtual disk identifier: %w", err) + } + + // Parse the response + id := &guid.GUID{} + reader := bytes.NewReader(info.data[:]) + if err := binary.Read(reader, binary.LittleEndian, id); err != nil { + return guid.GUID{}, fmt.Errorf("failed to parse virtual disk identifier: %w", err) + } + return *id, nil +} diff --git a/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go b/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go index 95c0407433..e9d202ed5d 100644 --- a/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go +++ b/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go @@ -42,8 +42,10 @@ var ( procAttachVirtualDisk = modvirtdisk.NewProc("AttachVirtualDisk") procCreateVirtualDisk = modvirtdisk.NewProc("CreateVirtualDisk") procDetachVirtualDisk = modvirtdisk.NewProc("DetachVirtualDisk") + procGetVirtualDiskInformation = modvirtdisk.NewProc("GetVirtualDiskInformation") procGetVirtualDiskPhysicalPath = modvirtdisk.NewProc("GetVirtualDiskPhysicalPath") procOpenVirtualDisk = modvirtdisk.NewProc("OpenVirtualDisk") + procSetVirtualDiskInformation = modvirtdisk.NewProc("SetVirtualDiskInformation") ) func attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (win32err error) { @@ -79,6 +81,14 @@ func detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, pro return } +func getVirtualDiskInformation(handle syscall.Handle, bufferSize *uint32, info *virtualDiskInfo, sizeUsed *uint32) (win32err error) { + r0, _, _ := syscall.SyscallN(procGetVirtualDiskInformation.Addr(), uintptr(handle), uintptr(unsafe.Pointer(bufferSize)), uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(sizeUsed))) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + func getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (win32err error) { r0, _, _ := syscall.SyscallN(procGetVirtualDiskPhysicalPath.Addr(), uintptr(handle), uintptr(unsafe.Pointer(diskPathSizeInBytes)), uintptr(unsafe.Pointer(buffer))) if r0 != 0 { @@ -103,3 +113,11 @@ func _openVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virt } return } + +func setVirtualDiskInformation(handle syscall.Handle, info *virtualDiskInfo) (win32err error) { + r0, _, _ := syscall.SyscallN(procSetVirtualDiskInformation.Addr(), uintptr(handle), uintptr(unsafe.Pointer(info))) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f0747c7edc..278a0a5d12 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -4,8 +4,8 @@ github.com/Microsoft/cosesign1go/pkg/cosesign1 # github.com/Microsoft/didx509go v0.0.3 ## explicit; go 1.20 github.com/Microsoft/didx509go/pkg/did-x509-resolver -# github.com/Microsoft/go-winio v0.6.2 -## explicit; go 1.21 +# github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 +## explicit; go 1.23.0 github.com/Microsoft/go-winio github.com/Microsoft/go-winio/backuptar github.com/Microsoft/go-winio/internal/fs From 12490991d54ec165d9857e6dc96853129c43d260 Mon Sep 17 00:00:00 2001 From: Amit Barve Date: Tue, 28 Oct 2025 17:01:33 -0400 Subject: [PATCH 2/2] Pass CWCOW UVM measurements to sidecar GCS A reference_info.cose file is usually shipped along with other UVM boot files. This file contains signed measurements of the UVM. We need to pass the contents of this file to the sidecar GCS so that it can make it available to the containers running inside the UVM. Signed-off-by: Amit Barve --- internal/gcs-sidecar/host.go | 1 + internal/oci/uvm.go | 1 + internal/protocol/guestresource/resources.go | 7 +------ internal/uvm/create_wcow.go | 5 +++++ internal/uvm/security_policy.go | 18 ++++++++++++++++++ internal/uvm/start.go | 1 + 6 files changed, 27 insertions(+), 6 deletions(-) diff --git a/internal/gcs-sidecar/host.go b/internal/gcs-sidecar/host.go index 85930cd496..82519ac414 100644 --- a/internal/gcs-sidecar/host.go +++ b/internal/gcs-sidecar/host.go @@ -182,6 +182,7 @@ func (h *Host) SetWCOWConfidentialUVMOptions(ctx context.Context, securityPolicy h.securityPolicyEnforcer = p h.securityPolicyEnforcerSet = true + h.uvmReferenceInfo = securityPolicyRequest.EncodedUVMReference return nil } diff --git a/internal/oci/uvm.go b/internal/oci/uvm.go index 204456da46..2183065b88 100644 --- a/internal/oci/uvm.go +++ b/internal/oci/uvm.go @@ -248,6 +248,7 @@ func handleWCOWSecurityPolicy(ctx context.Context, a map[string]string, wopts *u wopts.SecurityPolicyEnforcer = ParseAnnotationsString(a, annotations.WCOWSecurityPolicyEnforcer, wopts.SecurityPolicyEnforcer) wopts.DisableSecureBoot = ParseAnnotationsBool(ctx, a, annotations.WCOWDisableSecureBoot, false) wopts.GuestStateFilePath = ParseAnnotationsString(a, annotations.WCOWGuestStateFile, uvm.GetDefaultConfidentialVMGSPath()) + wopts.UVMReferenceInfoFile = ParseAnnotationsString(a, annotations.WCOWReferenceInfoFile, uvm.GetDefaultReferenceInfoFilePath()) wopts.IsolationType = "SecureNestedPaging" if noSecurityHardware := ParseAnnotationsBool(ctx, a, annotations.NoSecurityHardware, false); noSecurityHardware { wopts.IsolationType = "GuestStateOnly" diff --git a/internal/protocol/guestresource/resources.go b/internal/protocol/guestresource/resources.go index 5fe47b24ca..b956069107 100644 --- a/internal/protocol/guestresource/resources.go +++ b/internal/protocol/guestresource/resources.go @@ -244,10 +244,5 @@ type LCOWSecurityPolicyFragment struct { type WCOWConfidentialOptions struct { EnforcerType string `json:"EnforcerType,omitempty"` EncodedSecurityPolicy string `json:"EncodedSecurityPolicy,omitempty"` - // Optional security policy - WCOWSecurityPolicy string - // Set when there is a security policy to apply on actual SNP hardware, use this rathen than checking the string length - WCOWSecurityPolicyEnabled bool - // Set which security policy enforcer to use (open door or rego). This allows for better fallback mechanic. - WCOWSecurityPolicyEnforcer string + EncodedUVMReference string `json:"EncodedUVMReference,omitempty"` } diff --git a/internal/uvm/create_wcow.go b/internal/uvm/create_wcow.go index 752d726c2d..53a6373537 100644 --- a/internal/uvm/create_wcow.go +++ b/internal/uvm/create_wcow.go @@ -49,6 +49,7 @@ type ConfidentialWCOWOptions struct { SecurityPolicyEnabled bool // Set when there is a security policy to apply on actual SNP hardware, use this rathen than checking the string length SecurityPolicy string // Optional security policy SecurityPolicyEnforcer string // Set which security policy enforcer to use (open door or rego). This allows for better fallback mechanic. + UVMReferenceInfoFile string // Path to the file that contains the signed UVM measurements /* Below options are only included for testing/debugging purposes - shouldn't be used in regular scenarios */ IsolationType string @@ -90,6 +91,10 @@ func GetDefaultConfidentialEFIPath() string { return filepath.Join(defaultConfidentialWCOWOSBootFilesPath(), "boot.vhd") } +func GetDefaultReferenceInfoFilePath() string { + return filepath.Join(defaultConfidentialWCOWOSBootFilesPath(), "reference_info.cose") +} + // NewDefaultOptionsWCOW creates the default options for a bootable version of // WCOW. The caller `MUST` set the `BootFiles` on the returned value. // diff --git a/internal/uvm/security_policy.go b/internal/uvm/security_policy.go index ebd49dcec0..0dcf4fe693 100644 --- a/internal/uvm/security_policy.go +++ b/internal/uvm/security_policy.go @@ -53,6 +53,24 @@ func WithWCOWSecurityPolicyEnforcer(enforcer string) WCOWConfidentialUVMOpt { } } +// WithUVMReferenceInfo reads UVM reference info file and base64 encodes the +// content before setting it for the resource. This is no-op if the +// path is empty or the file doesn't exist. +func WithWCOWUVMReferenceInfo(path string) WCOWConfidentialUVMOpt { + return func(ctx context.Context, r *guestresource.WCOWConfidentialOptions) error { + encoded, err := base64EncodeFileContents(path) + if err != nil { + if os.IsNotExist(err) { + log.G(ctx).WithField("filePath", path).Debug("UVM reference info file not found") + return nil + } + return fmt.Errorf("failed to read UVM reference info file: %w", err) + } + r.EncodedUVMReference = encoded + return nil + } +} + func (uvm *UtilityVM) SetWCOWConfidentialUVMOptions(ctx context.Context, opts ...WCOWConfidentialUVMOpt) error { if uvm.operatingSystem != "windows" { return errNotSupported diff --git a/internal/uvm/start.go b/internal/uvm/start.go index b42e814624..321f5af67a 100644 --- a/internal/uvm/start.go +++ b/internal/uvm/start.go @@ -341,6 +341,7 @@ func (uvm *UtilityVM) Start(ctx context.Context) (err error) { copts := []WCOWConfidentialUVMOpt{ WithWCOWSecurityPolicy(uvm.createOpts.(*OptionsWCOW).SecurityPolicy), WithWCOWSecurityPolicyEnforcer(uvm.createOpts.(*OptionsWCOW).SecurityPolicyEnforcer), + WithWCOWUVMReferenceInfo(uvm.createOpts.(*OptionsWCOW).UVMReferenceInfoFile), } if err := uvm.SetWCOWConfidentialUVMOptions(ctx, copts...); err != nil { return err