Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
751ad6c
Fix for crash-12265fdfad744cb675bd2120453e87030b0624a6
pagabuc Feb 18, 2026
85fdf85
Fix for crash-03a99f1800aafdc2238298baec5d740cc77828e
pagabuc Feb 18, 2026
67fc3f9
Fix for crash-0462b4cc610360d4eec6e258c0dd026b37920346
pagabuc Feb 18, 2026
3278543
Fix for crash-0ac2dacfb8b8ab3ea1c4a574737995441d43adc3
pagabuc Feb 18, 2026
d93f076
Fix for crash-00338cef8739f88c048f410666fae2e8dc83f30c
pagabuc Feb 18, 2026
4a87475
Fix for crash-1010e6df8edc81e228ebdbe207845ac9df764ee2
pagabuc Feb 18, 2026
c4591ba
Fix for crash-32960b3c5271e7488ec54ca3a41b3073bcaac475
pagabuc Feb 18, 2026
a8c995f
Fix for crash-9bbd468e6bdd63bf52ad3bfb7c671b6828c0e269
pagabuc Feb 18, 2026
57cb6cc
Fix for crash-5b2884f74e0c64939d5b86913855397b425f2919
pagabuc Feb 18, 2026
1d07c32
Fix for crash-3a09ce5a1856e4840be29d5aab94258819673dc4
pagabuc Feb 18, 2026
c48ec81
Fix for crash-90141d59f9ae90f6dd3416f1da18f955299a6cb7. Since uFromUc…
pagabuc Feb 18, 2026
c6e1491
Fix for id:000004,sig:06,sync:main,src:000989
pagabuc Feb 18, 2026
70ab49f
Fix for id:000111,sig:06,src:003815+001062,time:119999402,execs:58381…
pagabuc Feb 18, 2026
d66362f
Fix for id:000072,sig:06,src:000657+002645,time:182432060,execs:64803…
pagabuc Feb 18, 2026
9b229c0
Fix for id:000038,sig:06,src:000932+002511,time:31298173,execs:184318…
pagabuc Feb 18, 2026
419c54c
Fix for id:000069,sig:06,src:003695+003120,time:70163542,execs:366425…
pagabuc Feb 18, 2026
6b7e55a
Fix for id:000021,sig:06,src:002943+000386,time:30587288,execs:171074…
pagabuc Feb 18, 2026
85feb8c
Fix for id:000052,sig:06,src:000792+002032,time:52018996,execs:291549…
pagabuc Feb 18, 2026
29adfb1
Fix for id:000035,sig:06,src:001084+002101,time:33939853,execs:194480…
pagabuc Feb 18, 2026
d6eab5a
Fix for id:000036,sig:11,src:002648+000195,time:32554444,execs:189662…
pagabuc Feb 18, 2026
02f9cce
Fix for id:000054,sig:06,src:002840+001762,time:47328890,execs:270510…
pagabuc Feb 18, 2026
93aee72
Fix for timeout-45a9fb1c96c55339c3040c9fd9db629300baf95b
pagabuc Feb 18, 2026
134dae4
Fix for crash-0460ebaff9b29228671165e9aa4e6c9658c05f98
pagabuc Feb 18, 2026
b9c5766
Fix for crash-0e2468a73f7589be69d5507c90fa74f35cc6b350
pagabuc Feb 18, 2026
df3919b
Fix for id:000084,sig:06,src:007274,time:6473143,execs:15849058,op:ha…
pagabuc Feb 18, 2026
dd8c57b
Fix for id:000168,sig:06,src:007662,time:14727712,execs:26544278,op:h…
pagabuc Feb 18, 2026
7c8a563
Fix for id:000139,sig:06,src:007968,time:16743140,execs:27281275,op:h…
pagabuc Feb 18, 2026
0e72182
Fix for id:000000,sig:06,sync:master,src:003236
pagabuc Feb 18, 2026
b7c0369
Fix for SIGABRT.PC.5555557c449b.STACK.1bcd0e03e5.CODE.-6.ADDR.0.INSTR…
pagabuc Feb 18, 2026
9e5acef
Move 'decompressedSize > INT32_MAX' check before decompression routin…
pagabuc Feb 18, 2026
3723fdc
Lower the max decompressedSize to INT32_MAX / 4
pagabuc Feb 18, 2026
2cef893
Fix for crash-0148f6, id:000069, id:000103, crash-3a6aad, id:000231: …
pagabuc Feb 18, 2026
2a87784
Fix for crash-8e8d4d34ea3e43a262f6b67c7d765ecf1d0cb45f: heap-buffer-o…
pagabuc Feb 18, 2026
22d3066
Fix for crash-68d04a, id:000019: heap-buffer-overflow in parsePeImage…
pagabuc Feb 18, 2026
712675b
Fix for id:000002, id:000207: allocation-size-too-big in kaitai read_…
pagabuc Feb 18, 2026
9b6bdb4
Fix for crash-f49d257a34c42fb5620c3153569c573aa51802ec: stack-overflo…
pagabuc Feb 18, 2026
bbde40b
fix a few off-by-one errors
pagabuc Feb 18, 2026
6d62d17
fix formatting
pagabuc Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions common/LZMA/LzmaDecompress.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);

Expand Down
126 changes: 82 additions & 44 deletions common/ffsparser.cpp

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion common/fitparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 13 additions & 2 deletions common/kaitai/kaitaistream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<char> 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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to indicate that this change in a part of UEFITool-related changes using "//UEFITool: your text here" template. This will ease porting of our changes next time the Kaitai runtime gets an update, and will ensure that we won't lose them.

// 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<char> result(len);

if (len > 0) {
m_io->read(&result[0], len);
}
Expand Down
16 changes: 13 additions & 3 deletions common/meparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
16 changes: 12 additions & 4 deletions common/nvramparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions common/ustring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
21 changes: 13 additions & 8 deletions common/utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
23 changes: 12 additions & 11 deletions fuzzing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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}
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions fuzzing/afl_driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ If 1, close stdout at startup. If 2 close stderr; if 3 close both.
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>

#include <fstream>
#include <iostream>
Expand Down
Loading