gcs: negotiate a guest/host contract version to detect drift - #2889
Draft
Jie Chen (jiechen0826) wants to merge 1 commit into
Draft
gcs: negotiate a guest/host contract version to detect drift#2889Jie Chen (jiechen0826) wants to merge 1 commit into
Jie Chen (jiechen0826) wants to merge 1 commit into
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
NegotiateProtocoland the host enforces at connect.internal/gcscompat(new, build-tag neutral) is the single source of truth:GuestHostContractVersion,MinCompatibleContractVersion, and aCompatible()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.[min..max]range as additiveomitemptyfields (host inNegotiateProtocolRequest, guest inGcsCapabilities), 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.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
GuestHostContractVersionin 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).internal/gcs:connect()rejects a mismatched guest range and accepts a compatible one.internal/guest/bridge:negotiateProtocolV2rejects a mismatched host range and accepts a compatible one, and advertises the guest range back.All pass (
go teston Windows for the host packages,GOOS=linuxfor 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.