[PATCH AUTOSEL 7.0-6.1] KVM: x86: Check for injected exceptions before queuing a debug exception
From: Sasha Levin
Date: Mon Apr 20 2026 - 11:18:56 EST
From: Yosry Ahmed <yosry@xxxxxxxxxx>
[ Upstream commit e907b4e72488f1df878e7e8acf88d23e49cb3ca7 ]
On KVM_SET_GUEST_DEBUG, if a #DB or #BP is injected with
KVM_GUESTDBG_INJECT_DB or KVM_GUESTDBG_INJECT_BP, KVM fails with -EBUSY
if there is an existing pending exception. This was introduced in
commit 4f926bf29186 ("KVM: x86: Polish exception injection via
KVM_SET_GUEST_DEBUG") to avoid a warning in kvm_queue_exception(),
presumably to avoid overriding a pending exception.
This added another (arguably nice) property, if there's a pending
exception, KVM_SET_GUEST_DEBUG cannot cause a #DF or triple fault.
However, if an exception is injected, KVM_SET_GUEST_DEBUG will cause
a #DF or triple fault in the guest, as kvm_multiple_exception() combines
them.
Check for both pending and injected exceptions for
KVM_GUESTDBG_INJECT_DB and KVM_GUESTDBG_INJECT_BP, to avoid accidentally
injecting a #DB or triple fault.
Signed-off-by: Yosry Ahmed <yosry@xxxxxxxxxx>
base-commit: a68a4bbc5b9ce5b722473399f05cb05217abaee8
Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---
LLM Generated explanations, may be completely bogus:
I have sufficient information to complete my analysis. Let me write up
the findings.
# Analysis: KVM x86 - Check for injected exceptions before queuing debug
exception
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1: SUBJECT LINE**
Record: Subsystem = "KVM: x86"; Action verb = "Check for" (implicit fix
- adds missing check); Summary = adds check for injected exceptions (not
just pending) before queuing #DB/#BP in KVM_SET_GUEST_DEBUG to prevent
accidental #DF/triple fault.
**Step 1.2: TAGS**
Record:
- Signed-off-by: Yosry Ahmed (author) and Sean Christopherson (KVM x86
maintainer, committer)
- base-commit: a68a4bbc5b9c
- NO Fixes: tag (expected - this is candidate review)
- NO Reported-by: (no syzbot tag directly on this patch, though cover
letter referenced syzkaller repro for the series)
- NO Reviewed-by/Tested-by/Acked-by on the final committed patch
- NO Cc: stable
**Step 1.3: COMMIT BODY**
Record:
- Bug description: `kvm_arch_vcpu_ioctl_set_guest_debug()` only checks
`kvm_is_exception_pending()` before queuing a #DB/#BP; if an exception
is currently *injected* (not pending), the check passes and
`kvm_queue_exception()` combines the two via
`kvm_multiple_exception()`, escalating to #DF or triple fault.
- Failure mode: Accidental #DF (double fault) or triple fault in the
guest.
- Author mentions introducing commit 4f926bf29186 from 2009.
**Step 1.4: HIDDEN BUG FIX?**
Record: Subject uses "Check for" rather than "fix", but body explicitly
describes a bug and the mechanism. This IS a bug fix.
## PHASE 2: DIFF ANALYSIS
**Step 2.1: INVENTORY**
Record: Single file `arch/x86/kvm/x86.c`, 1 line added, 1 line removed.
Function: `kvm_arch_vcpu_ioctl_set_guest_debug`. Surgical single-file
fix.
**Step 2.2: CODE FLOW CHANGE**
Record: Before: only returned -EBUSY if `kvm_is_exception_pending()`
(which checks `exception.pending`, `exception_vmexit.pending`,
`KVM_REQ_TRIPLE_FAULT`). After: also returns -EBUSY if
`vcpu->arch.exception.injected`.
**Step 2.3: BUG MECHANISM**
Record: Category (g) Logic/correctness fix - missing condition in
return-value check. Looking at `kvm_multiple_exception()` (x86.c:837+),
if another exception is already present (injected or pending), it either
synthesizes #DF (for contributory+contributory or PF+non-benign), or
escalates to triple fault if previous was DF. The fix prevents entering
`kvm_queue_exception` when injection is in progress.
**Step 2.4: FIX QUALITY**
Record: Obviously correct; surgical one-line addition of a boolean
condition to existing guard. No risk of deadlock/regression - it only
adds another case that returns -EBUSY, which is existing ioctl behavior
that userspace must already tolerate. Aligns with architectural
behavior: you cannot queue a new exception while one is being delivered.
## PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1: BLAME**
Record: Checked line; the code has been structured this way since
introduction in 2009.
**Step 3.2: ORIGINAL BUGGY COMMIT**
Record: Bug introduced in commit `4f926bf291863` ("KVM: x86: Polish
exception injection via KVM_SET_GUEST_DEBUG") by Jan Kiszka, Oct 2009.
`git describe --contains` = `v2.6.33-rc1~387^2~10`. This means the bug
exists in every active stable tree (5.4, 5.10, 5.15, 6.1, 6.6, 6.12,
etc.).
**Step 3.3: FILE HISTORY**
Record: `arch/x86/kvm/x86.c` has had 479 commits since v6.6. The
`kvm_is_exception_pending()` helper was introduced in commit
`7709aba8f7161` (v6.1-rc1). For stable trees >= v6.1, the fix applies
cleanly. For v5.15 and v5.10, an equivalent inline check against
`vcpu->arch.exception.pending || vcpu->arch.exception.injected` would be
needed (trivial adaptation).
**Step 3.4: AUTHOR CONTEXT**
Record: Yosry Ahmed is a regular KVM contributor (multiple nSVM/SVM
fixes merged, plus memory management). Sean Christopherson applied the
patch - he is the KVM x86 maintainer. Strong pedigree.
**Step 3.5: DEPENDENCIES**
Record: The commit was patch 3/3 of a series. Sean explicitly confirmed
on the list: "So you'll apply patch 3 as-is, drop patch 2, and
(potentially) take patch 1 and build another series on top of it?" --
"Yeah, that's where I'm trending." Patch 3 is explicitly standalone.
## PHASE 4: MAILING LIST RESEARCH
**Step 4.1: ORIGINAL DISCUSSION**
Record: `b4 dig -c e907b4e72488f` found the patch at
https://lore.kernel.org/all/20260227011306.3111731-4-yosry@xxxxxxxxxx/.
Single revision (v1). Sean Christopherson reviewed explicitly and said:
"First off, this patch looks good irrespective of nested crud.
Disallowing injection of #DB/#BP while there's already an injected
exception aligns with architectural behavior; KVM needs to finish
delivering the exception and thus 'complete' the instruction before
queueing a new exception." This is a strong endorsement from the
subsystem maintainer.
**Step 4.2: REVIEWERS**
Record: `b4 dig -w` showed recipients: Sean Christopherson (KVM x86
maintainer), Paolo Bonzini (KVM maintainer), kvm@ and linux-kernel@
mailing lists. Appropriate review coverage.
**Step 4.3: BUG REPORT**
Record: Cover letter references a syzkaller reproducer that triggers
nested_run_pending WARN; the series was motivated by syzkaller findings.
Patch 3 addresses an architectural correctness issue that the author
noticed while investigating, more than a direct syzkaller report.
**Step 4.4: SERIES CONTEXT**
Record: Part of "[PATCH 0/3] KVM: x86: Fix incorrect handling of triple
faults". Sean confirmed he'd apply patch 3 standalone and defer patches
1-2.
**Step 4.5: STABLE DISCUSSION**
Record: No explicit stable nomination was made by the author or reviewer
on the mailing list.
## PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1: KEY FUNCTIONS**
Record: `kvm_arch_vcpu_ioctl_set_guest_debug` is the only modified
function.
**Step 5.2: CALLERS**
Record: `kvm_arch_vcpu_ioctl_set_guest_debug` is the top-level ioctl
handler for `KVM_SET_GUEST_DEBUG` - invoked directly from userspace
through the KVM ioctl interface.
**Step 5.3: CALLEES**
Record: Relevant callees: `kvm_is_exception_pending()` (x86.h:198-203,
checks pending + exception_vmexit.pending + KVM_REQ_TRIPLE_FAULT),
`kvm_queue_exception()` → `kvm_multiple_exception()` (x86.c:837, which
is the function that synthesizes #DF or triple fault).
**Step 5.4: CALL CHAIN / REACHABILITY**
Record: Reachable directly from userspace ioctl `KVM_SET_GUEST_DEBUG`.
Any VMM/debugger that uses this ioctl with `KVM_GUESTDBG_INJECT_DB` or
`KVM_GUESTDBG_INJECT_BP` can hit this code path.
**Step 5.5: SIMILAR PATTERNS**
Record: Grep shows `vmx/vmx.c:6130` already combines both checks:
`(kvm_is_exception_pending(vcpu) || vcpu->arch.exception.injected)`. The
fix makes `kvm_arch_vcpu_ioctl_set_guest_debug` consistent with this
existing VMX pattern.
## PHASE 6: CROSS-REFERENCING STABLE TREE
**Step 6.1: CODE EXISTS IN STABLE?**
Record: The buggy code exists in ALL stable trees back to v2.6.33
(2010). The specific `kvm_is_exception_pending()` helper was added in
v6.1 (Oct 2022). For trees v6.1+, direct apply. For v5.15.y, v5.10.y,
the equivalent fix would use `vcpu->arch.exception.pending ||
vcpu->arch.exception.injected` directly.
**Step 6.2: BACKPORT COMPLICATIONS**
Record: For v6.1, v6.6, v6.12: clean apply expected. For v5.15 and
earlier: need to replace `kvm_is_exception_pending(vcpu)` with raw field
check (`vcpu->arch.exception.pending`) — trivial adaptation.
**Step 6.3: RELATED FIXES IN STABLE?**
Record: No prior fix for this specific issue in stable (checked mainline
history - single commit e907b4e72488f).
## PHASE 7: SUBSYSTEM CONTEXT
**Step 7.1: SUBSYSTEM**
Record: `arch/x86/kvm/` - KVM (Kernel-based Virtual Machine) x86
implementation. Criticality: IMPORTANT. KVM is widely used in
cloud/server workloads and affects any distro running VMs.
**Step 7.2: ACTIVITY**
Record: 479 commits since v6.6 in x86.c alone; very active subsystem.
Bug is not recent though - it's a 15-year-old latent bug.
## PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1: AFFECTED POPULATION**
Record: Users of KVM_SET_GUEST_DEBUG ioctl with INJECT_DB/BP - primarily
debuggers (gdb stub), QEMU, and other VMMs that perform guest
introspection. Not all VMMs use this, but it's common enough.
**Step 8.2: TRIGGER CONDITIONS**
Record: Requires (1) a VMM/debugger using KVM_SET_GUEST_DEBUG with
KVM_GUESTDBG_INJECT_DB or KVM_GUESTDBG_INJECT_BP, AND (2) timing where
the target vCPU has an already-injected exception at that moment. Not an
unprivileged trigger (VMM-level access needed).
**Step 8.3: FAILURE MODE**
Record: Accidental injection of #DF or triple fault into the guest -
causes guest kernel crash / reset. Severity: MEDIUM - guest-only impact
(no host compromise, no security escalation, no host-level crash).
**Step 8.4: RISK-BENEFIT**
Record: BENEFIT: medium (prevents unexpected guest crashes in a
debugger/VMM corner case, matches architectural behavior). RISK: very
low - 1-line addition of a condition to an existing -EBUSY return path
that userspace must already handle. Ratio strongly favors backporting.
## PHASE 9: FINAL SYNTHESIS
**Step 9.1: EVIDENCE**
FOR backporting:
- Minimal, surgical 1-line fix
- Explicitly endorsed by KVM x86 maintainer Sean Christopherson ("patch
looks good irrespective of nested crud")
- Fixes real bug present since 2009 in every active stable tree
- Matches architectural behavior and makes code consistent with
vmx.c:6130
- Fix quality is obvious by reading the code
- Low regression risk (only expands an existing -EBUSY guard)
- Author/committer are experienced KVM contributors
AGAINST backporting:
- No explicit stable tag/nomination on the list
- Impact is guest-only (not host)
- Requires specific user action via KVM_SET_GUEST_DEBUG ioctl
- For pre-v6.1 trees, minor adaptation is needed (inline the helper's
check)
**Step 9.2: STABLE RULES CHECKLIST**
1. Obviously correct and tested? YES - reviewed by maintainer, trivial
change, matches existing pattern.
2. Fixes a real bug? YES - guest-side #DF or triple fault.
3. Important issue? MEDIUM - guest kernel crash but not host-side.
4. Small and contained? YES - 1 line.
5. No new features or APIs? YES.
6. Applies cleanly? YES for v6.1+; trivial adaptation for v5.15 and
earlier.
**Step 9.3: EXCEPTION CATEGORIES**
Record: Does not fall into a trivial exception category (not a device
ID, not a quirk, not DT, not docs). But it IS a small, surgical bug fix
that fits the general stable criteria.
**Step 9.4: DECISION**
This is a small, surgical, well-reviewed fix for a long-standing latent
bug that can cause guest crashes through a KVM ioctl path. The subsystem
maintainer explicitly endorsed the patch as architecturally correct.
Regression risk is minimal (just an extra -EBUSY condition). The bug has
been present in every stable tree for 15+ years.
## Verification
- [Phase 1] Parsed tags: only SOB tags; no Fixes:/Reported-by:/Cc:stable
as expected for candidate patch.
- [Phase 2] Diff analysis: confirmed 1 line added / 1 removed in
`kvm_arch_vcpu_ioctl_set_guest_debug()`; verified the change adds `||
vcpu->arch.exception.injected` to existing condition.
- [Phase 2] `kvm_multiple_exception()` in x86.c:837-898 confirmed: when
`exception.injected=true`, code goes into #DF/triple-fault synthesis
path.
- [Phase 3] `git show 4f926bf29186` confirmed bug introduced by Jan
Kiszka in Oct 2009.
- [Phase 3] `git describe --contains 4f926bf29186` =
`v2.6.33-rc1~387^2~10` → bug present in all stable trees.
- [Phase 3] `git describe --contains e907b4e72488f` =
`next-20260313~...` → fix is in linux-next, not yet in a released tag.
- [Phase 3] `git describe --contains 7709aba8f7161` =
`v6.1-rc1~107^2~19` → `kvm_is_exception_pending()` helper exists only
in v6.1+.
- [Phase 4] `b4 dig -c e907b4e72488f` found original submission at
https://lore.kernel.org/all/20260227011306.3111731-4-yosry@xxxxxxxxxx/
- [Phase 4] `b4 dig -a` confirmed single v1 revision; no v2/v3 needed.
- [Phase 4] `b4 dig -w` confirmed recipients included Sean
Christopherson and Paolo Bonzini.
- [Phase 4] Read thread mbox: Sean explicitly said "this patch looks
good irrespective of nested crud" and confirmed he would apply patch 3
as-is, dropping patches 1-2 for later work.
- [Phase 4] Cover letter references syzkaller reproducer (manually
modified) but for the series as a whole; patch 3 itself is
architectural correctness.
- [Phase 5] `grep kvm_is_exception_pending` confirmed vmx.c:6130 uses
exactly the same combined check pattern - fix makes x86.c consistent.
- [Phase 6] File exists in all stable trees; helper
`kvm_is_exception_pending()` only in v6.1+, minor adaptation needed
for v5.15/v5.10.
- [Phase 7] `git log --pretty | rg "^Yosry"` confirmed Yosry Ahmed is
regular KVM contributor.
- [Phase 8] Reachability: KVM_SET_GUEST_DEBUG is the direct ioctl
handler - reachable from userspace VMMs/debuggers.
- UNVERIFIED: Whether any real-world VMM has actually hit this bug in
production (no specific user report in the patch).
- UNVERIFIED: Exact backport complexity for v5.15.y/v5.10.y - not tested
but a trivial inline expansion.
The fix is small, surgical, maintainer-endorsed as architecturally
correct, applies to a 15-year-old latent bug, is reachable from
userspace VMMs, and has very low regression risk. It meets stable kernel
criteria.
**YES**
arch/x86/kvm/x86.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index fd1c4a36b5936..58f7247814e06 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -12529,7 +12529,7 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
if (dbg->control & (KVM_GUESTDBG_INJECT_DB | KVM_GUESTDBG_INJECT_BP)) {
r = -EBUSY;
- if (kvm_is_exception_pending(vcpu))
+ if (kvm_is_exception_pending(vcpu) || vcpu->arch.exception.injected)
goto out;
if (dbg->control & KVM_GUESTDBG_INJECT_DB)
kvm_queue_exception(vcpu, DB_VECTOR);
--
2.53.0