From 95c838181f1606c02ecee4e2dc3907ac5475dc7e Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Wed, 4 Sep 2019 17:07:18 -0700 Subject: [PATCH] Improve microcode header detection, parse DevExp1 region as ME region --- common/ffsparser.cpp | 195 ++++++++++++++++++++++++----------------- common/ffsparser.h | 6 +- common/fit.h | 6 +- common/nvramparser.cpp | 17 +--- 4 files changed, 128 insertions(+), 96 deletions(-) diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 3a15d15..4e80c74 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -630,6 +630,8 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l result = parsePdrRegion(region.data, region.offset, index, regionIndex); break; case Subtypes::DevExp1Region: + result = parseDevExp1Region(region.data, region.offset, index, regionIndex); + break; case Subtypes::Bios2Region: case Subtypes::MicrocodeRegion: case Subtypes::EcRegion: @@ -779,6 +781,33 @@ USTATUS FfsParser::parsePdrRegion(const UByteArray & pdr, const UINT32 localOffs return U_SUCCESS; } +USTATUS FfsParser::parseDevExp1Region(const UByteArray & devExp1, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) +{ + // Check sanity + if (devExp1.isEmpty()) + return U_EMPTY_REGION; + + // Get info + UString name("DevExp1 region"); + UString info = usprintf("Full size: %Xh (%u)", devExp1.size(), devExp1.size()); + + bool emptyRegion = false; + // Check for empty region + if (devExp1.size() == devExp1.count('\xFF') || devExp1.size() == devExp1.count('\x00')) { + // Further parsing not needed + emptyRegion = true; + info += ("\nState: empty"); + } + + // Add tree item + index = model->addItem(localOffset, Types::Region, Subtypes::DevExp1Region, name, UString(), info, UByteArray(), devExp1, UByteArray(), Fixed, parent); + + if (!emptyRegion) { + meParser->parseMeRegionBody(index); + } + return U_SUCCESS; +} + USTATUS FfsParser::parseGenericRegion(const UINT8 subtype, const UByteArray & region, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index) { // Check sanity @@ -1189,6 +1218,75 @@ USTATUS FfsParser::parseVolumeHeader(const UByteArray & volume, const UINT32 loc return U_SUCCESS; } +BOOLEAN FfsParser::microcodeHeaderValid(const INTEL_MICROCODE_HEADER* ucodeHeader) +{ + // Check main reserved bytes to be zero + bool reservedBytesValid = true; + for (UINT32 i = 0; i < sizeof(ucodeHeader->Reserved); i++) { + if (ucodeHeader->Reserved[i] != 0x00) { + reservedBytesValid = false; + break; + } + } + if (!reservedBytesValid) { + return FALSE; + } + + // Check CpuFlags reserved bytes to be zero + for (UINT32 i = 0; i < sizeof(ucodeHeader->CpuFlagsReserved); i++) { + if (ucodeHeader->CpuFlagsReserved[i] != 0x00) { + reservedBytesValid = false; + break; + } + } + if (!reservedBytesValid) { + return FALSE; + } + + // Check data size to be multiple of 4 + if (ucodeHeader->DataSize % 4 != 0) { + return FALSE; + } + + // Check TotalSize to be greater then DataSize and multiple of 1024 + if (ucodeHeader->TotalSize <= ucodeHeader->DataSize || ucodeHeader->TotalSize % 1024 != 0) { + return FALSE; + } + + // Check date to be sane + // Check day to be in 0x01-0x09, 0x10-0x19, 0x20-0x29, 0x30-0x31 + if (ucodeHeader->DateDay < 0x01 || + (ucodeHeader->DateDay > 0x09 && ucodeHeader->DateDay < 0x10) || + (ucodeHeader->DateDay > 0x19 && ucodeHeader->DateDay < 0x20) || + (ucodeHeader->DateDay > 0x29 && ucodeHeader->DateDay < 0x30) || + ucodeHeader->DateDay > 0x31) { + return FALSE; + } + // Check month to be in 0x01-0x09, 0x10-0x12 + if (ucodeHeader->DateMonth < 0x01 || + (ucodeHeader->DateMonth > 0x09 && ucodeHeader->DateMonth < 0x10) || + ucodeHeader->DateMonth > 0x12) { + return FALSE; + } + // Check year to be in 0x1990-0x1999, 0x2000-0x2009, 0x2010-0x2019, 0x2020-0x2029, 0x2030-0x2030, 0x2040-0x2049 + if (ucodeHeader->DateYear < 0x1990 || + (ucodeHeader->DateYear > 0x1999 && ucodeHeader->DateYear < 0x2000) || + (ucodeHeader->DateYear > 0x2009 && ucodeHeader->DateYear < 0x2010) || + (ucodeHeader->DateYear > 0x2019 && ucodeHeader->DateYear < 0x2020) || + (ucodeHeader->DateYear > 0x2029 && ucodeHeader->DateYear < 0x2030) || + (ucodeHeader->DateYear > 0x2039 && ucodeHeader->DateYear < 0x2040) || + ucodeHeader->DateYear > 0x2049) { + return FALSE; + } + + // Check loader revision to be sane + if (ucodeHeader->LoaderRevision > INTEL_MICROCODE_MAX_LOADER_REVISION) { + return FALSE; + } + + return TRUE; +} + USTATUS FfsParser::findNextRawAreaItem(const UModelIndex & index, const UINT32 localOffset, UINT8 & nextItemType, UINT32 & nextItemOffset, UINT32 & nextItemSize, UINT32 & nextItemAlternativeSize) { UByteArray data = model->body(index); @@ -1203,29 +1301,14 @@ USTATUS FfsParser::findNextRawAreaItem(const UModelIndex & index, const UINT32 l const UINT32 restSize = dataSize - offset; if (readUnaligned(currentPos) == INTEL_MICROCODE_HEADER_VERSION_1) {// Intel microcode // Check data size - if (restSize < sizeof(INTEL_MICROCODE_HEADER)) - continue; - - // Check microcode size - const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)currentPos; - - // Check reserved bytes - bool reservedBytesValid = true; - for (UINT32 i = 0; i < sizeof(ucodeHeader->Reserved); i++) - if (ucodeHeader->Reserved[i] != 0x00) { - reservedBytesValid = false; - break; - } - if (!reservedBytesValid) - continue; - - // Data size is multiple of 4 - if (ucodeHeader->DataSize % 4 != 0) { + if (restSize < sizeof(INTEL_MICROCODE_HEADER)) { continue; } - // TotalSize is greater then DataSize and is multiple of 1024 - if (ucodeHeader->TotalSize <= ucodeHeader->DataSize || ucodeHeader->TotalSize % 1024 != 0) { + // Check microcode header candidate + const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)currentPos; + + if (FALSE == microcodeHeaderValid(ucodeHeader)) { continue; } @@ -3742,47 +3825,27 @@ void FfsParser::findFitRecursive(const UModelIndex & index, UModelIndex & found, USTATUS FfsParser::parseFitEntryMicrocode(const UByteArray & microcode, const UINT32 localOffset, const UModelIndex & parent, UString & info, UINT32 &realSize) { U_UNUSED_PARAMETER(parent); - if ((UINT32)microcode.size() < localOffset + sizeof(INTEL_MICROCODE_HEADER)) { + if ((UINT32)microcode.size() - localOffset < sizeof(INTEL_MICROCODE_HEADER)) { return U_INVALID_MICROCODE; } - const INTEL_MICROCODE_HEADER* header = (const INTEL_MICROCODE_HEADER*)(microcode.constData() + localOffset); - if (header->Version != INTEL_MICROCODE_HEADER_VERSION_1) { - return U_INVALID_MICROCODE; - } - - bool reservedBytesValid = true; - for (UINT8 i = 0; i < sizeof(header->Reserved); i++) - if (header->Reserved[i] != 0x00) { - reservedBytesValid = false; - break; - } - if (!reservedBytesValid) { - return U_INVALID_MICROCODE; - } - - if (header->DataSize % 4 != 0) { + const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)(microcode.constData() + localOffset); + if (!microcodeHeaderValid(ucodeHeader)) { return U_INVALID_MICROCODE; } - if (header->TotalSize <= header->DataSize || header->TotalSize % 1024 != 0) { + if ((UINT32)microcode.size() - localOffset < ucodeHeader->TotalSize) { return U_INVALID_MICROCODE; } - UINT32 mcSize = header->TotalSize; - if ((UINT32)microcode.size() < localOffset + mcSize) { - return U_INVALID_MICROCODE; - } - // Valid microcode found info = usprintf("CpuSignature: %08Xh, Revision: %08Xh, Date: %02X.%02X.%04X", - header->CpuSignature, - header->Revision, - header->DateDay, - header->DateMonth, - header->DateYear - ); - realSize = mcSize; + ucodeHeader->CpuSignature, + ucodeHeader->Revision, + ucodeHeader->DateDay, + ucodeHeader->DateMonth, + ucodeHeader->DateYear); + realSize = ucodeHeader->TotalSize; return U_SUCCESS; } @@ -4214,38 +4277,15 @@ USTATUS FfsParser::parseIntelMicrocodeHeader(const UByteArray & microcode, const const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)microcode.constData(); - // Header version is 1 - if (ucodeHeader->Version != INTEL_MICROCODE_HEADER_VERSION_1) { + if (!microcodeHeaderValid(ucodeHeader)) { return U_INVALID_MICROCODE; } - // Reserved bytes are all zeroes - bool reservedBytesValid = true; - for (UINT8 i = 0; i < sizeof(ucodeHeader->Reserved); i++) { - if (ucodeHeader->Reserved[i] != 0x00) { - reservedBytesValid = false; - break; - } - } - if (!reservedBytesValid) { - return U_INVALID_MICROCODE; - } - - // Data size is multiple of 4 - if (ucodeHeader->DataSize % 4 != 0) { - return U_INVALID_MICROCODE; - } - - // TotalSize is greater then DataSize and is multiple of 1024 - if (ucodeHeader->TotalSize <= ucodeHeader->DataSize || ucodeHeader->TotalSize % 1024 != 0) { - return U_INVALID_MICROCODE; - } - // We have enough data to fit the whole TotalSize if ((UINT32)microcode.size() < ucodeHeader->TotalSize) { return U_INVALID_MICROCODE; } - + // Valid microcode found UINT32 dataSize = ucodeHeader->DataSize; if (dataSize == 0) @@ -4301,8 +4341,7 @@ USTATUS FfsParser::parseIntelMicrocodeHeader(const UByteArray & microcode, const tempUcodeHeader->CpuSignature = entry->CpuSignature; UINT32 entryCalculated = calculateChecksum32((const UINT32*)tempMicrocode.constData(), sizeof(INTEL_MICROCODE_HEADER) + dataSize); - - extendedHeaderInfo += usprintf("\nCPU signature #%u: %08Xh\nCPU flags #%u: %08Xh\nChecksum #%u: %08Xh, ", + extendedHeaderInfo += usprintf("\nCPU signature #%u: %08Xh\nCPU flags #%u: %02Xh\nChecksum #%u: %08Xh, ", i + 1, entry->CpuSignature, i + 1, entry->CpuFlags, i + 1, entry->Checksum) @@ -4314,7 +4353,7 @@ USTATUS FfsParser::parseIntelMicrocodeHeader(const UByteArray & microcode, const // Add info UString name("Intel microcode"); UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nTail size: %Xh (%u)\n" - "Date: %02X.%02X.%04x\nCPU signature: %08Xh\nRevision: %08Xh\nLoader revision: %08Xh\nCPU flags: %08Xh\nChecksum: %08Xh, ", + "Date: %02X.%02X.%04x\nCPU signature: %08Xh\nRevision: %08Xh\nLoader revision: %08Xh\nCPU flags: %02Xh\nChecksum: %08Xh, ", dataSize, dataSize, header.size(), header.size(), body.size(), body.size(), diff --git a/common/ffsparser.h b/common/ffsparser.h index dc8f6a9..f775db5 100644 --- a/common/ffsparser.h +++ b/common/ffsparser.h @@ -115,6 +115,7 @@ private: USTATUS parseMeRegion(const UByteArray & me, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); USTATUS parseBiosRegion(const UByteArray & bios, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); USTATUS parsePdrRegion(const UByteArray & pdr, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + USTATUS parseDevExp1Region(const UByteArray & devExp1, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); USTATUS parseGenericRegion(const UINT8 subtype, const UByteArray & region, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); USTATUS parsePadFileBody(const UModelIndex & index); @@ -141,6 +142,9 @@ private: USTATUS findNextRawAreaItem(const UModelIndex & index, const UINT32 localOffset, UINT8 & nextItemType, UINT32 & nextItemOffset, UINT32 & nextItemSize, UINT32 & nextItemAlternativeSize); UINT32 getFileSize(const UByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion); UINT32 getSectionSize(const UByteArray & file, const UINT32 sectionOffset, const UINT8 ffsVersion); + + USTATUS parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + BOOLEAN microcodeHeaderValid(const INTEL_MICROCODE_HEADER* ucodeHeader); // Second pass USTATUS performSecondPass(const UModelIndex & index); @@ -152,7 +156,7 @@ private: USTATUS parseResetVectorData(); USTATUS parseFit(const UModelIndex & index); USTATUS parseVendorHashFile(const UByteArray & fileGuid, const UModelIndex & index); - USTATUS parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index); + #ifdef U_ENABLE_FIT_PARSING_SUPPORT void findFitRecursive(const UModelIndex & index, UModelIndex & found, UINT32 & fitOffset); diff --git a/common/fit.h b/common/fit.h index afa98bd..e88582e 100755 --- a/common/fit.h +++ b/common/fit.h @@ -71,7 +71,8 @@ typedef struct INTEL_MICROCODE_HEADER_ { // that comprise the microcode update result in 00000000H. UINT32 LoaderRevision; - UINT32 CpuFlags; + UINT8 CpuFlags; + UINT8 CpuFlagsReserved[3]; UINT32 DataSize; // Specifies the size of the encrypted data in bytes, and must be a multiple of DWORDs. // If this value is 00000000H, then the microcode update encrypted data is 2000 bytes (or 500 DWORDs). @@ -102,7 +103,8 @@ typedef struct INTEL_MICROCODE_EXTENDED_HEADER_ENTRY_ { // Checksum is correct when the summation of all DWORDs that comprise the created Extended Processor Patch results in 00000000H. } INTEL_MICROCODE_EXTENDED_HEADER_ENTRY; -#define INTEL_MICROCODE_HEADER_VERSION_1 0x00000001 +#define INTEL_MICROCODE_HEADER_VERSION_1 0x00000001 +#define INTEL_MICROCODE_MAX_LOADER_REVISION 0x00000010 #pragma pack(pop) diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp index 6a78b4f..7fd684e 100755 --- a/common/nvramparser.cpp +++ b/common/nvramparser.cpp @@ -533,6 +533,7 @@ USTATUS NvramParser::findNextStore(const UModelIndex & index, const UByteArray & if (dataSize < sizeof(UINT32)) return U_STORES_NOT_FOUND; + // TODO: add checks for restSize UINT32 offset = storeOffset; for (; offset < dataSize - sizeof(UINT32); offset++) { const UINT32* currentPos = (const UINT32*)(volume.constData() + offset); @@ -646,24 +647,10 @@ USTATUS NvramParser::findNextStore(const UModelIndex & index, const UByteArray & break; } else if (*currentPos == INTEL_MICROCODE_HEADER_VERSION_1) {// Intel microcode - // Check reserved bytes const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)currentPos; - bool reservedBytesValid = true; - for (UINT32 i = 0; i < sizeof(ucodeHeader->Reserved); i++) - if (ucodeHeader->Reserved[i] != 0x00) { - reservedBytesValid = false; - break; - } - if (!reservedBytesValid) - continue; - - // Data size is multiple of 4 - if (ucodeHeader->DataSize % 4 != 0) { - continue; - } // TotalSize is greater then DataSize and is multiple of 1024 - if (ucodeHeader->TotalSize <= ucodeHeader->DataSize || ucodeHeader->TotalSize % 1024 != 0) { + if (FALSE == ffsParser->microcodeHeaderValid(ucodeHeader)) { continue; }