Borys Tsyrulnikov

CVE-2026-89751: one extra bit in Linux TDX

CVE-2026-89751

On September 11, the Linux kernel CVE team published CVE-2026-89751 for a bug I reported in March. A mask in the TDX guest code included one extra bit during port I/O. Kiryl Shutsemau wrote the upstream fix. The commit credits me as the reporter.

My report also identified a separate error in 32-bit register handling. Both fixes are now upstream. The CVE record covers the mask error.

Where the bug sat

A TDX guest must work with the host virtual machine monitor for some I/O operations. The guest kernel emulates port I/O in its virtualization-exception handler, called the #VE handler. That code must preserve the register behavior defined by x86.

The affected functions were handle_in() and handle_out() in arch/x86/coco/tdx/tdx.c. They used GENMASK to select the bits for the requested I/O width. The calculation used the bit count as the upper endpoint, but GENMASK includes that endpoint. The result was a mask one bit wider than the operation required. The upstream fix corrects that endpoint in both functions.

On input, the error could change a register bit outside the requested width. On output, it could include an extra register bit in the value passed to the host. This put the error at a guest/host boundary where the exact data width matters.

What I verified

My original public report included live tests on a GCP TDX confidential VM. The tests confirmed incorrect register handling. I repeated the read-side test on a fresh VM and observed the same result.

The report also set limits on the finding. I did not establish a broad exploit through common kernel callers. Many typed I/O paths truncated or zero-extended values under the GCC and Clang toolchains I checked. The write-side tests did not include host-side capture on a VMM that I controlled. They did not establish a complete confidentiality exploit.

Those limits still matter after CVE assignment. The demonstrated result was incorrect guest emulation, with possible security effects that depend on the caller and guest configuration.

Amazon Linux assesses the CVE as Low, with a CVSS 3.1 score of 3.0. Its score assumes local access, high privileges, and high attack complexity. This is Amazon Linux's assessment. Its listed Amazon Linux packages are marked not affected.

The second issue concerned 32-bit input operations. In 64-bit mode, x86 requires a 32-bit general-purpose register write to clear the upper half of that register. The TDX handler preserved upper bits instead. The separate fix corrects this behavior and also carries my reporter credit.

The review went beyond the initial two patches. Maintainers discussed how to express the register rules clearly and share existing code. The final series moved KVM's register-assignment helper into a shared header as insn_assign_reg(). The TDX handler then used it. KVM's helper implementation was preserved.

Kiryl posted version 6 of the series on July 13. Dave Hansen applied it to the x86/tdx branch that day. The series contained two bug fixes and one helper move. The helper move was a supporting change.

This is the part of the review I find useful: the final correction made two emulation paths share the same register rules.

Fixed releases

The Linux CNA record traces the mask bug to Linux 5.19. As of September 29, it lists these first fixed releases:

Kernel branchFirst release with the mask fix
6.16.1.188
6.66.6.157
6.126.12.109
6.186.18.50
7.27.2.4
Mainline7.3-rc1

For a deployed system, use a supported kernel update from your distribution. Distribution kernels can contain backports without matching these upstream version numbers. Check the distribution's advisory for this CVE. The table describes the mask fix only.

Timeline

2026-03-30
Live TDX tests and public report.
2026-03-31
Kiryl posted the first two-patch series.
2026-07-13
Version 6 was posted and applied to the x86/tdx branch.
2026-09-03
Stable maintainers queued the mask fix for five kernel branches.
2026-09-11
The Linux kernel CVE team published CVE-2026-89751.

References