2007-12-15 18:14:41 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS Kernel
|
|
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
|
|
* FILE: ntoskrnl/config/cmkcbncb.c
|
|
|
|
* PURPOSE: Routines for handling KCBs, NCBs, as well as key hashes.
|
|
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
|
|
|
|
#include <ntoskrnl.h>
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
|
|
|
|
ULONG CmpHashTableSize = 2048;
|
|
|
|
PCM_KEY_HASH_TABLE_ENTRY CmpCacheTable;
|
|
|
|
PCM_NAME_HASH_TABLE_ENTRY CmpNameCacheTable;
|
|
|
|
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
2010-11-02 16:29:06 +00:00
|
|
|
INIT_FUNCTION
|
2007-12-15 18:14:41 +00:00
|
|
|
CmpInitializeCache(VOID)
|
|
|
|
{
|
|
|
|
ULONG Length, i;
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Calculate length for the table */
|
|
|
|
Length = CmpHashTableSize * sizeof(CM_KEY_HASH_TABLE_ENTRY);
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Allocate it */
|
2009-08-30 19:20:30 +00:00
|
|
|
CmpCacheTable = CmpAllocate(Length, TRUE, TAG_CM);
|
2007-12-15 18:14:41 +00:00
|
|
|
if (!CmpCacheTable)
|
|
|
|
{
|
|
|
|
/* Take the system down */
|
|
|
|
KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 1, 0, 0);
|
|
|
|
}
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Zero out the table */
|
|
|
|
RtlZeroMemory(CmpCacheTable, Length);
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Initialize the locks */
|
|
|
|
for (i = 0;i < CmpHashTableSize; i++)
|
|
|
|
{
|
|
|
|
/* Setup the pushlock */
|
2011-04-08 12:30:33 +00:00
|
|
|
ExInitializePushLock(&CmpCacheTable[i].Lock);
|
2007-12-15 18:14:41 +00:00
|
|
|
}
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Calculate length for the name cache */
|
|
|
|
Length = CmpHashTableSize * sizeof(CM_NAME_HASH_TABLE_ENTRY);
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Now allocate the name cache table */
|
2009-08-30 19:20:30 +00:00
|
|
|
CmpNameCacheTable = CmpAllocate(Length, TRUE, TAG_CM);
|
2007-12-15 18:14:41 +00:00
|
|
|
if (!CmpNameCacheTable)
|
|
|
|
{
|
|
|
|
/* Take the system down */
|
|
|
|
KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 3, 0, 0);
|
|
|
|
}
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Zero out the table */
|
|
|
|
RtlZeroMemory(CmpNameCacheTable, Length);
|
|
|
|
|
|
|
|
/* Initialize the locks */
|
|
|
|
for (i = 0;i < CmpHashTableSize; i++)
|
|
|
|
{
|
|
|
|
/* Setup the pushlock */
|
2011-04-08 12:30:33 +00:00
|
|
|
ExInitializePushLock(&CmpNameCacheTable[i].Lock);
|
2007-12-15 18:14:41 +00:00
|
|
|
}
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Setup the delayed close table */
|
|
|
|
CmpInitializeDelayedCloseTable();
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
CmpRemoveKeyHash(IN PCM_KEY_HASH KeyHash)
|
|
|
|
{
|
|
|
|
PCM_KEY_HASH *Prev;
|
|
|
|
PCM_KEY_HASH Current;
|
2007-12-16 16:37:15 +00:00
|
|
|
ASSERT_VALID_HASH(KeyHash);
|
2007-12-15 18:14:41 +00:00
|
|
|
|
|
|
|
/* Lookup all the keys in this index entry */
|
2017-08-16 20:30:45 +00:00
|
|
|
Prev = &GET_HASH_ENTRY(CmpCacheTable, KeyHash->ConvKey)->Entry;
|
2007-12-15 18:14:41 +00:00
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
/* Save the current one and make sure it's valid */
|
|
|
|
Current = *Prev;
|
|
|
|
ASSERT(Current != NULL);
|
2007-12-16 16:37:15 +00:00
|
|
|
ASSERT_VALID_HASH(Current);
|
2007-12-15 18:14:41 +00:00
|
|
|
|
|
|
|
/* Check if it matches */
|
|
|
|
if (Current == KeyHash)
|
|
|
|
{
|
|
|
|
/* Then write the previous one */
|
|
|
|
*Prev = Current->NextHash;
|
2007-12-16 16:37:15 +00:00
|
|
|
if (*Prev) ASSERT_VALID_HASH(*Prev);
|
2007-12-15 18:14:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise, keep going */
|
|
|
|
Prev = &Current->NextHash;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PCM_KEY_CONTROL_BLOCK
|
|
|
|
NTAPI
|
|
|
|
CmpInsertKeyHash(IN PCM_KEY_HASH KeyHash,
|
|
|
|
IN BOOLEAN IsFake)
|
|
|
|
{
|
|
|
|
ULONG i;
|
|
|
|
PCM_KEY_HASH Entry;
|
2007-12-16 16:37:15 +00:00
|
|
|
ASSERT_VALID_HASH(KeyHash);
|
2007-12-15 18:14:41 +00:00
|
|
|
|
|
|
|
/* Get the hash index */
|
|
|
|
i = GET_HASH_INDEX(KeyHash->ConvKey);
|
|
|
|
|
|
|
|
/* If this is a fake key, increase the key cell to use the parent data */
|
|
|
|
if (IsFake) KeyHash->KeyCell++;
|
|
|
|
|
|
|
|
/* Loop the hash table */
|
|
|
|
Entry = CmpCacheTable[i].Entry;
|
|
|
|
while (Entry)
|
|
|
|
{
|
|
|
|
/* Check if this matches */
|
2007-12-16 16:37:15 +00:00
|
|
|
ASSERT_VALID_HASH(Entry);
|
2007-12-15 18:14:41 +00:00
|
|
|
if ((KeyHash->ConvKey == Entry->ConvKey) &&
|
|
|
|
(KeyHash->KeyCell == Entry->KeyCell) &&
|
|
|
|
(KeyHash->KeyHive == Entry->KeyHive))
|
|
|
|
{
|
|
|
|
/* Return it */
|
|
|
|
return CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Keep looping */
|
|
|
|
Entry = Entry->NextHash;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No entry found, add this one and return NULL since none existed */
|
|
|
|
KeyHash->NextHash = CmpCacheTable[i].Entry;
|
|
|
|
CmpCacheTable[i].Entry = KeyHash;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PCM_NAME_CONTROL_BLOCK
|
|
|
|
NTAPI
|
|
|
|
CmpGetNameControlBlock(IN PUNICODE_STRING NodeName)
|
|
|
|
{
|
|
|
|
PCM_NAME_CONTROL_BLOCK Ncb = NULL;
|
|
|
|
ULONG ConvKey = 0;
|
|
|
|
PWCHAR p, pp;
|
|
|
|
ULONG i;
|
|
|
|
BOOLEAN IsCompressed = TRUE, Found = FALSE;
|
|
|
|
PCM_NAME_HASH HashEntry;
|
[HAL/NDK]
- Make Vector parameter in HalEnableSystemInterrupt, HalDisableSystemInterrupt and HalBeginSystemInterrupt an ULONG, not an UCHAR
[NDK]
- 64bit fixes for HANDLE_TABLE, KPROCESS, SECTION_IMAGE_INFORMATION, MMADDRESS_LIST, MMVAD_FLAGS, MMVAD, MMVAD_LONG, MMVAD_SHORT, MEMORY_DESCRIPTOR, MEMORY_ALLOCATION_DESCRIPTOR, LdrVerifyMappedImageMatchesChecksum
- KDPC_DATA::DpcQueueDepth is signed on amd64, unsigned on x86
[NTOSKRNL]
- Fix hundreds of MSVC and amd64 warnings
- add a pragma message to FstubFixupEfiPartition, since it looks broken
- Move portable Ke constants from <arch>/cpu.c to krnlinit.c
- Fixed a bug in amd64 KiGeneralProtectionFaultHandler
svn path=/trunk/; revision=53734
2011-09-18 13:11:45 +00:00
|
|
|
ULONG NcbSize;
|
|
|
|
USHORT Length;
|
2007-12-15 18:14:41 +00:00
|
|
|
|
|
|
|
/* Loop the name */
|
|
|
|
p = NodeName->Buffer;
|
|
|
|
for (i = 0; i < NodeName->Length; i += sizeof(WCHAR))
|
|
|
|
{
|
|
|
|
/* Make sure it's not a slash */
|
|
|
|
if (*p != OBJ_NAME_PATH_SEPARATOR)
|
|
|
|
{
|
|
|
|
/* Add it to the hash */
|
|
|
|
ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Next character */
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set assumed lengh and loop to check */
|
|
|
|
Length = NodeName->Length / sizeof(WCHAR);
|
|
|
|
for (i = 0; i < (NodeName->Length / sizeof(WCHAR)); i++)
|
|
|
|
{
|
2009-02-05 21:30:21 +00:00
|
|
|
/* Check if this is a 16-bit character */
|
|
|
|
if (NodeName->Buffer[i] > (UCHAR)-1)
|
2007-12-15 18:14:41 +00:00
|
|
|
{
|
|
|
|
/* This is the actual size, and we know we're not compressed */
|
|
|
|
Length = NodeName->Length;
|
|
|
|
IsCompressed = FALSE;
|
2009-02-05 21:30:21 +00:00
|
|
|
break;
|
2007-12-15 18:14:41 +00:00
|
|
|
}
|
|
|
|
}
|
2009-02-05 21:30:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Lock the NCB entry */
|
|
|
|
CmpAcquireNcbLockExclusiveByKey(ConvKey);
|
|
|
|
|
|
|
|
/* Get the hash entry */
|
2017-08-16 20:30:45 +00:00
|
|
|
HashEntry = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey)->Entry;
|
2007-12-15 18:14:41 +00:00
|
|
|
while (HashEntry)
|
|
|
|
{
|
|
|
|
/* Get the current NCB */
|
|
|
|
Ncb = CONTAINING_RECORD(HashEntry, CM_NAME_CONTROL_BLOCK, NameHash);
|
|
|
|
|
|
|
|
/* Check if the hash matches */
|
2009-02-16 11:18:37 +00:00
|
|
|
if ((ConvKey == HashEntry->ConvKey) && (Length == Ncb->NameLength))
|
2007-12-15 18:14:41 +00:00
|
|
|
{
|
|
|
|
/* Assume success */
|
|
|
|
Found = TRUE;
|
|
|
|
|
|
|
|
/* If the NCB is compressed, do a compressed name compare */
|
|
|
|
if (Ncb->Compressed)
|
|
|
|
{
|
|
|
|
/* Compare names */
|
|
|
|
if (CmpCompareCompressedName(NodeName, Ncb->Name, Length))
|
|
|
|
{
|
|
|
|
/* We failed */
|
|
|
|
Found = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Do a manual compare */
|
|
|
|
p = NodeName->Buffer;
|
|
|
|
pp = Ncb->Name;
|
|
|
|
for (i = 0; i < Ncb->NameLength; i += sizeof(WCHAR))
|
|
|
|
{
|
|
|
|
/* Compare the character */
|
|
|
|
if (RtlUpcaseUnicodeChar(*p) != RtlUpcaseUnicodeChar(*pp))
|
|
|
|
{
|
|
|
|
/* Failed */
|
|
|
|
Found = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Next chars */
|
|
|
|
p++;
|
|
|
|
pp++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we found a name */
|
|
|
|
if (Found)
|
|
|
|
{
|
|
|
|
/* Reference it */
|
[NTOS]: Fix for the the bug that broke ARM3 paged pool (and has been corrupting ReactOS paged pool behind the scenes for years):
When a KCB (key stuff) is allocated, the key name associated with it receives an NCB (name stuff). In case this name is already used, a cache exists, and an existing NCB is grabbed, and its reference count is increased. When the KCB goes away, its NCB loses a reference. When all references are gone, the NCB is destroyed. Simple enough.
It turns out that what was currently happening is that an NCB would get dereferenced to 0, deleted, but still remained attached to a valid KCB (shouldn't happen). When that KCB went away, the NCB's reference count was dropped to... -1, and then -2, -3, -4, etc. Remember this is a FREED NCB. In other words, freed pool, that might now belong to someone else, was getting "-1" operations on it. So any value stored in that freed pool would get decremented by one. In ARM3 paged pool, because the allocator keeps a linked list, what would happen is that the FLINK pointer would be 0xE0F01234 instead of 0xE1A01234. What happened is that "0xE1A0" was treated as the reference count of the freed NCB, and it kept getting dereferenced down to 0xE0F0.
Proving this was easy, by adding an ASSERT(Ncb->RefCount >= 1) to the routine that dereferences NCBs. Obviously, we should not try to dereference an NCB that has a reference count of 0, because that NCB is now gone. Adding this ASSERT immediately caught the error, regardless of which pool implementation was being used, so this was a problem in ReactOS today, right now.
My first thought was that we were taking references to NCBs without incrementing the reference count. The NCB gets referenced in two places: when it gets created, and everytime a cached NCB is re-used for a new KCB (all this in CmpGetNameControlBlock).
After adding some tracing code, I discovered that CmpGetNameControlBlock would sometimes return an NCB that was cached, but without referencing it. I did not understand why, since the code says "if (Found) Ncb->RefCount++".
Further analysis showed that what would happen, on this particular instance, is that NCB "Foo" was being Found, but NCB "Bar" was returned instead. Therefore, causing some serious issues: First, NCB Foo was receiving too many references. Secondly, NCB Bar was not being referenced.
Worse though, it turns out this would happen when "Foo" was the CORRECT NCB, and "Bar" was an INCORRECT NCB. What do we mean by correct and incorrect? Well, because NCBs are hashed, it's possible for two NCB hashes to be VERY SIMILAR, but only ONE OF THOSE NCBs will be the right one -- for example, HKLM\Software\Hello vs HKLM\Software\Hell.
In our case, when a KCB for "Hello" was searching for the "Hello" NCB, the "Hello NCB would get a reference, but the "Hell" NCB would be returned. In other words, whenever a HASH COLLISION happened, the incorrect NCB was returned, probably messing up registry code in the process. Subsequently, when the KCB was dereferneced, it was attached to this incorrect, under-referenced NCB.
Since in ANY hash collision with "Hell", in our example, the "Hell" NCB would come first, subsequent searches for "Hellmaster", "Hellboy", "Hello World" would all still return "Hell". Eventually when all these KCBs would go away, the "Hell" NCB would reach even -18 references.
The simple solution? When the CORRECT NCB is found, STOP SEARCHING! By adding a simple "break" statement. Otherwise, even after the correct NCB is found, further, incorrect, collided NCBs are found, and eventually the last one ("Hell", in our example) got returned, and under-referenced, while "Hellmaster" and "Hellboy" were not returned, but LEAKED REFERENCES.
There you have it folks, MEMORY CORRUPTION (USE-AFTER-FREE), INCORRECT REGISTRY NAME PARSHING, REFERENCE LEAKS and REFERENCE UNDERRUNS, all due to ONE missing "break;".
-r
svn path=/trunk/; revision=47605
2010-06-06 01:04:03 +00:00
|
|
|
ASSERT(Ncb->RefCount != 0xFFFF);
|
2007-12-15 18:14:41 +00:00
|
|
|
Ncb->RefCount++;
|
[NTOS]: Fix for the the bug that broke ARM3 paged pool (and has been corrupting ReactOS paged pool behind the scenes for years):
When a KCB (key stuff) is allocated, the key name associated with it receives an NCB (name stuff). In case this name is already used, a cache exists, and an existing NCB is grabbed, and its reference count is increased. When the KCB goes away, its NCB loses a reference. When all references are gone, the NCB is destroyed. Simple enough.
It turns out that what was currently happening is that an NCB would get dereferenced to 0, deleted, but still remained attached to a valid KCB (shouldn't happen). When that KCB went away, the NCB's reference count was dropped to... -1, and then -2, -3, -4, etc. Remember this is a FREED NCB. In other words, freed pool, that might now belong to someone else, was getting "-1" operations on it. So any value stored in that freed pool would get decremented by one. In ARM3 paged pool, because the allocator keeps a linked list, what would happen is that the FLINK pointer would be 0xE0F01234 instead of 0xE1A01234. What happened is that "0xE1A0" was treated as the reference count of the freed NCB, and it kept getting dereferenced down to 0xE0F0.
Proving this was easy, by adding an ASSERT(Ncb->RefCount >= 1) to the routine that dereferences NCBs. Obviously, we should not try to dereference an NCB that has a reference count of 0, because that NCB is now gone. Adding this ASSERT immediately caught the error, regardless of which pool implementation was being used, so this was a problem in ReactOS today, right now.
My first thought was that we were taking references to NCBs without incrementing the reference count. The NCB gets referenced in two places: when it gets created, and everytime a cached NCB is re-used for a new KCB (all this in CmpGetNameControlBlock).
After adding some tracing code, I discovered that CmpGetNameControlBlock would sometimes return an NCB that was cached, but without referencing it. I did not understand why, since the code says "if (Found) Ncb->RefCount++".
Further analysis showed that what would happen, on this particular instance, is that NCB "Foo" was being Found, but NCB "Bar" was returned instead. Therefore, causing some serious issues: First, NCB Foo was receiving too many references. Secondly, NCB Bar was not being referenced.
Worse though, it turns out this would happen when "Foo" was the CORRECT NCB, and "Bar" was an INCORRECT NCB. What do we mean by correct and incorrect? Well, because NCBs are hashed, it's possible for two NCB hashes to be VERY SIMILAR, but only ONE OF THOSE NCBs will be the right one -- for example, HKLM\Software\Hello vs HKLM\Software\Hell.
In our case, when a KCB for "Hello" was searching for the "Hello" NCB, the "Hello NCB would get a reference, but the "Hell" NCB would be returned. In other words, whenever a HASH COLLISION happened, the incorrect NCB was returned, probably messing up registry code in the process. Subsequently, when the KCB was dereferneced, it was attached to this incorrect, under-referenced NCB.
Since in ANY hash collision with "Hell", in our example, the "Hell" NCB would come first, subsequent searches for "Hellmaster", "Hellboy", "Hello World" would all still return "Hell". Eventually when all these KCBs would go away, the "Hell" NCB would reach even -18 references.
The simple solution? When the CORRECT NCB is found, STOP SEARCHING! By adding a simple "break" statement. Otherwise, even after the correct NCB is found, further, incorrect, collided NCBs are found, and eventually the last one ("Hell", in our example) got returned, and under-referenced, while "Hellmaster" and "Hellboy" were not returned, but LEAKED REFERENCES.
There you have it folks, MEMORY CORRUPTION (USE-AFTER-FREE), INCORRECT REGISTRY NAME PARSHING, REFERENCE LEAKS and REFERENCE UNDERRUNS, all due to ONE missing "break;".
-r
svn path=/trunk/; revision=47605
2010-06-06 01:04:03 +00:00
|
|
|
break;
|
2007-12-15 18:14:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Go to the next hash */
|
|
|
|
HashEntry = HashEntry->NextHash;
|
|
|
|
}
|
2009-02-05 21:30:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Check if we didn't find it */
|
|
|
|
if (!Found)
|
|
|
|
{
|
|
|
|
/* Allocate one */
|
|
|
|
NcbSize = FIELD_OFFSET(CM_NAME_CONTROL_BLOCK, Name) + Length;
|
2009-08-30 19:20:30 +00:00
|
|
|
Ncb = CmpAllocate(NcbSize, TRUE, TAG_CM);
|
2007-12-15 18:14:41 +00:00
|
|
|
if (!Ncb)
|
|
|
|
{
|
|
|
|
/* Release the lock and fail */
|
|
|
|
CmpReleaseNcbLockByKey(ConvKey);
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-02-05 21:30:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Clear it out */
|
|
|
|
RtlZeroMemory(Ncb, NcbSize);
|
2009-02-05 21:30:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Check if the name was compressed */
|
|
|
|
if (IsCompressed)
|
|
|
|
{
|
|
|
|
/* Copy the compressed name */
|
2009-02-05 21:30:21 +00:00
|
|
|
for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++)
|
2007-12-15 18:14:41 +00:00
|
|
|
{
|
|
|
|
/* Copy Unicode to ANSI */
|
|
|
|
((PCHAR)Ncb->Name)[i] = (CHAR)RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Copy the name directly */
|
2009-02-05 21:30:21 +00:00
|
|
|
for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++)
|
2007-12-15 18:14:41 +00:00
|
|
|
{
|
|
|
|
/* Copy each unicode character */
|
|
|
|
Ncb->Name[i] = RtlUpcaseUnicodeChar(NodeName->Buffer[i]);
|
|
|
|
}
|
|
|
|
}
|
2009-02-05 21:30:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Setup the rest of the NCB */
|
2009-02-05 21:30:21 +00:00
|
|
|
Ncb->Compressed = IsCompressed;
|
2007-12-15 18:14:41 +00:00
|
|
|
Ncb->ConvKey = ConvKey;
|
|
|
|
Ncb->RefCount++;
|
|
|
|
Ncb->NameLength = Length;
|
2009-02-05 21:30:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Insert the name in the hash table */
|
|
|
|
HashEntry = &Ncb->NameHash;
|
2017-08-16 20:30:45 +00:00
|
|
|
HashEntry->NextHash = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey)->Entry;
|
|
|
|
GET_HASH_ENTRY(CmpNameCacheTable, ConvKey)->Entry = HashEntry;
|
2007-12-15 18:14:41 +00:00
|
|
|
}
|
2009-02-05 21:30:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Release NCB lock */
|
|
|
|
CmpReleaseNcbLockByKey(ConvKey);
|
|
|
|
|
|
|
|
/* Return the NCB found */
|
|
|
|
return Ncb;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
|
|
|
|
{
|
2017-06-17 01:22:17 +00:00
|
|
|
/* Make sure we have the exclusive lock */
|
|
|
|
CMP_ASSERT_KCB_LOCK(Kcb);
|
2007-12-15 18:14:41 +00:00
|
|
|
|
|
|
|
/* Remove the key hash */
|
|
|
|
CmpRemoveKeyHash(&Kcb->KeyHash);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
CmpDereferenceNameControlBlockWithLock(IN PCM_NAME_CONTROL_BLOCK Ncb)
|
|
|
|
{
|
|
|
|
PCM_NAME_HASH Current, *Next;
|
|
|
|
ULONG ConvKey = Ncb->ConvKey;
|
|
|
|
|
|
|
|
/* Lock the NCB */
|
|
|
|
CmpAcquireNcbLockExclusiveByKey(ConvKey);
|
|
|
|
|
|
|
|
/* Decrease the reference count */
|
[NTOS]: Fix for the the bug that broke ARM3 paged pool (and has been corrupting ReactOS paged pool behind the scenes for years):
When a KCB (key stuff) is allocated, the key name associated with it receives an NCB (name stuff). In case this name is already used, a cache exists, and an existing NCB is grabbed, and its reference count is increased. When the KCB goes away, its NCB loses a reference. When all references are gone, the NCB is destroyed. Simple enough.
It turns out that what was currently happening is that an NCB would get dereferenced to 0, deleted, but still remained attached to a valid KCB (shouldn't happen). When that KCB went away, the NCB's reference count was dropped to... -1, and then -2, -3, -4, etc. Remember this is a FREED NCB. In other words, freed pool, that might now belong to someone else, was getting "-1" operations on it. So any value stored in that freed pool would get decremented by one. In ARM3 paged pool, because the allocator keeps a linked list, what would happen is that the FLINK pointer would be 0xE0F01234 instead of 0xE1A01234. What happened is that "0xE1A0" was treated as the reference count of the freed NCB, and it kept getting dereferenced down to 0xE0F0.
Proving this was easy, by adding an ASSERT(Ncb->RefCount >= 1) to the routine that dereferences NCBs. Obviously, we should not try to dereference an NCB that has a reference count of 0, because that NCB is now gone. Adding this ASSERT immediately caught the error, regardless of which pool implementation was being used, so this was a problem in ReactOS today, right now.
My first thought was that we were taking references to NCBs without incrementing the reference count. The NCB gets referenced in two places: when it gets created, and everytime a cached NCB is re-used for a new KCB (all this in CmpGetNameControlBlock).
After adding some tracing code, I discovered that CmpGetNameControlBlock would sometimes return an NCB that was cached, but without referencing it. I did not understand why, since the code says "if (Found) Ncb->RefCount++".
Further analysis showed that what would happen, on this particular instance, is that NCB "Foo" was being Found, but NCB "Bar" was returned instead. Therefore, causing some serious issues: First, NCB Foo was receiving too many references. Secondly, NCB Bar was not being referenced.
Worse though, it turns out this would happen when "Foo" was the CORRECT NCB, and "Bar" was an INCORRECT NCB. What do we mean by correct and incorrect? Well, because NCBs are hashed, it's possible for two NCB hashes to be VERY SIMILAR, but only ONE OF THOSE NCBs will be the right one -- for example, HKLM\Software\Hello vs HKLM\Software\Hell.
In our case, when a KCB for "Hello" was searching for the "Hello" NCB, the "Hello NCB would get a reference, but the "Hell" NCB would be returned. In other words, whenever a HASH COLLISION happened, the incorrect NCB was returned, probably messing up registry code in the process. Subsequently, when the KCB was dereferneced, it was attached to this incorrect, under-referenced NCB.
Since in ANY hash collision with "Hell", in our example, the "Hell" NCB would come first, subsequent searches for "Hellmaster", "Hellboy", "Hello World" would all still return "Hell". Eventually when all these KCBs would go away, the "Hell" NCB would reach even -18 references.
The simple solution? When the CORRECT NCB is found, STOP SEARCHING! By adding a simple "break" statement. Otherwise, even after the correct NCB is found, further, incorrect, collided NCBs are found, and eventually the last one ("Hell", in our example) got returned, and under-referenced, while "Hellmaster" and "Hellboy" were not returned, but LEAKED REFERENCES.
There you have it folks, MEMORY CORRUPTION (USE-AFTER-FREE), INCORRECT REGISTRY NAME PARSHING, REFERENCE LEAKS and REFERENCE UNDERRUNS, all due to ONE missing "break;".
-r
svn path=/trunk/; revision=47605
2010-06-06 01:04:03 +00:00
|
|
|
ASSERT(Ncb->RefCount >= 1);
|
2007-12-15 18:14:41 +00:00
|
|
|
if (!(--Ncb->RefCount))
|
|
|
|
{
|
|
|
|
/* Find the NCB in the table */
|
2017-08-16 20:30:45 +00:00
|
|
|
Next = &GET_HASH_ENTRY(CmpNameCacheTable, Ncb->ConvKey)->Entry;
|
2007-12-15 18:14:41 +00:00
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
/* Check the current entry */
|
|
|
|
Current = *Next;
|
|
|
|
ASSERT(Current != NULL);
|
|
|
|
if (Current == &Ncb->NameHash)
|
|
|
|
{
|
|
|
|
/* Unlink it */
|
|
|
|
*Next = Current->NextHash;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get to the next one */
|
|
|
|
Next = &Current->NextHash;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Found it, now free it */
|
2009-08-30 19:20:30 +00:00
|
|
|
CmpFree(Ncb, 0);
|
2007-12-15 18:14:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the lock */
|
|
|
|
CmpReleaseNcbLockByKey(ConvKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
|
|
NTAPI
|
|
|
|
CmpReferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
|
|
|
|
{
|
2008-09-23 14:15:56 +00:00
|
|
|
CMTRACE(CM_REFERENCE_DEBUG,
|
|
|
|
"%s - Referencing KCB: %p\n", __FUNCTION__, Kcb);
|
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Check if this is the KCB's first reference */
|
|
|
|
if (Kcb->RefCount == 0)
|
|
|
|
{
|
|
|
|
/* Check if the KCB is locked in shared mode */
|
|
|
|
if (!CmpIsKcbLockedExclusive(Kcb))
|
|
|
|
{
|
|
|
|
/* Convert it to exclusive */
|
|
|
|
if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
|
|
|
|
{
|
|
|
|
/* Set the delayed close index so that we can be ignored */
|
|
|
|
Kcb->DelayedCloseIndex = 1;
|
|
|
|
|
|
|
|
/* Increase the reference count while we release the lock */
|
|
|
|
InterlockedIncrement((PLONG)&Kcb->RefCount);
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Go from shared to exclusive */
|
|
|
|
CmpConvertKcbSharedToExclusive(Kcb);
|
|
|
|
|
|
|
|
/* Decrement the reference count; the lock is now held again */
|
|
|
|
InterlockedDecrement((PLONG)&Kcb->RefCount);
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Check if we still control the index */
|
|
|
|
if (Kcb->DelayedCloseIndex == 1)
|
|
|
|
{
|
|
|
|
/* Reset it */
|
|
|
|
Kcb->DelayedCloseIndex = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Sanity check */
|
|
|
|
ASSERT((Kcb->DelayedCloseIndex == CmpDelayedCloseSize) ||
|
|
|
|
(Kcb->DelayedCloseIndex == 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Increase the reference count */
|
|
|
|
if ((InterlockedIncrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
|
|
|
|
{
|
|
|
|
/* We've overflown to 64K references, bail out */
|
|
|
|
InterlockedDecrement((PLONG)&Kcb->RefCount);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if this was the last close index */
|
|
|
|
if (!Kcb->DelayedCloseIndex)
|
|
|
|
{
|
|
|
|
/* Check if the KCB is locked in shared mode */
|
|
|
|
if (!CmpIsKcbLockedExclusive(Kcb))
|
|
|
|
{
|
|
|
|
/* Convert it to exclusive */
|
|
|
|
if (!CmpTryToConvertKcbSharedToExclusive(Kcb))
|
|
|
|
{
|
|
|
|
/* Go from shared to exclusive */
|
|
|
|
CmpConvertKcbSharedToExclusive(Kcb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we're still the last entry, remove us */
|
|
|
|
if (!Kcb->DelayedCloseIndex) CmpRemoveFromDelayedClose(Kcb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return success */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb)
|
|
|
|
{
|
|
|
|
PULONG_PTR CachedList;
|
|
|
|
ULONG i;
|
|
|
|
|
2017-06-17 01:22:17 +00:00
|
|
|
/* Make sure we have the exclusive lock */
|
|
|
|
CMP_ASSERT_KCB_LOCK(Kcb);
|
2007-12-15 18:14:41 +00:00
|
|
|
|
|
|
|
/* Check if the value list is cached */
|
|
|
|
if (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList))
|
|
|
|
{
|
|
|
|
/* Get the cache list */
|
|
|
|
CachedList = (PULONG_PTR)CMP_GET_CACHED_DATA(Kcb->ValueCache.ValueList);
|
|
|
|
for (i = 0; i < Kcb->ValueCache.Count; i++)
|
|
|
|
{
|
|
|
|
/* Check if this cell is cached */
|
|
|
|
if (CMP_IS_CELL_CACHED(CachedList[i]))
|
|
|
|
{
|
|
|
|
/* Free it */
|
2009-08-30 19:20:30 +00:00
|
|
|
CmpFree((PVOID)CMP_GET_CACHED_CELL(CachedList[i]), 0);
|
2007-12-15 18:14:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now free the list */
|
2009-08-30 19:20:30 +00:00
|
|
|
CmpFree((PVOID)CMP_GET_CACHED_CELL(Kcb->ValueCache.ValueList), 0);
|
2007-12-15 18:14:41 +00:00
|
|
|
Kcb->ValueCache.ValueList = HCELL_NIL;
|
|
|
|
}
|
|
|
|
else if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
|
|
|
|
{
|
|
|
|
/* This is a sym link, check if there's only one reference left */
|
2007-12-15 20:51:40 +00:00
|
|
|
if ((Kcb->ValueCache.RealKcb->RefCount == 1) &&
|
|
|
|
!(Kcb->ValueCache.RealKcb->Delete))
|
2007-12-15 18:14:41 +00:00
|
|
|
{
|
|
|
|
/* Disable delay close for the KCB */
|
2007-12-15 20:51:40 +00:00
|
|
|
Kcb->ValueCache.RealKcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
|
2007-12-15 18:14:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Dereference the KCB */
|
2007-12-15 20:51:40 +00:00
|
|
|
CmpDelayDerefKeyControlBlock(Kcb->ValueCache.RealKcb);
|
2007-12-15 18:14:41 +00:00
|
|
|
Kcb->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
|
|
|
|
IN BOOLEAN LockHeldExclusively)
|
|
|
|
{
|
|
|
|
PCM_KEY_CONTROL_BLOCK Parent;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
|
|
/* Sanity checks */
|
2017-06-17 01:22:17 +00:00
|
|
|
CMP_ASSERT_KCB_LOCK(Kcb);
|
2007-12-15 18:14:41 +00:00
|
|
|
ASSERT(Kcb->RefCount == 0);
|
|
|
|
|
|
|
|
/* Cleanup the value cache */
|
|
|
|
CmpCleanUpKcbValueCache(Kcb);
|
|
|
|
|
2008-08-31 16:24:29 +00:00
|
|
|
/* Dereference the NCB */
|
2007-12-15 18:14:41 +00:00
|
|
|
CmpDereferenceNameControlBlockWithLock(Kcb->NameBlock);
|
|
|
|
|
|
|
|
/* Check if we have an index hint block and free it */
|
2009-08-30 19:20:30 +00:00
|
|
|
if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) CmpFree(Kcb->IndexHint, 0);
|
2007-12-15 18:14:41 +00:00
|
|
|
|
|
|
|
/* Check if we were already deleted */
|
|
|
|
Parent = Kcb->ParentKcb;
|
|
|
|
if (!Kcb->Delete) CmpRemoveKeyControlBlock(Kcb);
|
2008-08-31 16:24:29 +00:00
|
|
|
|
2007-12-16 16:37:15 +00:00
|
|
|
/* Set invalid KCB signature */
|
|
|
|
Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
|
2008-08-31 16:24:29 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Free the KCB as well */
|
|
|
|
CmpFreeKeyControlBlock(Kcb);
|
|
|
|
|
|
|
|
/* Check if we have a parent */
|
|
|
|
if (Parent)
|
|
|
|
{
|
|
|
|
/* Dereference the parent */
|
|
|
|
LockHeldExclusively ?
|
2008-08-31 16:24:29 +00:00
|
|
|
CmpDereferenceKeyControlBlockWithLock(Parent,LockHeldExclusively) :
|
|
|
|
CmpDelayDerefKeyControlBlock(Parent);
|
2007-12-15 18:14:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-15 20:51:40 +00:00
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb)
|
|
|
|
{
|
|
|
|
PCM_KEY_NODE KeyNode;
|
[HAL/NDK]
- Make Vector parameter in HalEnableSystemInterrupt, HalDisableSystemInterrupt and HalBeginSystemInterrupt an ULONG, not an UCHAR
[NDK]
- 64bit fixes for HANDLE_TABLE, KPROCESS, SECTION_IMAGE_INFORMATION, MMADDRESS_LIST, MMVAD_FLAGS, MMVAD, MMVAD_LONG, MMVAD_SHORT, MEMORY_DESCRIPTOR, MEMORY_ALLOCATION_DESCRIPTOR, LdrVerifyMappedImageMatchesChecksum
- KDPC_DATA::DpcQueueDepth is signed on amd64, unsigned on x86
[NTOSKRNL]
- Fix hundreds of MSVC and amd64 warnings
- add a pragma message to FstubFixupEfiPartition, since it looks broken
- Move portable Ke constants from <arch>/cpu.c to krnlinit.c
- Fixed a bug in amd64 KiGeneralProtectionFaultHandler
svn path=/trunk/; revision=53734
2011-09-18 13:11:45 +00:00
|
|
|
|
2017-06-17 01:22:17 +00:00
|
|
|
/* Make sure we have the exclusive lock */
|
|
|
|
CMP_ASSERT_KCB_LOCK(Kcb);
|
2007-12-15 20:51:40 +00:00
|
|
|
|
|
|
|
/* Check if there's any cached subkey */
|
|
|
|
if (Kcb->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT))
|
|
|
|
{
|
|
|
|
/* Check if there's a hint */
|
|
|
|
if (Kcb->ExtFlags & (CM_KCB_SUBKEY_HINT))
|
|
|
|
{
|
|
|
|
/* Kill it */
|
2009-08-30 19:20:30 +00:00
|
|
|
CmpFree(Kcb->IndexHint, 0);
|
2007-12-15 20:51:40 +00:00
|
|
|
}
|
[HAL/NDK]
- Make Vector parameter in HalEnableSystemInterrupt, HalDisableSystemInterrupt and HalBeginSystemInterrupt an ULONG, not an UCHAR
[NDK]
- 64bit fixes for HANDLE_TABLE, KPROCESS, SECTION_IMAGE_INFORMATION, MMADDRESS_LIST, MMVAD_FLAGS, MMVAD, MMVAD_LONG, MMVAD_SHORT, MEMORY_DESCRIPTOR, MEMORY_ALLOCATION_DESCRIPTOR, LdrVerifyMappedImageMatchesChecksum
- KDPC_DATA::DpcQueueDepth is signed on amd64, unsigned on x86
[NTOSKRNL]
- Fix hundreds of MSVC and amd64 warnings
- add a pragma message to FstubFixupEfiPartition, since it looks broken
- Move portable Ke constants from <arch>/cpu.c to krnlinit.c
- Fixed a bug in amd64 KiGeneralProtectionFaultHandler
svn path=/trunk/; revision=53734
2011-09-18 13:11:45 +00:00
|
|
|
|
2007-12-15 20:51:40 +00:00
|
|
|
/* Remove subkey flags */
|
|
|
|
Kcb->ExtFlags &= ~(CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if there's no linked cell */
|
2009-02-05 21:30:21 +00:00
|
|
|
if (Kcb->KeyCell == HCELL_NIL)
|
2007-12-15 20:51:40 +00:00
|
|
|
{
|
|
|
|
/* Make sure it's a delete */
|
2009-02-05 21:30:21 +00:00
|
|
|
ASSERT(Kcb->Delete);
|
|
|
|
KeyNode = NULL;
|
|
|
|
}
|
2007-12-15 20:51:40 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Get the key node */
|
2009-02-05 21:30:21 +00:00
|
|
|
KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell);
|
|
|
|
}
|
|
|
|
|
2007-12-15 20:51:40 +00:00
|
|
|
/* Check if we got the node */
|
|
|
|
if (!KeyNode)
|
|
|
|
{
|
|
|
|
/* We didn't, mark the cached data invalid */
|
|
|
|
Kcb->ExtFlags |= CM_KCB_INVALID_CACHED_INFO;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* We have a keynode, update subkey counts */
|
|
|
|
Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
|
|
|
|
Kcb->SubKeyCount = KeyNode->SubKeyCounts[Stable] +
|
|
|
|
KeyNode->SubKeyCounts[Volatile];
|
2009-02-05 21:30:21 +00:00
|
|
|
|
2007-12-15 20:51:40 +00:00
|
|
|
/* Release the cell */
|
|
|
|
HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
|
|
|
|
{
|
|
|
|
LONG OldRefCount, NewRefCount;
|
|
|
|
ULONG ConvKey;
|
2008-09-23 14:15:56 +00:00
|
|
|
CMTRACE(CM_REFERENCE_DEBUG,
|
|
|
|
"%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb);
|
2007-12-15 18:14:41 +00:00
|
|
|
|
|
|
|
/* Get the ref count and update it */
|
|
|
|
OldRefCount = *(PLONG)&Kcb->RefCount;
|
|
|
|
NewRefCount = OldRefCount - 1;
|
[HAL/NDK]
- Make Vector parameter in HalEnableSystemInterrupt, HalDisableSystemInterrupt and HalBeginSystemInterrupt an ULONG, not an UCHAR
[NDK]
- 64bit fixes for HANDLE_TABLE, KPROCESS, SECTION_IMAGE_INFORMATION, MMADDRESS_LIST, MMVAD_FLAGS, MMVAD, MMVAD_LONG, MMVAD_SHORT, MEMORY_DESCRIPTOR, MEMORY_ALLOCATION_DESCRIPTOR, LdrVerifyMappedImageMatchesChecksum
- KDPC_DATA::DpcQueueDepth is signed on amd64, unsigned on x86
[NTOSKRNL]
- Fix hundreds of MSVC and amd64 warnings
- add a pragma message to FstubFixupEfiPartition, since it looks broken
- Move portable Ke constants from <arch>/cpu.c to krnlinit.c
- Fixed a bug in amd64 KiGeneralProtectionFaultHandler
svn path=/trunk/; revision=53734
2011-09-18 13:11:45 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Check if we still have references */
|
[NTOS]: Fix for the the bug that broke ARM3 paged pool (and has been corrupting ReactOS paged pool behind the scenes for years):
When a KCB (key stuff) is allocated, the key name associated with it receives an NCB (name stuff). In case this name is already used, a cache exists, and an existing NCB is grabbed, and its reference count is increased. When the KCB goes away, its NCB loses a reference. When all references are gone, the NCB is destroyed. Simple enough.
It turns out that what was currently happening is that an NCB would get dereferenced to 0, deleted, but still remained attached to a valid KCB (shouldn't happen). When that KCB went away, the NCB's reference count was dropped to... -1, and then -2, -3, -4, etc. Remember this is a FREED NCB. In other words, freed pool, that might now belong to someone else, was getting "-1" operations on it. So any value stored in that freed pool would get decremented by one. In ARM3 paged pool, because the allocator keeps a linked list, what would happen is that the FLINK pointer would be 0xE0F01234 instead of 0xE1A01234. What happened is that "0xE1A0" was treated as the reference count of the freed NCB, and it kept getting dereferenced down to 0xE0F0.
Proving this was easy, by adding an ASSERT(Ncb->RefCount >= 1) to the routine that dereferences NCBs. Obviously, we should not try to dereference an NCB that has a reference count of 0, because that NCB is now gone. Adding this ASSERT immediately caught the error, regardless of which pool implementation was being used, so this was a problem in ReactOS today, right now.
My first thought was that we were taking references to NCBs without incrementing the reference count. The NCB gets referenced in two places: when it gets created, and everytime a cached NCB is re-used for a new KCB (all this in CmpGetNameControlBlock).
After adding some tracing code, I discovered that CmpGetNameControlBlock would sometimes return an NCB that was cached, but without referencing it. I did not understand why, since the code says "if (Found) Ncb->RefCount++".
Further analysis showed that what would happen, on this particular instance, is that NCB "Foo" was being Found, but NCB "Bar" was returned instead. Therefore, causing some serious issues: First, NCB Foo was receiving too many references. Secondly, NCB Bar was not being referenced.
Worse though, it turns out this would happen when "Foo" was the CORRECT NCB, and "Bar" was an INCORRECT NCB. What do we mean by correct and incorrect? Well, because NCBs are hashed, it's possible for two NCB hashes to be VERY SIMILAR, but only ONE OF THOSE NCBs will be the right one -- for example, HKLM\Software\Hello vs HKLM\Software\Hell.
In our case, when a KCB for "Hello" was searching for the "Hello" NCB, the "Hello NCB would get a reference, but the "Hell" NCB would be returned. In other words, whenever a HASH COLLISION happened, the incorrect NCB was returned, probably messing up registry code in the process. Subsequently, when the KCB was dereferneced, it was attached to this incorrect, under-referenced NCB.
Since in ANY hash collision with "Hell", in our example, the "Hell" NCB would come first, subsequent searches for "Hellmaster", "Hellboy", "Hello World" would all still return "Hell". Eventually when all these KCBs would go away, the "Hell" NCB would reach even -18 references.
The simple solution? When the CORRECT NCB is found, STOP SEARCHING! By adding a simple "break" statement. Otherwise, even after the correct NCB is found, further, incorrect, collided NCBs are found, and eventually the last one ("Hell", in our example) got returned, and under-referenced, while "Hellmaster" and "Hellboy" were not returned, but LEAKED REFERENCES.
There you have it folks, MEMORY CORRUPTION (USE-AFTER-FREE), INCORRECT REGISTRY NAME PARSHING, REFERENCE LEAKS and REFERENCE UNDERRUNS, all due to ONE missing "break;".
-r
svn path=/trunk/; revision=47605
2010-06-06 01:04:03 +00:00
|
|
|
if ((NewRefCount & 0xFFFF) > 0)
|
2007-12-15 18:14:41 +00:00
|
|
|
{
|
|
|
|
/* Do the dereference */
|
|
|
|
if (InterlockedCompareExchange((PLONG)&Kcb->RefCount,
|
|
|
|
NewRefCount,
|
|
|
|
OldRefCount) == OldRefCount)
|
|
|
|
{
|
|
|
|
/* We'de done */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
[HAL/NDK]
- Make Vector parameter in HalEnableSystemInterrupt, HalDisableSystemInterrupt and HalBeginSystemInterrupt an ULONG, not an UCHAR
[NDK]
- 64bit fixes for HANDLE_TABLE, KPROCESS, SECTION_IMAGE_INFORMATION, MMADDRESS_LIST, MMVAD_FLAGS, MMVAD, MMVAD_LONG, MMVAD_SHORT, MEMORY_DESCRIPTOR, MEMORY_ALLOCATION_DESCRIPTOR, LdrVerifyMappedImageMatchesChecksum
- KDPC_DATA::DpcQueueDepth is signed on amd64, unsigned on x86
[NTOSKRNL]
- Fix hundreds of MSVC and amd64 warnings
- add a pragma message to FstubFixupEfiPartition, since it looks broken
- Move portable Ke constants from <arch>/cpu.c to krnlinit.c
- Fixed a bug in amd64 KiGeneralProtectionFaultHandler
svn path=/trunk/; revision=53734
2011-09-18 13:11:45 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Save the key */
|
|
|
|
ConvKey = Kcb->ConvKey;
|
|
|
|
|
|
|
|
/* Do the dereference inside the lock */
|
|
|
|
CmpAcquireKcbLockExclusive(Kcb);
|
|
|
|
CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE);
|
|
|
|
CmpReleaseKcbLockByKey(ConvKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb,
|
|
|
|
IN BOOLEAN LockHeldExclusively)
|
|
|
|
{
|
2008-09-23 14:15:56 +00:00
|
|
|
CMTRACE(CM_REFERENCE_DEBUG,
|
|
|
|
"%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb);
|
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Sanity check */
|
2007-12-16 16:37:15 +00:00
|
|
|
ASSERT_KCB_VALID(Kcb);
|
2007-12-15 18:14:41 +00:00
|
|
|
|
|
|
|
/* Check if this is the last reference */
|
|
|
|
if ((InterlockedDecrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0)
|
|
|
|
{
|
2017-06-17 01:22:17 +00:00
|
|
|
/* Make sure we have the exclusive lock */
|
|
|
|
CMP_ASSERT_KCB_LOCK(Kcb);
|
2007-12-16 16:37:15 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Check if we should do a direct delete */
|
|
|
|
if (((CmpHoldLazyFlush) &&
|
|
|
|
!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) &&
|
|
|
|
!(Kcb->Flags & KEY_SYM_LINK)) ||
|
|
|
|
(Kcb->ExtFlags & CM_KCB_NO_DELAY_CLOSE) ||
|
|
|
|
(Kcb->Delete))
|
|
|
|
{
|
|
|
|
/* Clean up the KCB*/
|
|
|
|
CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Otherwise, use delayed close */
|
|
|
|
CmpAddToDelayedClose(Kcb, LockHeldExclusively);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
InitializeKCBKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb)
|
|
|
|
{
|
|
|
|
/* Initialize the list */
|
|
|
|
InitializeListHead(&Kcb->KeyBodyListHead);
|
|
|
|
|
|
|
|
/* Clear the bodies */
|
|
|
|
Kcb->KeyBodyArray[0] =
|
|
|
|
Kcb->KeyBodyArray[1] =
|
|
|
|
Kcb->KeyBodyArray[2] =
|
|
|
|
Kcb->KeyBodyArray[3] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PCM_KEY_CONTROL_BLOCK
|
|
|
|
NTAPI
|
|
|
|
CmpCreateKeyControlBlock(IN PHHIVE Hive,
|
|
|
|
IN HCELL_INDEX Index,
|
|
|
|
IN PCM_KEY_NODE Node,
|
|
|
|
IN PCM_KEY_CONTROL_BLOCK Parent,
|
|
|
|
IN ULONG Flags,
|
|
|
|
IN PUNICODE_STRING KeyName)
|
|
|
|
{
|
|
|
|
PCM_KEY_CONTROL_BLOCK Kcb, FoundKcb = NULL;
|
|
|
|
UNICODE_STRING NodeName;
|
|
|
|
ULONG ConvKey = 0, i;
|
|
|
|
BOOLEAN IsFake, HashLock;
|
|
|
|
PWCHAR p;
|
|
|
|
|
|
|
|
/* Make sure we own this hive in case it's being unloaded */
|
|
|
|
if ((Hive->HiveFlags & HIVE_IS_UNLOADING) &&
|
|
|
|
(((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread()))
|
|
|
|
{
|
|
|
|
/* Fail */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if this is a fake KCB */
|
|
|
|
IsFake = Flags & CMP_CREATE_FAKE_KCB ? TRUE : FALSE;
|
|
|
|
|
|
|
|
/* If we have a parent, use its ConvKey */
|
|
|
|
if (Parent) ConvKey = Parent->ConvKey;
|
|
|
|
|
|
|
|
/* Make a copy of the name */
|
|
|
|
NodeName = *KeyName;
|
|
|
|
|
|
|
|
/* Remove leading slash */
|
|
|
|
while ((NodeName.Length) && (*NodeName.Buffer == OBJ_NAME_PATH_SEPARATOR))
|
|
|
|
{
|
|
|
|
/* Move the buffer by one */
|
|
|
|
NodeName.Buffer++;
|
|
|
|
NodeName.Length -= sizeof(WCHAR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure we didn't get just a slash or something */
|
|
|
|
ASSERT(NodeName.Length > 0);
|
|
|
|
|
|
|
|
/* Now setup the hash */
|
|
|
|
p = NodeName.Buffer;
|
|
|
|
for (i = 0; i < NodeName.Length; i += sizeof(WCHAR))
|
|
|
|
{
|
|
|
|
/* Make sure it's a valid character */
|
|
|
|
if (*p != OBJ_NAME_PATH_SEPARATOR)
|
|
|
|
{
|
|
|
|
/* Add this key to the hash */
|
|
|
|
ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move on */
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate the KCB */
|
|
|
|
Kcb = CmpAllocateKeyControlBlock();
|
|
|
|
if (!Kcb) return NULL;
|
|
|
|
|
|
|
|
/* Initailize the key list */
|
|
|
|
InitializeKCBKeyBodyList(Kcb);
|
|
|
|
|
|
|
|
/* Set it up */
|
2007-12-16 16:37:15 +00:00
|
|
|
Kcb->Signature = CM_KCB_SIGNATURE;
|
2007-12-15 18:14:41 +00:00
|
|
|
Kcb->Delete = FALSE;
|
|
|
|
Kcb->RefCount = 1;
|
|
|
|
Kcb->KeyHive = Hive;
|
|
|
|
Kcb->KeyCell = Index;
|
|
|
|
Kcb->ConvKey = ConvKey;
|
|
|
|
Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
|
|
|
|
Kcb->InDelayClose = 0;
|
2007-12-16 16:37:15 +00:00
|
|
|
ASSERT_KCB_VALID(Kcb);
|
2007-12-15 18:14:41 +00:00
|
|
|
|
|
|
|
/* Check if we have two hash entires */
|
|
|
|
HashLock = Flags & CMP_LOCK_HASHES_FOR_KCB ? TRUE : FALSE;
|
|
|
|
if (!HashLock)
|
|
|
|
{
|
|
|
|
/* It's not locked, do we have a parent? */
|
|
|
|
if (Parent)
|
|
|
|
{
|
|
|
|
/* Lock the parent KCB and ourselves */
|
|
|
|
CmpAcquireTwoKcbLocksExclusiveByKey(ConvKey, Parent->ConvKey);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Lock only ourselves */
|
|
|
|
CmpAcquireKcbLockExclusive(Kcb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we already have a KCB */
|
|
|
|
FoundKcb = CmpInsertKeyHash(&Kcb->KeyHash, IsFake);
|
|
|
|
if (FoundKcb)
|
|
|
|
{
|
|
|
|
/* Sanity check */
|
|
|
|
ASSERT(!FoundKcb->Delete);
|
2007-12-16 16:37:15 +00:00
|
|
|
Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
|
2007-12-15 18:14:41 +00:00
|
|
|
|
|
|
|
/* Free the one we allocated and reference this one */
|
|
|
|
CmpFreeKeyControlBlock(Kcb);
|
2007-12-16 16:37:15 +00:00
|
|
|
ASSERT_KCB_VALID(FoundKcb);
|
2007-12-15 18:14:41 +00:00
|
|
|
Kcb = FoundKcb;
|
|
|
|
if (!CmpReferenceKeyControlBlock(Kcb))
|
|
|
|
{
|
|
|
|
/* We got too many handles */
|
|
|
|
ASSERT(Kcb->RefCount + 1 != 0);
|
|
|
|
Kcb = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Check if we're not creating a fake one, but it used to be fake */
|
|
|
|
if ((Kcb->ExtFlags & CM_KCB_KEY_NON_EXIST) && !(IsFake))
|
|
|
|
{
|
|
|
|
/* Set the hive and cell */
|
|
|
|
Kcb->KeyHive = Hive;
|
|
|
|
Kcb->KeyCell = Index;
|
|
|
|
|
|
|
|
/* This means that our current information is invalid */
|
|
|
|
Kcb->ExtFlags = CM_KCB_INVALID_CACHED_INFO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we didn't have any valid data */
|
|
|
|
if (!(Kcb->ExtFlags & (CM_KCB_NO_SUBKEY |
|
|
|
|
CM_KCB_SUBKEY_ONE |
|
|
|
|
CM_KCB_SUBKEY_HINT)))
|
|
|
|
{
|
|
|
|
/* Calculate the index hint */
|
|
|
|
Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
|
|
|
|
Node->SubKeyCounts[Volatile];
|
|
|
|
|
|
|
|
/* Cached information is now valid */
|
|
|
|
Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup the other data */
|
|
|
|
Kcb->KcbLastWriteTime = Node->LastWriteTime;
|
|
|
|
Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
|
|
|
|
Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
|
|
|
|
Kcb->KcbMaxValueDataLen = Node->MaxValueDataLen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* No KCB, do we have a parent? */
|
|
|
|
if (Parent)
|
|
|
|
{
|
|
|
|
/* Reference the parent */
|
|
|
|
if (((Parent->TotalLevels + 1) < 512) &&
|
|
|
|
(CmpReferenceKeyControlBlock(Parent)))
|
|
|
|
{
|
|
|
|
/* Link it */
|
|
|
|
Kcb->ParentKcb = Parent;
|
|
|
|
Kcb->TotalLevels = Parent->TotalLevels + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Remove the KCB and free it */
|
|
|
|
CmpRemoveKeyControlBlock(Kcb);
|
2007-12-16 16:37:15 +00:00
|
|
|
Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
|
2007-12-15 18:14:41 +00:00
|
|
|
CmpFreeKeyControlBlock(Kcb);
|
|
|
|
Kcb = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* No parent, this is the root node */
|
|
|
|
Kcb->ParentKcb = NULL;
|
|
|
|
Kcb->TotalLevels = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we have a KCB */
|
|
|
|
if (Kcb)
|
|
|
|
{
|
|
|
|
/* Get the NCB */
|
|
|
|
Kcb->NameBlock = CmpGetNameControlBlock(&NodeName);
|
|
|
|
if (Kcb->NameBlock)
|
|
|
|
{
|
|
|
|
/* Fill it out */
|
|
|
|
Kcb->ValueCache.Count = Node->ValueList.Count;
|
|
|
|
Kcb->ValueCache.ValueList = Node->ValueList.List;
|
|
|
|
Kcb->Flags = Node->Flags;
|
|
|
|
Kcb->ExtFlags = 0;
|
|
|
|
Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
|
|
|
|
|
|
|
|
/* Remember if this is a fake key */
|
|
|
|
if (IsFake) Kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST;
|
|
|
|
|
|
|
|
/* Setup the other data */
|
|
|
|
Kcb->SubKeyCount = Node->SubKeyCounts[Stable] +
|
|
|
|
Node->SubKeyCounts[Volatile];
|
|
|
|
Kcb->KcbLastWriteTime = Node->LastWriteTime;
|
|
|
|
Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen;
|
|
|
|
Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen;
|
|
|
|
Kcb->KcbMaxValueDataLen = (USHORT)Node->MaxValueDataLen;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Dereference the KCB */
|
|
|
|
CmpDereferenceKeyControlBlockWithLock(Parent, FALSE);
|
|
|
|
|
|
|
|
/* Remove the KCB and free it */
|
|
|
|
CmpRemoveKeyControlBlock(Kcb);
|
2007-12-16 16:37:15 +00:00
|
|
|
Kcb->Signature = CM_KCB_INVALID_SIGNATURE;
|
2007-12-15 18:14:41 +00:00
|
|
|
CmpFreeKeyControlBlock(Kcb);
|
|
|
|
Kcb = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-16 16:37:15 +00:00
|
|
|
/* Check if this is a KCB inside a frozen hive */
|
2009-02-05 21:30:21 +00:00
|
|
|
if ((Kcb) && (((PCMHIVE)Hive)->Frozen) && (!(Kcb->Flags & KEY_SYM_LINK)))
|
2007-12-16 16:37:15 +00:00
|
|
|
{
|
|
|
|
/* Don't add these to the delay close */
|
2009-02-05 21:30:21 +00:00
|
|
|
Kcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
|
2007-12-16 16:37:15 +00:00
|
|
|
}
|
2009-02-05 21:30:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Sanity check */
|
|
|
|
ASSERT((!Kcb) || (Kcb->Delete == FALSE));
|
|
|
|
|
|
|
|
/* Check if we had locked the hashes */
|
|
|
|
if (!HashLock)
|
|
|
|
{
|
|
|
|
/* We locked them manually, do we have a parent? */
|
|
|
|
if (Parent)
|
|
|
|
{
|
|
|
|
/* Unlock the parent KCB and ourselves */
|
|
|
|
CmpReleaseTwoKcbLockByKey(ConvKey, Parent->ConvKey);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Unlock only ourselves */
|
|
|
|
CmpReleaseKcbLockByKey(ConvKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the KCB */
|
|
|
|
return Kcb;
|
|
|
|
}
|
|
|
|
|
2008-08-27 15:55:32 +00:00
|
|
|
PUNICODE_STRING
|
|
|
|
NTAPI
|
|
|
|
CmpConstructName(IN PCM_KEY_CONTROL_BLOCK Kcb)
|
|
|
|
{
|
2008-08-28 10:16:48 +00:00
|
|
|
PUNICODE_STRING KeyName;
|
[HAL/NDK]
- Make Vector parameter in HalEnableSystemInterrupt, HalDisableSystemInterrupt and HalBeginSystemInterrupt an ULONG, not an UCHAR
[NDK]
- 64bit fixes for HANDLE_TABLE, KPROCESS, SECTION_IMAGE_INFORMATION, MMADDRESS_LIST, MMVAD_FLAGS, MMVAD, MMVAD_LONG, MMVAD_SHORT, MEMORY_DESCRIPTOR, MEMORY_ALLOCATION_DESCRIPTOR, LdrVerifyMappedImageMatchesChecksum
- KDPC_DATA::DpcQueueDepth is signed on amd64, unsigned on x86
[NTOSKRNL]
- Fix hundreds of MSVC and amd64 warnings
- add a pragma message to FstubFixupEfiPartition, since it looks broken
- Move portable Ke constants from <arch>/cpu.c to krnlinit.c
- Fixed a bug in amd64 KiGeneralProtectionFaultHandler
svn path=/trunk/; revision=53734
2011-09-18 13:11:45 +00:00
|
|
|
ULONG i;
|
|
|
|
USHORT NameLength;
|
2008-08-28 10:16:48 +00:00
|
|
|
PCM_KEY_CONTROL_BLOCK MyKcb;
|
|
|
|
PCM_KEY_NODE KeyNode;
|
|
|
|
BOOLEAN DeletedKey = FALSE;
|
|
|
|
PWCHAR TargetBuffer, CurrentNameW;
|
|
|
|
PUCHAR CurrentName;
|
|
|
|
|
2008-08-28 15:37:57 +00:00
|
|
|
/* Calculate how much size our key name is going to occupy */
|
2008-08-28 10:16:48 +00:00
|
|
|
NameLength = 0;
|
|
|
|
MyKcb = Kcb;
|
|
|
|
|
|
|
|
while (MyKcb)
|
|
|
|
{
|
|
|
|
/* Add length of the name */
|
|
|
|
if (!MyKcb->NameBlock->Compressed)
|
2008-08-28 15:37:57 +00:00
|
|
|
{
|
2008-08-28 10:16:48 +00:00
|
|
|
NameLength += MyKcb->NameBlock->NameLength;
|
2008-08-28 15:37:57 +00:00
|
|
|
}
|
2008-08-28 10:16:48 +00:00
|
|
|
else
|
2008-08-28 15:37:57 +00:00
|
|
|
{
|
|
|
|
NameLength += CmpCompressedNameSize(MyKcb->NameBlock->Name,
|
|
|
|
MyKcb->NameBlock->NameLength);
|
|
|
|
}
|
2008-08-28 10:16:48 +00:00
|
|
|
|
2008-08-28 15:37:57 +00:00
|
|
|
/* Sum up the separator too */
|
2008-08-28 10:16:48 +00:00
|
|
|
NameLength += sizeof(WCHAR);
|
|
|
|
|
|
|
|
/* Go to the parent KCB */
|
|
|
|
MyKcb = MyKcb->ParentKcb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate the unicode string now */
|
2009-08-30 19:20:30 +00:00
|
|
|
KeyName = CmpAllocate(NameLength + sizeof(UNICODE_STRING),
|
|
|
|
TRUE,
|
|
|
|
TAG_CM);
|
2008-08-28 10:16:48 +00:00
|
|
|
|
|
|
|
if (!KeyName) return NULL;
|
|
|
|
|
2008-08-28 15:37:57 +00:00
|
|
|
/* Set it up */
|
2008-08-28 10:16:48 +00:00
|
|
|
KeyName->Buffer = (PWSTR)(KeyName + 1);
|
|
|
|
KeyName->Length = NameLength;
|
|
|
|
KeyName->MaximumLength = NameLength;
|
|
|
|
|
|
|
|
/* Loop the keys again, now adding names */
|
|
|
|
NameLength = 0;
|
|
|
|
MyKcb = Kcb;
|
|
|
|
|
|
|
|
while (MyKcb)
|
|
|
|
{
|
2008-08-28 15:37:57 +00:00
|
|
|
/* Sanity checks for deleted and fake keys */
|
2008-08-28 10:16:48 +00:00
|
|
|
if ((!MyKcb->KeyCell && !MyKcb->Delete) ||
|
|
|
|
!MyKcb->KeyHive ||
|
|
|
|
MyKcb->ExtFlags & CM_KCB_KEY_NON_EXIST)
|
|
|
|
{
|
|
|
|
/* Failure */
|
2009-08-30 19:20:30 +00:00
|
|
|
CmpFree(KeyName, 0);
|
2008-08-28 10:16:48 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try to get the name from the keynode,
|
|
|
|
if the key is not deleted */
|
|
|
|
if (!DeletedKey && !MyKcb->Delete)
|
|
|
|
{
|
|
|
|
KeyNode = HvGetCell(MyKcb->KeyHive, MyKcb->KeyCell);
|
|
|
|
|
|
|
|
if (!KeyNode)
|
|
|
|
{
|
|
|
|
/* Failure */
|
2009-08-30 19:20:30 +00:00
|
|
|
CmpFree(KeyName, 0);
|
2008-08-28 10:16:48 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* The key was deleted */
|
|
|
|
KeyNode = NULL;
|
|
|
|
DeletedKey = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the pointer to the beginning of the current key name */
|
|
|
|
NameLength += (MyKcb->NameBlock->NameLength + 1) * sizeof(WCHAR);
|
|
|
|
TargetBuffer = &KeyName->Buffer[(KeyName->Length - NameLength) / sizeof(WCHAR)];
|
|
|
|
|
|
|
|
/* Add a separator */
|
|
|
|
TargetBuffer[0] = OBJ_NAME_PATH_SEPARATOR;
|
|
|
|
|
|
|
|
/* Add the name, but remember to go from the end to the beginning */
|
|
|
|
if (!MyKcb->NameBlock->Compressed)
|
|
|
|
{
|
|
|
|
/* Get the pointer to the name (from the keynode, if possible) */
|
|
|
|
if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ||
|
|
|
|
!KeyNode)
|
|
|
|
{
|
|
|
|
CurrentNameW = MyKcb->NameBlock->Name;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CurrentNameW = KeyNode->Name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy the name */
|
|
|
|
for (i=0; i < MyKcb->NameBlock->NameLength; i++)
|
|
|
|
{
|
|
|
|
TargetBuffer[i+1] = *CurrentNameW;
|
|
|
|
CurrentNameW++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Get the pointer to the name (from the keynode, if possible) */
|
|
|
|
if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) ||
|
|
|
|
!KeyNode)
|
|
|
|
{
|
|
|
|
CurrentName = (PUCHAR)MyKcb->NameBlock->Name;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CurrentName = (PUCHAR)KeyNode->Name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy the name */
|
|
|
|
for (i=0; i < MyKcb->NameBlock->NameLength; i++)
|
|
|
|
{
|
|
|
|
TargetBuffer[i+1] = (WCHAR)*CurrentName;
|
|
|
|
CurrentName++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release the cell, if needed */
|
|
|
|
if (KeyNode) HvReleaseCell(MyKcb->KeyHive, MyKcb->KeyCell);
|
|
|
|
|
|
|
|
/* Go to the parent KCB */
|
|
|
|
MyKcb = MyKcb->ParentKcb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return resulting buffer (both UNICODE_STRING and
|
|
|
|
its buffer following it) */
|
|
|
|
return KeyName;
|
2008-08-27 15:55:32 +00:00
|
|
|
}
|
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody,
|
|
|
|
IN ULONG Flags)
|
|
|
|
{
|
2007-12-16 16:37:15 +00:00
|
|
|
ULONG i;
|
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Sanity check */
|
|
|
|
ASSERT(KeyBody->KeyControlBlock != NULL);
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-15 18:14:41 +00:00
|
|
|
/* Initialize the list entry */
|
|
|
|
InitializeListHead(&KeyBody->KeyBodyList);
|
|
|
|
|
2007-12-16 16:37:15 +00:00
|
|
|
/* Check if we can use the parent KCB array */
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
/* Add it into the list */
|
2010-01-13 22:35:43 +00:00
|
|
|
if (!InterlockedCompareExchangePointer((PVOID*)&KeyBody->KeyControlBlock->
|
2007-12-16 16:37:15 +00:00
|
|
|
KeyBodyArray[i],
|
|
|
|
KeyBody,
|
|
|
|
NULL))
|
|
|
|
{
|
|
|
|
/* Added */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-16 16:37:15 +00:00
|
|
|
/* Array full, check if we need to unlock the KCB */
|
|
|
|
if (Flags & CMP_ENLIST_KCB_LOCKED_SHARED)
|
|
|
|
{
|
|
|
|
/* It's shared, so release the KCB shared lock */
|
2011-04-08 12:30:33 +00:00
|
|
|
CmpReleaseKcbLock(KeyBody->KeyControlBlock);
|
2007-12-16 16:37:15 +00:00
|
|
|
ASSERT(!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE));
|
2011-04-08 12:30:33 +00:00
|
|
|
}
|
2007-12-16 16:37:15 +00:00
|
|
|
|
|
|
|
/* Check if we need to lock the KCB */
|
|
|
|
if (!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE))
|
|
|
|
{
|
|
|
|
/* Acquire the lock */
|
2007-12-16 17:23:38 +00:00
|
|
|
CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
|
|
|
|
}
|
2007-12-16 16:37:15 +00:00
|
|
|
|
|
|
|
/* Make sure we have the exclusive lock */
|
2017-06-17 01:22:17 +00:00
|
|
|
CMP_ASSERT_KCB_LOCK(KeyBody->KeyControlBlock);
|
2007-12-16 16:37:15 +00:00
|
|
|
|
2017-06-17 01:22:17 +00:00
|
|
|
/* Do the insert */
|
2007-12-16 16:37:15 +00:00
|
|
|
InsertTailList(&KeyBody->KeyControlBlock->KeyBodyListHead,
|
|
|
|
&KeyBody->KeyBodyList);
|
|
|
|
|
|
|
|
/* Check if we did a manual lock */
|
|
|
|
if (!(Flags & (CMP_ENLIST_KCB_LOCKED_SHARED |
|
|
|
|
CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)))
|
|
|
|
{
|
|
|
|
/* Release the lock */
|
2007-12-16 17:23:38 +00:00
|
|
|
CmpReleaseKcbLock(KeyBody->KeyControlBlock);
|
2011-04-08 12:30:33 +00:00
|
|
|
}
|
2007-12-15 18:14:41 +00:00
|
|
|
}
|
|
|
|
|
2007-12-16 16:37:15 +00:00
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody,
|
|
|
|
IN BOOLEAN LockHeld)
|
|
|
|
{
|
|
|
|
ULONG i;
|
|
|
|
|
|
|
|
/* Sanity check */
|
|
|
|
ASSERT(KeyBody->KeyControlBlock != NULL);
|
|
|
|
|
|
|
|
/* Check if we can use the parent KCB array */
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
/* Add it into the list */
|
2010-01-13 22:35:43 +00:00
|
|
|
if (InterlockedCompareExchangePointer((VOID*)&KeyBody->KeyControlBlock->
|
2007-12-16 16:37:15 +00:00
|
|
|
KeyBodyArray[i],
|
|
|
|
NULL,
|
|
|
|
KeyBody) == KeyBody)
|
|
|
|
{
|
|
|
|
/* Removed */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sanity checks */
|
|
|
|
ASSERT(IsListEmpty(&KeyBody->KeyControlBlock->KeyBodyListHead) == FALSE);
|
|
|
|
ASSERT(IsListEmpty(&KeyBody->KeyBodyList) == FALSE);
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-16 16:37:15 +00:00
|
|
|
/* Lock the KCB */
|
2007-12-16 17:23:38 +00:00
|
|
|
if (!LockHeld) CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock);
|
2017-06-17 01:22:17 +00:00
|
|
|
CMP_ASSERT_KCB_LOCK(KeyBody->KeyControlBlock);
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2007-12-16 16:37:15 +00:00
|
|
|
/* Remove the entry */
|
|
|
|
RemoveEntryList(&KeyBody->KeyBodyList);
|
|
|
|
|
|
|
|
/* Unlock it it if we did a manual lock */
|
2007-12-16 17:23:38 +00:00
|
|
|
if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock);
|
2007-12-16 16:37:15 +00:00
|
|
|
}
|
2010-04-03 20:22:32 +00:00
|
|
|
|
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
CmpFlushNotifiesOnKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb,
|
|
|
|
IN BOOLEAN LockHeld)
|
|
|
|
{
|
|
|
|
PLIST_ENTRY NextEntry, ListHead;
|
|
|
|
PCM_KEY_BODY KeyBody;
|
|
|
|
|
|
|
|
/* Sanity check */
|
|
|
|
LockHeld ? CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK() : CmpIsKcbLockedExclusive(Kcb);
|
|
|
|
while (TRUE)
|
|
|
|
{
|
|
|
|
/* Is the list empty? */
|
|
|
|
ListHead = &Kcb->KeyBodyListHead;
|
|
|
|
if (!IsListEmpty(ListHead))
|
|
|
|
{
|
|
|
|
/* Loop the list */
|
|
|
|
NextEntry = ListHead->Flink;
|
|
|
|
while (NextEntry != ListHead)
|
|
|
|
{
|
|
|
|
/* Get the key body */
|
|
|
|
KeyBody = CONTAINING_RECORD(NextEntry, CM_KEY_BODY, KeyBodyList);
|
2014-05-30 21:47:22 +00:00
|
|
|
ASSERT(KeyBody->Type == CM_KEY_BODY_TYPE);
|
2010-04-03 20:22:32 +00:00
|
|
|
|
|
|
|
/* Check for notifications */
|
|
|
|
if (KeyBody->NotifyBlock)
|
|
|
|
{
|
|
|
|
/* Is the lock held? */
|
|
|
|
if (LockHeld)
|
|
|
|
{
|
|
|
|
/* Flush it */
|
|
|
|
CmpFlushNotify(KeyBody, LockHeld);
|
|
|
|
ASSERT(KeyBody->NotifyBlock == NULL);
|
|
|
|
continue;
|
|
|
|
}
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2010-04-03 20:22:32 +00:00
|
|
|
/* Lock isn't held, so we need to take a reference */
|
|
|
|
if (ObReferenceObjectSafe(KeyBody))
|
|
|
|
{
|
|
|
|
/* Now we can flush */
|
|
|
|
CmpFlushNotify(KeyBody, LockHeld);
|
|
|
|
ASSERT(KeyBody->NotifyBlock == NULL);
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2010-04-03 20:22:32 +00:00
|
|
|
/* Release the reference we took */
|
2011-04-08 12:30:33 +00:00
|
|
|
ObDereferenceObjectDeferDelete(KeyBody);
|
2010-04-03 20:22:32 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try the next entry */
|
|
|
|
NextEntry = NextEntry->Flink;
|
|
|
|
}
|
|
|
|
}
|
2017-06-17 01:21:21 +00:00
|
|
|
|
2010-04-03 20:22:32 +00:00
|
|
|
/* List has been parsed, exit */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|