Skip to content

Commit 869709c

Browse files
committed
cmd/containerd-shim-runhcs-v1: serialize confidential container bring-up
Starting the containers of a multi-container container group in a single confidential WCOW UVM concurrently reliably crashes the guest: the guest resets and the GCS bridge drops ("bridge closed: use of closed network connection"), and all containers end up Exited. Root cause is concurrent container bring-up into one confidential UVM. createContainer runs on its own goroutine per container, so two containers' hcsoci.CreateContainer calls overlap. CreateContainer performs the container bring-up: block-CIM mount, scratch SCSI attach, CombineLayers/hive-merge and the guest container create. The host-side device hot-adds (uvm.modify -> hcsSystem.Modify) go straight to the VM worker and do not travel over the GCS bridge, so nothing serializes them; overlapping mount/device operations into the confidential guest put it into a bad state and it resets. Serialize the bring-up with a per-UVM lock held across CreateContainer, taken only for confidential UVMs (HasConfidentialPolicy). This makes the container starts effectively one-at-a-time into a given confidential UVM. createContainer is shared with LCOW, hence the generic name and the confidential-only guard. Validated on a confidential WCOW UVM (VBS): concurrent multi-container groups that previously crashed every time (2x nanoserver, 2x mount-host, 1x mount-host + 2x nanoserver) now come up cleanly and repeatably; removing the lock reproduces the crash on the same image. Signed-off-by: Emanuel Oprea <emanueloprea@microsoft.com>
1 parent ef47025 commit 869709c

3 files changed

Lines changed: 37 additions & 0 deletions

File tree

cmd/containerd-shim-runhcs-v1/task_hcs.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,18 @@ func createContainer(
171171
if shimOpts != nil {
172172
opts.ScaleCPULimitsToSandbox = shimOpts.ScaleCpuLimitsToSandbox
173173
}
174+
// Serialize the per-container bring-up (layer mount + hive-merge + guest
175+
// container create) into a single confidential WCOW UVM. Concurrent
176+
// container starts in a multi-container CG otherwise issue overlapping
177+
// guest mount/create operations that the confidential guest cannot
178+
// handle, triggering a guest reset that drops the GCS bridge
179+
// ("bridge closed: use of closed network connection"). Serializing makes
180+
// the starts effectively one-at-a-time, which is reliable. Only taken for
181+
// confidential containers; createContainer is shared with LCOW.
182+
if parent != nil && parent.HasConfidentialPolicy() {
183+
parent.LockContainerCreate()
184+
defer parent.UnlockContainerCreate()
185+
}
174186
container, resources, err = hcsoci.CreateContainer(ctx, opts)
175187
if err != nil {
176188
return nil, nil, err

internal/uvm/combine_layers.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,22 @@ import (
1010
"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
1111
)
1212

13+
// LockContainerCreate acquires the per-UVM lock that serializes a container's
14+
// bring-up (block-CIM mount + scratch SCSI attach + CombineLayers/hive-merge +
15+
// guest container create). It is intended for confidential containers: concurrent
16+
// container starts otherwise issue overlapping guest mount/create operations into
17+
// the single confidential UVM, which the guest cannot handle and responds to with
18+
// a guest reset that drops the GCS bridge.
19+
// Callers must pair this with UnlockContainerCreate (typically via defer).
20+
func (uvm *UtilityVM) LockContainerCreate() {
21+
uvm.containerCreateLock.Lock()
22+
}
23+
24+
// UnlockContainerCreate releases the lock acquired by LockContainerCreate.
25+
func (uvm *UtilityVM) UnlockContainerCreate() {
26+
uvm.containerCreateLock.Unlock()
27+
}
28+
1329
// CombineLayersWCOW combines `layerPaths` with `containerRootPath` into the
1430
// container file system.
1531
//

internal/uvm/types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,15 @@ type UtilityVM struct {
144144
blockCIMMounts map[string]*UVMMountedBlockCIMs
145145
blockCIMMountLock sync.Mutex
146146

147+
// containerCreateLock serializes the per-container bring-up (block-CIM
148+
// mount + scratch SCSI attach + CombineLayers/hive-merge + guest container
149+
// create) into a single UVM. It is only taken for confidential containers
150+
// (see createContainer): concurrent container starts in a confidential WCOW
151+
// UVM otherwise issue overlapping guest mount/create operations that the
152+
// guest cannot handle, causing a guest reset that drops the GCS bridge
153+
// ("bridge closed: use of closed network connection").
154+
containerCreateLock sync.Mutex
155+
147156
logForwardingEnabled bool // Indicates whether to forward logs from the UVM to the host
148157
defaultLogSourcesEnabled bool // Specifies whether addition of default list of ETW providers should be disabled
149158
logSources string // ETW providers to enable for log forwarding

0 commit comments

Comments
 (0)