# DF-5123 — `hypercall_md()` inline asm violates the GCC asm contract: r8 lifetime unprotected between two asm statements, indirect-call asm missing clobbers

Severity: **Info** (latent miscompile hazard) · Confidence: **likely** (the contract violation is certain by inspection; an actual miscompile was *not* produced on the shipping compiler — see verdict)

## What this is

`sys/dev/virtual/hyperv/x86_64/hyperv_machdep.c:74-84`:

```c
	__asm__ __volatile__ ("mov %0, %%r8" : : "r" (out_paddr): "r8");
	__asm__ __volatile__ ("call *%3" : "=a" (status) :
	    "c" (in_val), "d" (in_paddr), "m" (hc_addr));
```

Three contract violations in one function:

1. **r8 lifetime.** The first asm *declares r8 clobbered* while depending on
   r8 surviving **until the second asm executes**. The compiler is entitled
   to allocate r8 for anything between the two statements (including the
   input-register setup moves for the second asm). The Hyper-V hypercall
   ABI argument in r8 (output-page GFN) is therefore carried across a gap
   the compiler knows nothing about — silently dropping or corrupting
   `out_paddr`.
2. **Missing call clobbers.** `call *%3` can clobber every caller-saved
   register (rax,rcx,rdx,rsi,rdi,r8-r11) and flags; the asm declares none
   of r9-r11/rsi/rdi/"cc".
3. **No "memory" clobber.** Hypercalls read their input page and write
   their output page in memory; without "memory" the compiler may
   reorder/cache memory traffic around the asm (relevant if the function
   is ever inlined — e.g. under LTO).

Inherited verbatim from FreeBSD's hyperv_machdep.c.

## Reproduce (codegen evidence — done on the audit guest)

`build.sh` compiles an exact copy of the function (plus an
`always_inline` variant exercising the inlined case) with the guest's
gcc 8.3 at -O2 and disassembles. Observed
(`objdump_gcc83_O2.txt`):

```
hypercall_md:
   0: mov    %rdi,-0x8(%rsp)
   5: mov    %rcx,%r8          <- asm #1
   8: mov    %rsi,%rcx         <- compiler-emitted, BETWEEN the two asms
   b: callq  *-0x8(%rsp)       <- asm #2
```

The `mov %rsi,%rcx` sitting **between** the two asm statements is the
demonstration: the compiler does place instructions in exactly the gap
where r8 must be preserved, and nothing in the asm constraints stops a
different allocation decision from putting a conflicting use of r8 there.
With gcc 8.3 at -O2 the standalone function happens to leave r8 intact —
so the *consequence* (hypercall invoked with a garbage output-page GFN /
stale hypercall pages) does not manifest today; the defect is real but
latent, hence Info.

## Recommended fix

Merge into a single asm and declare the full call effect
(see `fix.diff`):

```c
	__asm__ __volatile__ ("movq %3, %%r8\n\tcall *%4"
	    : "=a" (status)
	    : "c" (in_val), "d" (in_paddr), "r" (out_paddr), "m" (hc_addr)
	    : "r8", "r9", "r10", "r11", "memory", "cc");
```
