From a427e18ed0725a445d385203ac0f0e12779d7a36 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Aug 2026 11:41:02 +0000 Subject: [PATCH] cm: Fix KCB lock leaks in CmpLookInCache and CmpDoOpen Two related bugs in the CM registry parser that can cause KCB pushlock deadlocks under SMP: 1. CmpLookInCache: when a key is found in the cache and CmpReferenceKeyControlBlock fails for the newly-found KCB, the function returned STATUS_UNSUCCESSFUL without calling CmpUnLockKcbArray(LockedKcbs). Every subsequent attempt to acquire that pushlock (shared or exclusive) would block permanently, causing the deadlocks observed on the SMP branch. Fix: reference the new KCB *before* dropping the old one so that on failure we can cleanly unlock the array and release the temporary reference, leaving the caller with a consistent state. 2. CmpDoOpen (CMP_OPEN_KCB_NO_CREATE, symlink-found path): after CmpUnLockKcbArray / CmpAcquireKcbLockExclusiveByIndex the lock held on the cached KCB is exclusive, but IsLockShared was left TRUE. The wrong flag was then passed to EnlistKeyBodyWithKCB which, when the KeyBodyArray is full, would release the wrong lock (the RealKcb's lock, which was never acquired) and acquire exclusive on an already-exclusively-held KCB. Fix: set IsLockShared = FALSE after upgrading to the exclusive lock. --- ntoskrnl/config/cmparse.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/ntoskrnl/config/cmparse.c b/ntoskrnl/config/cmparse.c index db2ec0e970507..4e9849b0234c2 100644 --- a/ntoskrnl/config/cmparse.c +++ b/ntoskrnl/config/cmparse.c @@ -688,13 +688,15 @@ CmpDoOpen(IN PHHIVE Hive, /* * The symlink has been found. As in the similar case above, - * the KCB of the symlink exclusively, we don't want anybody - * to mess it up. + * lock the KCB of the symlink exclusively; we don't want + * anybody to mess it up. The lock is now exclusive so + * update IsLockShared accordingly. */ CmpUnLockKcbArray(KcbsLocked); CmpAcquireKcbLockExclusiveByIndex(GET_HASH_INDEX((*CachedKcb)->ConvKey)); KcbsLocked[0] = 1; KcbsLocked[1] = GET_HASH_INDEX((*CachedKcb)->ConvKey); + IsLockShared = FALSE; } else { @@ -1610,20 +1612,27 @@ CmpLookInCache( if (KeyFoundInCache) { /* - * Before we change the KCB we must dereference the prior - * KCB that we no longer need it. + * Reference the new KCB before dropping the old one. If this + * fails we must unlock the KCBs and release the temporary + * reference we took at the start of this function before + * returning, so that the caller sees a clean state. */ - CmpDereferenceKeyControlBlock(*Kcb); - *Kcb = CurrentKcb; - - /* Reference the new KCB now */ - if (!CmpReferenceKeyControlBlock(*Kcb)) + if (!CmpReferenceKeyControlBlock(CurrentKcb)) { /* This key is opened too many times, bail out */ - DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb); + DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", CurrentKcb); + CmpUnLockKcbArray(LockedKcbs); + CmpDereferenceKeyControlBlock(*Kcb); return STATUS_UNSUCCESSFUL; } + /* + * Dereference the prior KCB that we no longer need + * and switch to the newly referenced one. + */ + CmpDereferenceKeyControlBlock(*Kcb); + *Kcb = CurrentKcb; + /* Update hive and cell data from current KCB */ *Hive = CurrentKcb->KeyHive; *Cell = CurrentKcb->KeyCell;