[NTOSKRNL] Fix refcounting for BCBs

Now, we make sure that we update ref count and BCB list membership
with the BCB lock held, in a row.
This will avoid race conditions where the BCB was removed from the
list, then referenced again, leading to inconsistencies in memory
and crashes later on.
This could notably be triggered while building ReactOS on ReactOS
(one would call this a regression).

CORE-15235
This commit is contained in:
Pierre Schweitzer 2018-10-28 20:48:01 +01:00
parent 0c88f79480
commit cf7969fbfa
No known key found for this signature in database
GPG key ID: 7545556C3D585B0B

View file

@ -152,6 +152,38 @@ CcpMapData(
return TRUE; return TRUE;
} }
static
VOID
CcpDereferenceBcb(
IN PROS_SHARED_CACHE_MAP SharedCacheMap,
IN PINTERNAL_BCB Bcb)
{
ULONG RefCount;
KIRQL OldIrql;
KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
RefCount = --Bcb->RefCount;
if (RefCount == 0)
{
RemoveEntryList(&Bcb->BcbEntry);
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
ASSERT(Bcb->PinCount == 0);
CcRosReleaseVacb(SharedCacheMap,
Bcb->Vacb,
TRUE,
Bcb->Dirty,
FALSE);
ExDeleteResourceLite(&Bcb->Lock);
ExFreeToNPagedLookasideList(&iBcbLookasideList, Bcb);
}
else
{
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
}
}
static static
PVOID PVOID
CcpGetAppropriateBcb( CcpGetAppropriateBcb(
@ -191,12 +223,13 @@ CcpGetAppropriateBcb(
/* Yes, and we've lost */ /* Yes, and we've lost */
if (DupBcb != NULL) if (DupBcb != NULL)
{ {
/* We will return that BCB */
++DupBcb->RefCount;
Result = TRUE; Result = TRUE;
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
if (ToPin) if (ToPin)
{ {
DupBcb->PinCount++;
if (BooleanFlagOn(PinFlags, PIN_EXCLUSIVE)) if (BooleanFlagOn(PinFlags, PIN_EXCLUSIVE))
{ {
Result = ExAcquireResourceExclusiveLite(&iBcb->Lock, BooleanFlagOn(PinFlags, PIN_WAIT)); Result = ExAcquireResourceExclusiveLite(&iBcb->Lock, BooleanFlagOn(PinFlags, PIN_WAIT));
@ -206,24 +239,25 @@ CcpGetAppropriateBcb(
Result = ExAcquireSharedStarveExclusive(&iBcb->Lock, BooleanFlagOn(PinFlags, PIN_WAIT)); Result = ExAcquireSharedStarveExclusive(&iBcb->Lock, BooleanFlagOn(PinFlags, PIN_WAIT));
} }
if (!Result) if (Result)
{ {
DupBcb->PinCount--; DupBcb->PinCount++;
}
else
{
CcpDereferenceBcb(SharedCacheMap, DupBcb);
DupBcb = NULL; DupBcb = NULL;
} }
} }
if (Result) if (DupBcb != NULL)
{ {
/* We'll return that BCB */ /* Delete the loser */
++DupBcb->RefCount; CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
ExDeleteResourceLite(&iBcb->Lock);
ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
} }
/* Delete the loser */
CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
ExDeleteResourceLite(&iBcb->Lock);
ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
/* Return the winner - no need to update buffer address, it's /* Return the winner - no need to update buffer address, it's
* relative to the VACB, which is unchanged. * relative to the VACB, which is unchanged.
*/ */
@ -249,8 +283,8 @@ CcpGetAppropriateBcb(
} }
InsertTailList(&SharedCacheMap->BcbList, &iBcb->BcbEntry); InsertTailList(&SharedCacheMap->BcbList, &iBcb->BcbEntry);
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
} }
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
return iBcb; return iBcb;
} }
@ -273,11 +307,11 @@ CcpPinData(
KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql); KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
NewBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, TRUE); NewBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, TRUE);
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
if (NewBcb != NULL) if (NewBcb != NULL)
{ {
NewBcb->PinCount++; ++NewBcb->RefCount;
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
if (BooleanFlagOn(Flags, PIN_EXCLUSIVE)) if (BooleanFlagOn(Flags, PIN_EXCLUSIVE))
{ {
@ -290,11 +324,12 @@ CcpPinData(
if (!Result) if (!Result)
{ {
NewBcb->PinCount--; CcpDereferenceBcb(SharedCacheMap, NewBcb);
NewBcb = NULL;
} }
else else
{ {
NewBcb->RefCount++; NewBcb->PinCount++;
*Bcb = NewBcb; *Bcb = NewBcb;
*Buffer = (PUCHAR)NewBcb->Vacb->BaseAddress + FileOffset->QuadPart % VACB_MAPPING_GRANULARITY; *Buffer = (PUCHAR)NewBcb->Vacb->BaseAddress + FileOffset->QuadPart % VACB_MAPPING_GRANULARITY;
} }
@ -303,6 +338,8 @@ CcpPinData(
} }
else else
{ {
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
if (BooleanFlagOn(Flags, PIN_IF_BCB)) if (BooleanFlagOn(Flags, PIN_IF_BCB))
{ {
return FALSE; return FALSE;
@ -374,10 +411,11 @@ CcMapData (
KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql); KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
iBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, FALSE); iBcb = CcpFindBcb(SharedCacheMap, FileOffset, Length, FALSE);
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
if (iBcb == NULL) if (iBcb == NULL)
{ {
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
Ret = CcpMapData(SharedCacheMap, FileOffset, Length, Flags, &Vacb, pBuffer); Ret = CcpMapData(SharedCacheMap, FileOffset, Length, Flags, &Vacb, pBuffer);
if (Ret) if (Ret)
{ {
@ -396,6 +434,8 @@ CcMapData (
else else
{ {
++iBcb->RefCount; ++iBcb->RefCount;
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
*pBcb = iBcb; *pBcb = iBcb;
*pBuffer = (PUCHAR)iBcb->Vacb->BaseAddress + FileOffset->QuadPart % VACB_MAPPING_GRANULARITY; *pBuffer = (PUCHAR)iBcb->Vacb->BaseAddress + FileOffset->QuadPart % VACB_MAPPING_GRANULARITY;
Ret = TRUE; Ret = TRUE;
@ -564,6 +604,7 @@ CcUnpinDataForThread (
IN ERESOURCE_THREAD ResourceThreadId) IN ERESOURCE_THREAD ResourceThreadId)
{ {
PINTERNAL_BCB iBcb = Bcb; PINTERNAL_BCB iBcb = Bcb;
PROS_SHARED_CACHE_MAP SharedCacheMap;
CCTRACE(CC_API_DEBUG, "Bcb=%p ResourceThreadId=%lu\n", Bcb, ResourceThreadId); CCTRACE(CC_API_DEBUG, "Bcb=%p ResourceThreadId=%lu\n", Bcb, ResourceThreadId);
@ -573,29 +614,8 @@ CcUnpinDataForThread (
iBcb->PinCount--; iBcb->PinCount--;
} }
if (--iBcb->RefCount == 0) SharedCacheMap = iBcb->Vacb->SharedCacheMap;
{ CcpDereferenceBcb(SharedCacheMap, iBcb);
KIRQL OldIrql;
PROS_SHARED_CACHE_MAP SharedCacheMap;
ASSERT(iBcb->PinCount == 0);
SharedCacheMap = iBcb->Vacb->SharedCacheMap;
CcRosReleaseVacb(SharedCacheMap,
iBcb->Vacb,
TRUE,
iBcb->Dirty,
FALSE);
KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
if (!IsListEmpty(&iBcb->BcbEntry))
{
RemoveEntryList(&iBcb->BcbEntry);
}
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
ExDeleteResourceLite(&iBcb->Lock);
ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
}
} }
/* /*
@ -629,9 +649,15 @@ CcUnpinRepinnedBcb (
CCTRACE(CC_API_DEBUG, "Bcb=%p WriteThrough=%d\n", Bcb, WriteThrough); CCTRACE(CC_API_DEBUG, "Bcb=%p WriteThrough=%d\n", Bcb, WriteThrough);
SharedCacheMap = iBcb->Vacb->SharedCacheMap;
IoStatus->Status = STATUS_SUCCESS; IoStatus->Status = STATUS_SUCCESS;
KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
if (--iBcb->RefCount == 0) if (--iBcb->RefCount == 0)
{ {
RemoveEntryList(&iBcb->BcbEntry);
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
IoStatus->Information = 0; IoStatus->Information = 0;
if (WriteThrough) if (WriteThrough)
{ {
@ -656,21 +682,17 @@ CcUnpinRepinnedBcb (
ASSERT(iBcb->PinCount == 0); ASSERT(iBcb->PinCount == 0);
} }
SharedCacheMap = iBcb->Vacb->SharedCacheMap;
CcRosReleaseVacb(iBcb->Vacb->SharedCacheMap, CcRosReleaseVacb(iBcb->Vacb->SharedCacheMap,
iBcb->Vacb, iBcb->Vacb,
TRUE, TRUE,
iBcb->Dirty, iBcb->Dirty,
FALSE); FALSE);
KeAcquireSpinLock(&SharedCacheMap->BcbSpinLock, &OldIrql);
if (!IsListEmpty(&iBcb->BcbEntry))
{
RemoveEntryList(&iBcb->BcbEntry);
}
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
ExDeleteResourceLite(&iBcb->Lock); ExDeleteResourceLite(&iBcb->Lock);
ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb); ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
} }
else
{
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
}
} }