Skip to content

gcs: negotiate a guest/host contract version to detect drift - #2889

Draft
Jie Chen (jiechen0826) wants to merge 1 commit into
microsoft:mainfrom
jiechen0826:gcs-contract-version
Draft

gcs: negotiate a guest/host contract version to detect drift#2889
Jie Chen (jiechen0826) wants to merge 1 commit into
microsoft:mainfrom
jiechen0826:gcs-contract-version

Conversation

@jiechen0826

Copy link
Copy Markdown
Contributor

Problem

The HCS <-> GCS bridge negotiates only a protocol version, which is frozen at 4 (prot.PvV4) and bumps only for an epochal bridge rewrite. GCS is built from this repo but ships separately from the host (it is embedded into the UVM image), so the two binaries can be built from source commits whose guest/host message contract has drifted. Because the bridge speaks permissive JSON (unknown fields ignored, missing fields zeroed) and the protocol version does not move for ordinary changes, a mispaired host and GCS still negotiate successfully and then fail later in confusing ways (RPC timeouts, JSON errors, unexpected behavior).

Change

Introduce a guest/host contract version, independent of the frozen protocol version, that both sides advertise during NegotiateProtocol and the host enforces at connect.

  • internal/gcscompat (new, build-tag neutral) is the single source of truth: GuestHostContractVersion, MinCompatibleContractVersion, and a Compatible() range-overlap check. Both the Windows host and the Linux guest compile the same constants, so the values can only differ at runtime when the two binaries came from incompatible commits.
  • Each side advertises its [min..max] range as additive omitempty fields (host in NegotiateProtocolRequest, guest in GcsCapabilities), plus a source commit for diagnostics. Peers that predate the contract advertise no range (MaxContractVersion == 0) and are skipped, so already-deployed UVM images keep working.
  • If the ranges do not overlap, the host fails connect() with an actionable error naming both commits and ranges, and the guest rejects negotiation. The mismatch now surfaces at the first GCS connection instead of downstream.

Bump rule: increment GuestHostContractVersion in any change to the guest/host contract that both sides must agree on (a change that is not backward compatible).

Testing

  • internal/gcscompat: overlap unit tests (boundaries, disjoint, symmetry, self-compatibility).
  • Host internal/gcs: connect() rejects a mismatched guest range and accepts a compatible one.
  • Guest internal/guest/bridge: negotiateProtocolV2 rejects a mismatched host range and accepts a compatible one, and advertises the guest range back.

All pass (go test on Windows for the host packages, GOOS=linux for the guest packages).

Notes

Draft for discussion. This is the runtime half of a larger effort; a follow-up would stamp the range into build artifacts and add a pre-test validator so a mispairing can also be caught before a VM boots. Feedback on the mechanism and on the placement of the shared constant is welcome.

The HCS<->GCS bridge negotiates only a protocol version that is frozen at 4
(prot.PvV4), so a host and GCS built from source commits whose message
contract has drifted still connect successfully and then fail later in
confusing ways (RPC timeouts, JSON errors). GCS ships separately from the
host, as an RPM embedded in the UVM image, so this mispairing is easy to hit.

Add a guest/host contract version, independent of the frozen protocol version,
that both sides advertise during NegotiateProtocol and the host enforces at
connect:

- internal/gcscompat holds the single source of truth
  (GuestHostContractVersion, MinCompatibleContractVersion) plus a Compatible()
  range-overlap check. Both the Windows host and the Linux guest compile the
  same constants, so the values can only differ at runtime when the two
  binaries came from incompatible commits.
- Each side advertises its [min..max] range as additive, omitempty fields
  (host in NegotiateProtocolRequest, guest in GcsCapabilities) plus a source
  commit for diagnostics. Peers that predate the contract advertise no range
  and are skipped, so already-deployed UVM images keep working.
- If the ranges do not overlap, the host fails connect() with an actionable
  message naming both commits and ranges, and the guest rejects negotiation.
  The mispairing now surfaces at the first GCS connection instead of as a
  downstream failure.

Bump GuestHostContractVersion in any change to the guest/host contract that
both sides must agree on.

Signed-off-by: Jie Chen <jiechen3@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant