EreTIk's Box » Cтатьи, исходники » Deadlock при использовании ObOpenObjectByPointer для ключей реестра на Win XP SP2


На описываемую далее взаимо-блокировку я наталкивался минимум дважды, и теперь решил описать суть проблемы. Есть полезная документированная функция ObOpenObjectByPointer, которая позволяет создавать описатели на объекты ядра по указателю на объект. Она, в ходе своего исполнения, вызывает security-обработчик для целевого типа объекта. Это и сыграло решающую роль в формировании описываемого deadlock'а.


Схема воспроизведения достаточно проста: на машине с Windows XP SP2 запускаем драйвер, который постоянно вызывает ObOpenObjectByPointer и процесс, который в бесконечном цикле загружает и выгружает hiv'ы реестра. Достаточно быстро система просто замирает. В частности, рабочая нить драйвера находится в следующем состоянии:


b28a5a60 80502b17 82365d90 82365d20 804fad6c nt!KiSwapContext+0x2f b28a5a6c 804fad6c 00000000 805591e0 82365d20 nt!KiSwapThread+0x6b b28a5a94 80534d40 00000000 00000000 00000000 nt!KeWaitForSingleObject+0x1c2 b28a5ad0 805352da b28a5b98 825bc680 b28a5b8c nt!ExpWaitForResource+0xd4 b28a5af0 80629d19 805591e0 00000001 80634df3 nt!ExAcquireResourceSharedLite+0xca b28a5afc 80634df3 b28a5b98 825bc680 b28a5b8c nt!CmpLockRegistry+0x27 b28a5b34 805be714 e1fc47c8 00000001 b28a5b68 nt!CmpSecurityMethod+0x17 b28a5b70 805beaf5 e1fc47c8 b28a5b98 e1fc47c4 nt!ObGetObjectSecurity+0x96 b28a5b9c 805bb8c2 e1fc47c8 b28a5cd4 00000001 nt!ObCheckObjectAccess+0x29 b28a5c38 805bbf13 00000001 825c6830 e1fc47c8 nt!ObpIncrementHandleCount+0x1e8 b28a5c98 805ba6d8 00000001 e1fc47c8 00000000 nt!ObpCreateHandle+0x161 b28a5d68 f8cc14ed e1fc47c8 00000200 00000000 nt!ObOpenObjectByPointer+0xa4 b28a5dac 805ce794 00000000 00000000 00000000 openkey!WorkThreadRoutine+0x5d b28a5ddc 805450ce f8cc1490 00000000 00000000 nt!PspSystemThreadStartup+0x34 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

Фактически, нить ожидает глобальный ресурс реестра (Cm-блокировка). Смотрим, кто же захватил этот ресурс:


1: kd> !locks 805591e0 Resource @ nt!CmpRegistryLock (0x805591e0) Exclusively owned Contention Count = 1975 NumberOfSharedWaiters = 8 Threads: 81f88c90-01<*> 82104b30-01 82365d20-01 81f95b08-01 82169798-01 825c48b8-01 82367480-01 824aa250-01 824026c8-01 1 total locks, 1 locks currently held 1: kd> .thread 81f88c90 Implicit thread is now 81f88c90 1: kd> kb *** Stack trace for last set context - .thread/.cxr resets it ChildEBP RetAddr Args to Child b283d940 80502b17 81f88d00 81f88c90 804fad6c nt!KiSwapContext+0x2f b283d94c 804fad6c 00000000 825bc7d8 81f88c90 nt!KiSwapThread+0x6b b283d974 80534d40 00000000 00000000 00000000 nt!KeWaitForSingleObject+0x1c2 b283d9b0 80535198 e1e6c360 e1e6c378 825bc680 nt!ExpWaitForResource+0xd4 b283d9d0 805bb735 825bc7d8 00000001 8251f368 nt!ExAcquireResourceExclusiveLite+0x8e b283da60 805bbf13 00000000 8236f6b0 e1e6c378 nt!ObpIncrementHandleCount+0x5b b283dac0 805ba515 00000000 e1e6c378 825bc680 nt!ObpCreateHandle+0x161 b283db10 8062358d 00000000 825bc680 80000600 nt!ObOpenObjectByName+0x267 b283dbe4 8054060c b283dca4 0002001f b283dc7c nt!NtOpenKey+0x1af b283dbe4 804ff9fd b283dca4 0002001f b283dc7c nt!KiFastCallEntry+0xfc b283dc68 80639d44 b283dca4 0002001f b283dc7c nt!ZwOpenKey+0x11 b283dca8 806310a7 e1d48628 00000020 e1d37d38 nt!CmpRemoveFromHiveFileList+0x48 b283dcc0 80620b5c e1d48628 00000020 e1d91518 nt!CmUnloadKey+0x55 b283dd58 8054060c 0012fee0 0012fef8 7c90eb94 nt!NtUnloadKey+0x18c

Как видно из стека вызовов, нить эксклюзивно захватившая глобальный ресурс реестра выгружает hive. И в свою очередь эта нить ожидает захвата ресурса дерева объектов (Ob-блокировка):


1: kd> !locks 825bc7d8 Resource @ 0x825bc7d8 Exclusively owned Contention Count = 9195 NumberOfExclusiveWaiters = 1 Threads: 82365d20-01<*> Threads Waiting On Exclusive Access: 81f88c90 1 total locks, 1 locks currently held 1: kd> .thread 82365d20 Implicit thread is now 82365d20 1: kd> kb *** Stack trace for last set context - .thread/.cxr resets it ChildEBP RetAddr Args to Child b28a5a60 80502b17 82365d90 82365d20 804fad6c nt!KiSwapContext+0x2f b28a5a6c 804fad6c 00000000 805591e0 82365d20 nt!KiSwapThread+0x6b b28a5a94 80534d40 00000000 00000000 00000000 nt!KeWaitForSingleObject+0x1c2 b28a5ad0 805352da b28a5b98 825bc680 b28a5b8c nt!ExpWaitForResource+0xd4 b28a5af0 80629d19 805591e0 00000001 80634df3 nt!ExAcquireResourceSharedLite+0xca b28a5afc 80634df3 b28a5b98 825bc680 b28a5b8c nt!CmpLockRegistry+0x27 b28a5b34 805be714 e1fc47c8 00000001 b28a5b68 nt!CmpSecurityMethod+0x17 b28a5b70 805beaf5 e1fc47c8 b28a5b98 e1fc47c4 nt!ObGetObjectSecurity+0x96 b28a5b9c 805bb8c2 e1fc47c8 b28a5cd4 00000001 nt!ObCheckObjectAccess+0x29 b28a5c38 805bbf13 00000001 825c6830 e1fc47c8 nt!ObpIncrementHandleCount+0x1e8 b28a5c98 805ba6d8 00000001 e1fc47c8 00000000 nt!ObpCreateHandle+0x161 b28a5d68 f8cc14ed e1fc47c8 00000200 00000000 nt!ObOpenObjectByPointer+0xa4 b28a5dac 805ce794 00000000 00000000 00000000 openkey!WorkThreadRoutine+0x5d b28a5ddc 805450ce f8cc1490 00000000 00000000 nt!PspSystemThreadStartup+0x34 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

Что и приводит нас к начальной нити, вызвавшей ObOpenObjectByPointer. Это и есть классический пример взаимо-блокировки, когда два синхро-объекта захватываются в разных нитях в разном порядке.


В коде WRK 1.2 эту ситуацию можно отследить по исходным кодам. И так же по исходным кодам можно убедиться, что ядро никогда не вызывает функции, приводящие к Ob-блокировке, не захватив предварительно Cm-блокировку.


В конце хочется сказать, что в WinXP SP3 ситуация в корне изменилась. По коду нет вызовов, приводящих к захвату Ob-блокировки при захваченной Cm-блокировке. Поэтому на WinXP SP3 и старше можно смело использовать ObOpenObjectByPointer для объектов ключей реестра.


ΞρεΤΙκ