MdeModulePkg/DxeCorePerformanceLib: add lock protection

DXE performance gauge record access functions might be reentered since
we are supporting something like USB hot-plug, which is a timer event
where gBS->ConnectController might be called and then PERF will be
called in CoreConnectSingleController.

When StartGaugeEx is being reentered, not only the gauge record might
be overwritten, more serious situation will be caused if gauge data
buffer reallocation procedure is interrupted, between line 180 and 187
in DxeCorePerformanceLib.c specifically. There, mMaxGaugeRecords will
be doubled twice (denoted as 4X), but mGaugeData only points to a
buffer of size 2X, which will probably cause the following 2X memory
to be overflowed when gauge records are increased.

So we add EFI lock with TPL_NOTIFY in StartGaugeEx/EndGaugeEx/GetGaugeEx
to avoid memory overflow and gauge data corruption.

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Heyi Guo <heyi.guo@linaro.org>
Cc: Star Zeng <star.zeng@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Reviewed-by: Star Zeng <star.zeng@intel.com>
This commit is contained in:
Heyi Guo 2017-11-27 11:31:44 +08:00 committed by Star Zeng
parent cc1ad92aaa
commit f1f7190bf3
1 changed files with 79 additions and 14 deletions

View File

@ -63,6 +63,11 @@ PERFORMANCE_EX_PROTOCOL mPerformanceExInterface = {
PERFORMANCE_PROPERTY mPerformanceProperty;
//
// Gauge record lock to avoid data corruption or even memory overflow
//
STATIC EFI_LOCK mPerfRecordLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
/**
Searches in the gauge array with keyword Handle, Token, Module and Identifier.
@ -162,6 +167,12 @@ StartGaugeEx (
UINTN OldGaugeDataSize;
GAUGE_DATA_HEADER *OldGaugeData;
UINT32 Index;
EFI_STATUS Status;
Status = EfiAcquireLockOrFail (&mPerfRecordLock);
if (EFI_ERROR (Status)) {
return Status;
}
Index = mGaugeData->NumberOfEntries;
if (Index >= mMaxGaugeRecords) {
@ -175,6 +186,7 @@ StartGaugeEx (
NewGaugeData = AllocateZeroPool (GaugeDataSize);
if (NewGaugeData == NULL) {
EfiReleaseLock (&mPerfRecordLock);
return EFI_OUT_OF_RESOURCES;
}
@ -209,6 +221,8 @@ StartGaugeEx (
mGaugeData->NumberOfEntries++;
EfiReleaseLock (&mPerfRecordLock);
return EFI_SUCCESS;
}
@ -250,6 +264,12 @@ EndGaugeEx (
{
GAUGE_DATA_ENTRY_EX *GaugeEntryExArray;
UINT32 Index;
EFI_STATUS Status;
Status = EfiAcquireLockOrFail (&mPerfRecordLock);
if (EFI_ERROR (Status)) {
return Status;
}
if (TimeStamp == 0) {
TimeStamp = GetPerformanceCounter ();
@ -257,11 +277,63 @@ EndGaugeEx (
Index = InternalSearchForGaugeEntry (Handle, Token, Module, Identifier);
if (Index >= mGaugeData->NumberOfEntries) {
EfiReleaseLock (&mPerfRecordLock);
return EFI_NOT_FOUND;
}
GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1);
GaugeEntryExArray[Index].EndTimeStamp = TimeStamp;
EfiReleaseLock (&mPerfRecordLock);
return EFI_SUCCESS;
}
/**
Retrieves a previously logged performance measurement.
It can also retrieve the log created by StartGauge and EndGauge of PERFORMANCE_PROTOCOL,
and then assign the Identifier with 0.
Retrieves the performance log entry from the performance log specified by LogEntryKey.
If it stands for a valid entry, then EFI_SUCCESS is returned and
GaugeDataEntryEx stores the pointer to that entry.
This internal function is added to avoid releasing lock before each return statement.
@param LogEntryKey The key for the previous performance measurement log entry.
If 0, then the first performance measurement log entry is retrieved.
@param GaugeDataEntryEx The indirect pointer to the extended gauge data entry specified by LogEntryKey
if the retrieval is successful.
@retval EFI_SUCCESS The GuageDataEntryEx is successfully found based on LogEntryKey.
@retval EFI_NOT_FOUND The LogEntryKey is the last entry (equals to the total entry number).
@retval EFI_INVALIDE_PARAMETER The LogEntryKey is not a valid entry (greater than the total entry number).
@retval EFI_INVALIDE_PARAMETER GaugeDataEntryEx is NULL.
**/
EFI_STATUS
EFIAPI
InternalGetGaugeEx (
IN UINTN LogEntryKey,
OUT GAUGE_DATA_ENTRY_EX **GaugeDataEntryEx
)
{
UINTN NumberOfEntries;
GAUGE_DATA_ENTRY_EX *GaugeEntryExArray;
NumberOfEntries = (UINTN) (mGaugeData->NumberOfEntries);
if (LogEntryKey > NumberOfEntries) {
return EFI_INVALID_PARAMETER;
}
if (LogEntryKey == NumberOfEntries) {
return EFI_NOT_FOUND;
}
GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1);
if (GaugeDataEntryEx == NULL) {
return EFI_INVALID_PARAMETER;
}
*GaugeDataEntryEx = &GaugeEntryExArray[LogEntryKey];
return EFI_SUCCESS;
}
@ -292,25 +364,18 @@ GetGaugeEx (
OUT GAUGE_DATA_ENTRY_EX **GaugeDataEntryEx
)
{
UINTN NumberOfEntries;
GAUGE_DATA_ENTRY_EX *GaugeEntryExArray;
EFI_STATUS Status;
NumberOfEntries = (UINTN) (mGaugeData->NumberOfEntries);
if (LogEntryKey > NumberOfEntries) {
return EFI_INVALID_PARAMETER;
}
if (LogEntryKey == NumberOfEntries) {
return EFI_NOT_FOUND;
Status = EfiAcquireLockOrFail (&mPerfRecordLock);
if (EFI_ERROR (Status)) {
return Status;
}
GaugeEntryExArray = (GAUGE_DATA_ENTRY_EX *) (mGaugeData + 1);
Status = InternalGetGaugeEx (LogEntryKey, GaugeDataEntryEx);
if (GaugeDataEntryEx == NULL) {
return EFI_INVALID_PARAMETER;
}
*GaugeDataEntryEx = &GaugeEntryExArray[LogEntryKey];
EfiReleaseLock (&mPerfRecordLock);
return EFI_SUCCESS;
return Status;
}
/**