diff --git a/common/LZMA/LzmaDecompress.c b/common/LZMA/LzmaDecompress.c index 913ec9710..af3e97017 100644 --- a/common/LZMA/LzmaDecompress.c +++ b/common/LZMA/LzmaDecompress.c @@ -72,7 +72,7 @@ checking of the validity of the source data. It just retrieves the "Original Siz field from the LZMA_HEADER_SIZE beginning bytes of the source data and output it as DestinationSize. And ScratchSize is specific to the decompression implementation. -If SourceSize is less than LZMA_HEADER_SIZE, then ASSERT(). +If SourceSize is less than LZMA_HEADER_SIZE, then return U_BUFFER_TOO_SMALL. @param Source The source buffer containing the compressed data. @param SourceSize The size, bytes, of the source buffer. @@ -94,8 +94,9 @@ LzmaGetInfo ( ) { UINT64 DecodedSize; - ASSERT(SourceSize >= LZMA_HEADER_SIZE); - (void)SourceSize; + if (SourceSize <= LZMA_HEADER_SIZE) { + return U_BUFFER_TOO_SMALL; + } DecodedSize = GetDecodedSizeOfBuf((UINT8*)Source); diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 758af4857..53c963124 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -352,6 +352,9 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l if (regionSection->MeLimit) { me.offset = calculateRegionOffset(regionSection->MeBase); me.length = calculateRegionSize(regionSection->MeBase, regionSection->MeLimit); + if (me.offset + me.length < me.offset) { + return U_INVALID_FLASH_DESCRIPTOR; + } if ((UINT32)intelImage.size() < me.offset + me.length) { msg(usprintf("%s: ", __FUNCTION__) + itemSubtypeToUString(Types::Region, me.type) @@ -391,8 +394,11 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l index); return U_TRUNCATED_IMAGE; } - bios.data = intelImage.mid(bios.offset, bios.length); - regions.push_back(bios); + + if (intelImage.size() > bios.offset) { + bios.data = intelImage.mid(bios.offset, bios.length); + regions.push_back(bios); + } } else { msg(usprintf("%s: descriptor parsing failed, BIOS region not found in descriptor", __FUNCTION__)); @@ -419,8 +425,11 @@ USTATUS FfsParser::parseIntelImage(const UByteArray & intelImage, const UINT32 l index); return U_TRUNCATED_IMAGE; } - region.data = intelImage.mid(region.offset, region.length); - regions.push_back(region); + + if (intelImage.size() > region.offset) { + region.data = intelImage.mid(region.offset, region.length); + regions.push_back(region); + } } } } @@ -1866,7 +1875,11 @@ USTATUS FfsParser::parseVolumeNonUefiData(const UByteArray & data, const UINT32 // Sanity check if (!index.isValid()) return U_INVALID_PARAMETER; - + + // If parent has the same offset as this item, then we are in infinite recursion, so we break here. + if (model->offset(index) == localOffset) + return U_INVALID_PARAMETER; + // Get info UString info = usprintf("Full size: %Xh (%u)", (UINT32)data.size(), (UINT32)data.size()); @@ -2168,7 +2181,11 @@ USTATUS FfsParser::parseFileHeader(const UByteArray & file, const UINT32 localOf // Get file body UByteArray body = file.mid(header.size()); - + + if (body.size() < sizeof(UINT16)) { + return U_INVALID_FILE; + } + // Check for file tail presence UByteArray tail; bool msgInvalidTailValue = false; @@ -2771,13 +2788,16 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI } else { // Normal section const EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader = (const EFI_GUID_DEFINED_SECTION*)(sectionHeader + 1); + if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER) + sizeof(EFI_GUID_DEFINED_SECTION)) + return U_INVALID_SECTION; headerSize = sizeof(EFI_COMMON_SECTION_HEADER) + sizeof(EFI_GUID_DEFINED_SECTION); guid = guidDefinedSectionHeader->SectionDefinitionGuid; dataOffset = guidDefinedSectionHeader->DataOffset; attributes = guidDefinedSectionHeader->Attributes; } + // Check sanity again - if ((UINT32)section.size() < headerSize) + if ((UINT32)section.size() < headerSize || section.size() < dataOffset) return U_INVALID_SECTION; // Check for special GUIDed sections @@ -2892,6 +2912,10 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI if (certType == WIN_CERT_TYPE_EFI_GUID) { additionalInfo += UString("\nCertificate type: UEFI"); + // Sanity check + if ((UINT32)section.size() < headerSize + sizeof(WIN_CERTIFICATE_UEFI_GUID)) + return U_INVALID_SECTION; + // Get certificate GUID const WIN_CERTIFICATE_UEFI_GUID* winCertificateUefiGuid = (const WIN_CERTIFICATE_UEFI_GUID*)(section.constData() + headerSize); UByteArray certTypeGuid((const char*)&winCertificateUefiGuid->CertType, sizeof(EFI_GUID)); @@ -2967,7 +2991,7 @@ USTATUS FfsParser::parseGuidedSectionHeader(const UByteArray & section, const UI USTATUS FfsParser::parseFreeformGuidedSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool probe) { // Check sanity - if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER)) + if ((UINT32)section.size() < sizeof(EFI_COMMON_SECTION_HEADER) + sizeof(EFI_FREEFORM_SUBTYPE_GUID_SECTION)) return U_INVALID_SECTION; // Obtain required information from parent volume @@ -3420,7 +3444,8 @@ USTATUS FfsParser::parseVersionSectionBody(const UModelIndex & index) return U_INVALID_PARAMETER; // Add info - model->addInfo(index, UString("\nVersion string: ") + uFromUcs2(model->body(index).constData())); + UByteArray body = model->body(index); + model->addInfo(index, UString("\nVersion string: ") + uFromUcs2(body.constData(), body.size())); return U_SUCCESS; } @@ -3556,8 +3581,9 @@ USTATUS FfsParser::parseUiSectionBody(const UModelIndex & index) // Sanity check if (!index.isValid()) return U_INVALID_PARAMETER; - - UString text = uFromUcs2(model->body(index).constData()); + + const auto body = model->body(index); + UString text = uFromUcs2(body.constData(), body.size()); // Add info model->addInfo(index, UString("\nText: ") + text); @@ -3677,7 +3703,7 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index, const bool } const EFI_IMAGE_PE_HEADER* peHeader = (EFI_IMAGE_PE_HEADER*)(body.constData() + dosHeader->e_lfanew); - if (body.size() < (UINT8*)peHeader - (UINT8*)dosHeader) { + if (body.size() < (UINT8*)peHeader - (UINT8*)dosHeader + sizeof(EFI_IMAGE_PE_HEADER)) { if (probe) return U_INVALID_IMAGE; info += UString("\nDOS header: invalid"); @@ -3696,7 +3722,7 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index, const bool } const EFI_IMAGE_FILE_HEADER* imageFileHeader = (const EFI_IMAGE_FILE_HEADER*)(peHeader + 1); - if (body.size() < (UINT8*)imageFileHeader - (UINT8*)dosHeader) { + if (body.size() < (UINT8*)imageFileHeader - (UINT8*)dosHeader + sizeof(EFI_IMAGE_FILE_HEADER)) { if (probe) return U_INVALID_IMAGE; info += UString("\nPE header: invalid"); @@ -3715,7 +3741,7 @@ USTATUS FfsParser::parsePeImageSectionBody(const UModelIndex & index, const bool EFI_IMAGE_OPTIONAL_HEADER_POINTERS_UNION optionalHeader = {}; optionalHeader.H32 = (const EFI_IMAGE_OPTIONAL_HEADER32*)(imageFileHeader + 1); - if (body.size() < (UINT8*)optionalHeader.H32 - (UINT8*)dosHeader) { + if (body.size() < (UINT8*)optionalHeader.H32 - (UINT8*)dosHeader + sizeof(EFI_IMAGE_OPTIONAL_HEADER32)) { if (probe) return U_INVALID_IMAGE; info += UString("\nPE optional header: invalid"); @@ -4968,6 +4994,11 @@ USTATUS FfsParser::parseCpdRegion(const UByteArray & region, const UINT32 localO std::vector partitions; UINT32 offset = ptHeaderSize; const CPD_ENTRY* firstCpdEntry = (const CPD_ENTRY*)(body.constData()); + + if (cpdHeader->NumEntries * sizeof(CPD_ENTRY) > (UINT32)body.size()) { + return U_INVALID_ME_PARTITION_TABLE; + } + for (UINT32 i = 0; i < cpdHeader->NumEntries; i++) { // Populate entry header const CPD_ENTRY* cpdEntry = firstCpdEntry + i; @@ -5032,10 +5063,15 @@ USTATUS FfsParser::parseCpdRegion(const UByteArray & region, const UINT32 localO // Parse into data block, find Module Attributes extension, and get compressed size from there UINT32 offset = 0; UINT32 length = 0xFFFFFFFF; // Special guardian value + + if (region.size() <= partitions[i].ptEntry.Offset.Offset) { + break; + } + UByteArray partition = region.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length); - while (offset < (UINT32)partition.size()) { + while (offset < ((UINT32)partition.size() - sizeof(CPD_EXTENTION_HEADER))) { const CPD_EXTENTION_HEADER* extHeader = (const CPD_EXTENTION_HEADER*) (partition.constData() + offset); - if (extHeader->Length <= ((UINT32)partition.size() - offset)) { + if (extHeader->Length && extHeader->Length <= ((UINT32)partition.size() - offset)) { if (extHeader->Type == CPD_EXT_TYPE_MODULE_ATTRIBUTES) { const CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const CPD_EXT_MODULE_ATTRIBUTES*)(partition.constData() + offset); length = attrHeader->CompressedSize; @@ -5183,7 +5219,7 @@ USTATUS FfsParser::parseCpdRegion(const UByteArray & region, const UINT32 localO if (!partitions[i].ptEntry.Offset.HuffmanCompressed && partitions[i].ptEntry.Length >= sizeof(CPD_MANIFEST_HEADER)) { const CPD_MANIFEST_HEADER* manifestHeader = (const CPD_MANIFEST_HEADER*) partition.constData(); - if (manifestHeader->HeaderId == ME_MANIFEST_HEADER_ID) { + if (manifestHeader->HeaderId == ME_MANIFEST_HEADER_ID && (manifestHeader->HeaderLength * sizeof(UINT32)) <= partition.size()) { UByteArray header = partition.left(manifestHeader->HeaderLength * sizeof(UINT32)); UByteArray body = partition.mid(manifestHeader->HeaderLength * sizeof(UINT32)); @@ -5274,7 +5310,7 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index, const UINT3 UByteArray body = model->body(index); UINT32 offset = 0; - while (offset < (UINT32)body.size()) { + while (offset < (UINT32)body.size() - sizeof(CPD_EXTENTION_HEADER)) { const CPD_EXTENTION_HEADER* extHeader = (const CPD_EXTENTION_HEADER*) (body.constData() + offset); if (extHeader->Length > 0 && extHeader->Length <= ((UINT32)body.size() - offset)) { @@ -5288,30 +5324,31 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index, const UINT3 if (extHeader->Type == CPD_EXT_TYPE_SIGNED_PACKAGE_INFO) { UByteArray header = partition.left(sizeof(CPD_EXT_SIGNED_PACKAGE_INFO)); UByteArray data = partition.mid(header.size()); - - const CPD_EXT_SIGNED_PACKAGE_INFO* infoHeader = (const CPD_EXT_SIGNED_PACKAGE_INFO*)header.constData(); - - info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %Xh\n" - "Package name: %.4s\nVersion control number: %Xh\nSecurity version number: %Xh\n" - "Usage bitmap: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", - (UINT32)partition.size(), (UINT32)partition.size(), - (UINT32)header.size(), (UINT32)header.size(), - (UINT32)body.size(), (UINT32)body.size(), - infoHeader->ExtensionType, - infoHeader->PackageName, - infoHeader->Vcn, - infoHeader->Svn, - infoHeader->UsageBitmap[0], infoHeader->UsageBitmap[1], infoHeader->UsageBitmap[2], infoHeader->UsageBitmap[3], - infoHeader->UsageBitmap[4], infoHeader->UsageBitmap[5], infoHeader->UsageBitmap[6], infoHeader->UsageBitmap[7], - infoHeader->UsageBitmap[8], infoHeader->UsageBitmap[9], infoHeader->UsageBitmap[10], infoHeader->UsageBitmap[11], - infoHeader->UsageBitmap[12], infoHeader->UsageBitmap[13], infoHeader->UsageBitmap[14], infoHeader->UsageBitmap[15]); - - // Add tree item - extIndex = model->addItem(offset + localOffset, Types::CpdExtension, 0, name, UString(), info, header, data, UByteArray(), Fixed, index); - parseSignedPackageInfoData(extIndex); + if (header.size() >= sizeof(CPD_EXT_SIGNED_PACKAGE_INFO)) { + const CPD_EXT_SIGNED_PACKAGE_INFO* infoHeader = (const CPD_EXT_SIGNED_PACKAGE_INFO*)header.constData(); + + info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %Xh\n" + "Package name: %.4s\nVersion control number: %Xh\nSecurity version number: %Xh\n" + "Usage bitmap: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + (UINT32)partition.size(), (UINT32)partition.size(), + (UINT32)header.size(), (UINT32)header.size(), + (UINT32)body.size(), (UINT32)body.size(), + infoHeader->ExtensionType, + infoHeader->PackageName, + infoHeader->Vcn, + infoHeader->Svn, + infoHeader->UsageBitmap[0], infoHeader->UsageBitmap[1], infoHeader->UsageBitmap[2], infoHeader->UsageBitmap[3], + infoHeader->UsageBitmap[4], infoHeader->UsageBitmap[5], infoHeader->UsageBitmap[6], infoHeader->UsageBitmap[7], + infoHeader->UsageBitmap[8], infoHeader->UsageBitmap[9], infoHeader->UsageBitmap[10], infoHeader->UsageBitmap[11], + infoHeader->UsageBitmap[12], infoHeader->UsageBitmap[13], infoHeader->UsageBitmap[14], infoHeader->UsageBitmap[15]); + + // Add tree item + extIndex = model->addItem(offset + localOffset, Types::CpdExtension, 0, name, UString(), info, header, data, UByteArray(), Fixed, index); + parseSignedPackageInfoData(extIndex); + } } // Parse IFWI Partition Manifest a bit further - else if (extHeader->Type == CPD_EXT_TYPE_IFWI_PARTITION_MANIFEST) { + else if (extHeader->Type == CPD_EXT_TYPE_IFWI_PARTITION_MANIFEST && partition.size() >= sizeof(CPD_EXT_IFWI_PARTITION_MANIFEST)) { const CPD_EXT_IFWI_PARTITION_MANIFEST* attrHeader = (const CPD_EXT_IFWI_PARTITION_MANIFEST*)partition.constData(); // Check HashSize to be sane. @@ -5356,10 +5393,10 @@ USTATUS FfsParser::parseCpdExtensionsArea(const UModelIndex & index, const UINT3 } } // Parse Module Attributes a bit further - else if (extHeader->Type == CPD_EXT_TYPE_MODULE_ATTRIBUTES) { + else if (extHeader->Type == CPD_EXT_TYPE_MODULE_ATTRIBUTES && partition.size() >= sizeof(CPD_EXT_MODULE_ATTRIBUTES)) { const CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const CPD_EXT_MODULE_ATTRIBUTES*)partition.constData(); int hashSize = (UINT32)partition.size() - CpdExtModuleImageHashOffset; - + // This hash is stored reversed // Need to reverse it back to normal UByteArray hash((const char*)attrHeader + CpdExtModuleImageHashOffset, hashSize); @@ -5411,9 +5448,10 @@ USTATUS FfsParser::parseSignedPackageInfoData(const UModelIndex & index) UByteArray body = model->body(index); UINT32 offset = 0; while (offset < (UINT32)body.size()) { + if (body.size() - offset < sizeof(CPD_EXT_SIGNED_PACKAGE_INFO_MODULE)) + break; const CPD_EXT_SIGNED_PACKAGE_INFO_MODULE* moduleHeader = (const CPD_EXT_SIGNED_PACKAGE_INFO_MODULE*)(body.constData() + offset); - if (sizeof(CPD_EXT_SIGNED_PACKAGE_INFO_MODULE) <= ((UINT32)body.size() - offset)) { - // TODO: check sanity of moduleHeader->HashSize + if ((sizeof(CPD_EXT_SIGNED_PACKAGE_INFO_MODULE) + moduleHeader->HashSize) <= ((UINT32)body.size() - offset)) { UByteArray module((const char*)moduleHeader, CpdExtSignedPkgMetadataHashOffset + moduleHeader->HashSize); UString name = usprintf("%.12s", moduleHeader->Name); diff --git a/common/fitparser.cpp b/common/fitparser.cpp index 5f617a39c..03ccad623 100644 --- a/common/fitparser.cpp +++ b/common/fitparser.cpp @@ -70,7 +70,12 @@ USTATUS FitParser::parseFit(const UModelIndex & index) msg(usprintf("%s: not enough space to contain the whole FIT table", __FUNCTION__), fitIndex); return U_INVALID_FIT; } - + + if (fitSize == 0) { + msg(usprintf("%s: FIT header size is 0", __FUNCTION__)); + return U_INVALID_FIT; + } + // Check FIT checksum, if present if (fitHeader->ChecksumValid) { // Calculate FIT entry checksum @@ -221,6 +226,10 @@ void FitParser::findFitRecursive(const UModelIndex & index, UModelIndex & found, UByteArray lastVtfBody = model->body(ffsParser->lastVtf); UINT64 fitSignatureValue = INTEL_FIT_SIGNATURE; UByteArray fitSignature((const char*)&fitSignatureValue, sizeof(fitSignatureValue)); + + if (lastVtfBody.size() < INTEL_FIT_POINTER_OFFSET) + return; + UINT32 storedFitAddress = *(const UINT32*)(lastVtfBody.constData() + lastVtfBody.size() - INTEL_FIT_POINTER_OFFSET); for (INT32 offset = (INT32)model->body(index).indexOf(fitSignature); offset >= 0; diff --git a/common/kaitai/kaitaistream.cpp b/common/kaitai/kaitaistream.cpp index c592d3e86..dd1c08a67 100644 --- a/common/kaitai/kaitaistream.cpp +++ b/common/kaitai/kaitaistream.cpp @@ -478,14 +478,25 @@ uint64_t kaitai::kstream::read_bits_int_le(int n) { // ======================================================================== std::string kaitai::kstream::read_bytes(std::streamsize len) { - std::vector result(len); - // NOTE: streamsize type is signed, negative values are only *supposed* to not be used. // https://en.cppreference.com/w/cpp/io/streamsize if (len < 0) { throw std::runtime_error("read_bytes: requested a negative amount"); } + if (len > 0) { + // Check that the stream has enough data before allocating + std::streampos cur_pos = m_io->tellg(); + m_io->seekg(0, std::ios::end); + std::streampos end_pos = m_io->tellg(); + m_io->seekg(cur_pos); + if (len > end_pos - cur_pos) { + throw std::runtime_error("read_bytes: requested more bytes than available in stream"); + } + } + + std::vector result(len); + if (len > 0) { m_io->read(&result[0], len); } diff --git a/common/meparser.cpp b/common/meparser.cpp index aea1ae001..ca15ad40b 100755 --- a/common/meparser.cpp +++ b/common/meparser.cpp @@ -183,6 +183,12 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & UINT32 offset = (UINT32)header.size(); UINT32 numEntries = ptHeader->NumEntries; const FPT_HEADER_ENTRY* firstPtEntry = (const FPT_HEADER_ENTRY*)(region.constData() + offset); + + if ((UINT32)offset + sizeof(FPT_HEADER_ENTRY) * numEntries >= region.size()) { + msg(usprintf("%s: Corrupted ME region, too many header entries", __FUNCTION__), parent); + return U_INVALID_ME_PARTITION_TABLE; + } + for (UINT32 i = 0; i < numEntries; i++) { // Populate entry header const FPT_HEADER_ENTRY* ptEntry = firstPtEntry + i; @@ -249,9 +255,9 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & // Check for intersections/paddings between partitions for (size_t i = 1; i < partitions.size(); i++) { UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Size; - // Check that current region is fully present in the image - if ((UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Size > (UINT32)region.size()) { + if (partitions[i].ptEntry.Offset > UINT_MAX - partitions[i].ptEntry.Size || + (UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Size > (UINT32)region.size()) { if ((UINT32)partitions[i].ptEntry.Offset >= (UINT32)region.size()) { msg(usprintf("%s: FPT partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index); partitions.erase(partitions.begin() + i); @@ -460,6 +466,9 @@ USTATUS MeParser::parseIfwi16Region(const UByteArray & region, const UModelIndex // Partition map is consistent for (size_t i = 0; i < partitions.size(); i++) { + // Sanity check + if (partitions[i].ptEntry.Offset >= region.size()) + break; UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Size); if (partitions[i].type == Types::IfwiPartition) { UModelIndex partitionIndex; @@ -597,7 +606,8 @@ USTATUS MeParser::parseIfwi17Region(const UByteArray & region, const UModelIndex UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Size; // Check that current region is fully present in the image - if ((UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Size > (UINT32)region.size()) { + if (partitions[i].ptEntry.Offset > UINT_MAX - partitions[i].ptEntry.Size || + (UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Size > (UINT32)region.size()) { if ((UINT32)partitions[i].ptEntry.Offset >= (UINT32)region.size()) { msg(usprintf("%s: IFWI partition is located outside of the opened image, skipped", __FUNCTION__), index); partitions.erase(partitions.begin() + i); diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp index 0801b624d..fd9a0641a 100644 --- a/common/nvramparser.cpp +++ b/common/nvramparser.cpp @@ -56,8 +56,10 @@ USTATUS NvramParser::parseNvarStore(const UModelIndex & index, const bool probe) UINT8 emptyByte = 0xFF; if (model->hasEmptyParsingData(index) == false) { UByteArray data = model->parsingData(index); - const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData(); - emptyByte = pdata->emptyByte; + if ((UINT32)data.size() >= sizeof(VOLUME_PARSING_DATA)) { + const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData(); + emptyByte = pdata->emptyByte; + } } try { @@ -205,6 +207,10 @@ USTATUS NvramParser::parseNvarStore(const UModelIndex & index, const bool probe) if (guidsInStore < entry_body->guid_index() + 1) guidsInStore = entry_body->guid_index() + 1; + // Sanity check. + if (nvar.size() < sizeof(EFI_GUID) * (entry_body->guid_index() + 1)) + goto processing_done; + // The list begins at the end of the store and goes backwards const EFI_GUID g = readUnaligned((EFI_GUID*)(nvar.constData() + nvar.size()) - (entry_body->guid_index() + 1)); name = guidToUString(g); @@ -316,8 +322,10 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index,const UINT32 UINT8 emptyByte = 0xFF; if (model->hasEmptyParsingData(index) == false) { UByteArray data = model->parsingData(index); - const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData(); - emptyByte = pdata->emptyByte; + if ((UINT32)data.size() >= sizeof(VOLUME_PARSING_DATA)) { + const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData(); + emptyByte = pdata->emptyByte; + } } // Get local offset diff --git a/common/ustring.cpp b/common/ustring.cpp index 2493a4187..f26c86874 100644 --- a/common/ustring.cpp +++ b/common/ustring.cpp @@ -112,11 +112,11 @@ UString uFromUcs2(const char* str, size_t max_len) // Naive implementation assuming that only ASCII LE part of UCS2 is used, str may not be aligned. UString msg; const char *str8 = str; - size_t rest = (max_len == 0) ? SIZE_MAX : max_len; - while (str8[0] && rest) { + size_t rest = (max_len == 0) ? SIZE_MAX : max_len - (max_len % 2); + while (rest > 0 && str8[0]) { msg += str8[0]; str8 += 2; - rest--; + rest -= 2; } return msg; } diff --git a/common/utility.cpp b/common/utility.cpp index 13cf0ebf0..3297602e2 100755 --- a/common/utility.cpp +++ b/common/utility.cpp @@ -206,7 +206,11 @@ USTATUS decompress(const UByteArray & compressedData, const UINT8 compressionTyp UINT8* scratch; UINT32 scratchSize = 0; const EFI_TIANO_HEADER* header; - + + if (compressedData.size() < 4) { + return U_BUFFER_TOO_SMALL; + } + // For all but LZMA dictionary size is 0 dictionarySize = 0; @@ -233,7 +237,11 @@ USTATUS decompress(const UByteArray & compressedData, const UINT8 compressionTyp // Get info function is the same for both algorithms if (U_SUCCESS != EfiTianoGetInfo(data, dataSize, &decompressedSize, &scratchSize)) return U_STANDARD_DECOMPRESSION_FAILED; - + + // Check for suspiciously large decompressed size + if (decompressedSize > INT32_MAX / 4) + return U_STANDARD_DECOMPRESSION_FAILED; + // Allocate memory decompressed = (UINT8*)malloc(decompressedSize); efiDecompressed = (UINT8*)malloc(decompressedSize); @@ -244,18 +252,14 @@ USTATUS decompress(const UByteArray & compressedData, const UINT8 compressionTyp free(scratch); return U_STANDARD_DECOMPRESSION_FAILED; } - + // Decompress section data using both algorithms USTATUS result = U_SUCCESS; // Try Tiano USTATUS TianoResult = TianoDecompress(data, dataSize, decompressed, decompressedSize, scratch, scratchSize); // Try EFI 1.1 USTATUS EfiResult = EfiDecompress(data, dataSize, efiDecompressed, decompressedSize, scratch, scratchSize); - - if (decompressedSize > INT32_MAX) { - result = U_STANDARD_DECOMPRESSION_FAILED; - } - else if (EfiResult == U_SUCCESS && TianoResult == U_SUCCESS) { // Both decompressions are OK + if (EfiResult == U_SUCCESS && TianoResult == U_SUCCESS) { // Both decompressions are OK algorithm = COMPRESSION_ALGORITHM_UNDECIDED; decompressedData = UByteArray((const char*)decompressed, (int)decompressedSize); efiDecompressedData = UByteArray((const char*)efiDecompressed, (int)decompressedSize); @@ -289,6 +293,7 @@ USTATUS decompress(const UByteArray & compressedData, const UINT8 compressionTyp if (U_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) { // Get info as Intel legacy LZMA section data += sizeof(UINT32); + dataSize -= sizeof(UINT32); if (U_SUCCESS != LzmaGetInfo(data, dataSize, &decompressedSize)) { return U_CUSTOMIZED_DECOMPRESSION_FAILED; } diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index fec08f265..ab72dd1fc 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -3,7 +3,14 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.22) PROJECT(ffsparser_fuzzer LANGUAGES C CXX) OPTION(USE_QT "Link against Qt" OFF) -OPTION(USE_AFL "Build in AFL-compatible mode" OFF) + +if (CMAKE_CXX_COMPILER MATCHES "afl-") + MESSAGE("-- Building in AFL-compatible mode") + SET(USE_AFL ON) +ELSE() + MESSAGE("-- Building in libFuzzer mode") + SET(USE_AFL OFF) +ENDIF() SET(CMAKE_CXX_STANDARD 11) SET(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -82,12 +89,6 @@ SET(PROJECT_SOURCES ../common/zlib/zutil.c ) -IF(USE_AFL) - SET(PROJECT_SOURCES ${PROJECT_SOURCES} afl_driver.cpp) - MESSAGE("-- Building in AFL-compatible mode") -ELSE() - MESSAGE("-- Building in libFuzzer mode") -ENDIF() IF(NOT USE_QT) SET(PROJECT_SOURCES ${PROJECT_SOURCES} @@ -110,12 +111,12 @@ ADD_DEFINITIONS( ADD_EXECUTABLE(ffsparser_fuzzer ${PROJECT_SOURCES}) -IF(NOT USE_AFL_DRIVER) +IF(USE_AFL) +TARGET_COMPILE_OPTIONS(ffsparser_fuzzer PRIVATE -O1 -fno-omit-frame-pointer -g -ggdb3 -fsanitize=fuzzer) +TARGET_LINK_LIBRARIES(ffsparser_fuzzer PRIVATE -fsanitize=fuzzer) +ELSE() TARGET_COMPILE_OPTIONS(ffsparser_fuzzer PRIVATE -O1 -fno-omit-frame-pointer -g -ggdb3 -fsanitize=fuzzer,address,undefined -fsanitize-address-use-after-scope -fno-sanitize-recover=undefined) TARGET_LINK_LIBRARIES(ffsparser_fuzzer PRIVATE -fsanitize=fuzzer,address,undefined) -ELSE() -TARGET_COMPILE_OPTIONS(ffsparser_fuzzer PRIVATE -O1 -fno-omit-frame-pointer -g -ggdb3 -fsanitize=address,undefined -fsanitize-coverage=trace-pc-guard -fsanitize-address-use-after-scope -fno-sanitize-recover=undefined) -TARGET_LINK_LIBRARIES(ffsparser_fuzzer PRIVATE -fsanitize=address,undefined) ENDIF() IF(USE_QT) diff --git a/fuzzing/afl_driver.cpp b/fuzzing/afl_driver.cpp index f21dfc58f..147c4c6e1 100644 --- a/fuzzing/afl_driver.cpp +++ b/fuzzing/afl_driver.cpp @@ -48,6 +48,7 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both. #include #include #include +#include #include #include