Skip to content

Commit 863ca37

Browse files
committed
Add sanity checks and fix a few potential UBs
1 parent e40168b commit 863ca37

1 file changed

Lines changed: 90 additions & 54 deletions

File tree

src/transform/EXECodec.cpp

Lines changed: 90 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -413,9 +413,10 @@ bool EXECodec::inverseX86(SliceArray<kanzi::byte>& input, SliceArray<kanzi::byte
413413

414414
// Current instruction is a jump/call. Decode absolute address
415415
const int addr = BigEndian::readInt32(&src[srcIdx + 1]) ^ MASK_ADDRESS;
416-
const int offset = addr - dstIdx;
416+
const int64 offset = int64(addr) - int64(dstIdx);
417+
const int32 encodedOffset = (offset >= 0) ? int32(offset) : -int32((-offset) & X86_ADDR_MASK);
417418
dst[dstIdx++] = src[srcIdx++];
418-
LittleEndian::writeInt32(&dst[dstIdx], (offset >= 0) ? offset : -(-offset & X86_ADDR_MASK));
419+
LittleEndian::writeInt32(&dst[dstIdx], encodedOffset);
419420
srcIdx += 4;
420421
dstIdx += 4;
421422
}
@@ -623,17 +624,34 @@ kanzi::byte EXECodec::detectType(const kanzi::byte src[], int count, int& codeSt
623624
return NOT_EXE | kanzi::byte(dt);
624625
}
625626

627+
static bool setCodeRange(int count, int& codeStart, int& codeEnd, int64 start, int64 length)
628+
{
629+
if ((start < 0) || (length < 0) || (start > int64(count)) || (length > int64(count) - start))
630+
return false;
631+
632+
if (codeStart == 0)
633+
codeStart = int(start);
634+
635+
codeEnd = int(start + length);
636+
return true;
637+
}
638+
626639
// Return true if known header
627640
bool EXECodec::parseHeader(const kanzi::byte src[], int count, uint magic, int& arch, int& codeStart, int& codeEnd)
628641
{
642+
629643
if (magic == Magic::WIN_MAGIC) {
630644
if (count >= 64) {
631645
const int posPE = LittleEndian::readInt32(&src[60]);
632646

633647
if ((posPE > 0) && (posPE <= count - 48) && (LittleEndian::readInt32(&src[posPE]) == WIN_PE)) {
634648
const kanzi::byte* pe = &src[posPE];
635-
codeStart = min(LittleEndian::readInt32(&pe[44]), count);
636-
codeEnd = min(codeStart + LittleEndian::readInt32(&pe[28]), count);
649+
650+
if (setCodeRange(count, codeStart, codeEnd,
651+
int64(LittleEndian::readInt32(&pe[44])),
652+
int64(LittleEndian::readInt32(&pe[28]))) == false)
653+
return false;
654+
637655
arch = LittleEndian::readInt16(&pe[4]);
638656
}
639657

@@ -648,48 +666,50 @@ bool EXECodec::parseHeader(const kanzi::byte src[], int count, uint magic, int&
648666
if (isLittleEndian == true) {
649667
if (src[4] == kanzi::byte(2)) {
650668
// 64 bits
651-
int nbEntries = int(LittleEndian::readInt16(&src[0x3C]));
652-
int szEntry = int(LittleEndian::readInt16(&src[0x3A]));
653-
int posSection = int(LittleEndian::readLong64(&src[0x28]));
669+
const int nbEntries = int(LittleEndian::readInt16(&src[0x3C]));
670+
const int szEntry = int(LittleEndian::readInt16(&src[0x3A]));
671+
const int64 posSection = LittleEndian::readLong64(&src[0x28]);
672+
673+
if ((szEntry <= 0) || (posSection < 0) || (posSection > int64(count) - 0x28))
674+
return false;
654675

655676
for (int i = 0; i < nbEntries; i++) {
656-
int startEntry = posSection + i * szEntry;
677+
const int64 startEntry = posSection + int64(i) * int64(szEntry);
657678

658-
if (startEntry + 0x28 >= count)
679+
if (startEntry > int64(count) - 0x28)
659680
return false;
660681

661682
int typeSection = int(LittleEndian::readInt32(&src[startEntry + 4]));
662-
int offSection = int(LittleEndian::readLong64(&src[startEntry + 0x18]));
663-
int lenSection = int(LittleEndian::readLong64(&src[startEntry + 0x20]));
683+
const int64 offSection = LittleEndian::readLong64(&src[startEntry + 0x18]);
684+
const int64 lenSection = LittleEndian::readLong64(&src[startEntry + 0x20]);
664685

665686
if ((typeSection == 1) && (lenSection >= 64)) {
666-
if (codeStart == 0)
667-
codeStart = offSection;
668-
669-
codeEnd = offSection + lenSection;
687+
if (setCodeRange(count, codeStart, codeEnd, offSection, lenSection) == false)
688+
return false;
670689
}
671690
}
672691
} else {
673692
// 32 bits
674-
int nbEntries = int(LittleEndian::readInt16(&src[0x30]));
675-
int szEntry = int(LittleEndian::readInt16(&src[0x2E]));
676-
int posSection = int(LittleEndian::readInt32(&src[0x20]));
693+
const int nbEntries = int(LittleEndian::readInt16(&src[0x30]));
694+
const int szEntry = int(LittleEndian::readInt16(&src[0x2E]));
695+
const int64 posSection = int64(LittleEndian::readInt32(&src[0x20]));
696+
697+
if ((szEntry <= 0) || (posSection < 0) || (posSection > int64(count) - 0x18))
698+
return false;
677699

678700
for (int i = 0; i < nbEntries; i++) {
679-
int startEntry = posSection + i * szEntry;
701+
const int64 startEntry = posSection + int64(i) * int64(szEntry);
680702

681-
if (startEntry + 0x18 >= count)
703+
if (startEntry > int64(count) - 0x18)
682704
return false;
683705

684706
int typeSection = int(LittleEndian::readInt32(&src[startEntry + 4]));
685-
int offSection = int(LittleEndian::readInt32(&src[startEntry + 0x10]));
686-
int lenSection = int(LittleEndian::readInt32(&src[startEntry + 0x14]));
707+
const int64 offSection = int64(LittleEndian::readInt32(&src[startEntry + 0x10]));
708+
const int64 lenSection = int64(LittleEndian::readInt32(&src[startEntry + 0x14]));
687709

688710
if ((typeSection == 1) && (lenSection >= 64)) {
689-
if (codeStart == 0)
690-
codeStart = offSection;
691-
692-
codeEnd = offSection + lenSection;
711+
if (setCodeRange(count, codeStart, codeEnd, offSection, lenSection) == false)
712+
return false;
693713
}
694714
}
695715
}
@@ -698,48 +718,50 @@ bool EXECodec::parseHeader(const kanzi::byte src[], int count, uint magic, int&
698718
} else {
699719
if (src[4] == kanzi::byte(2)) {
700720
// 64 bits
701-
int nbEntries = int(BigEndian::readInt16(&src[0x3C]));
702-
int szEntry = int(BigEndian::readInt16(&src[0x3A]));
703-
int posSection = int(BigEndian::readLong64(&src[0x28]));
721+
const int nbEntries = int(BigEndian::readInt16(&src[0x3C]));
722+
const int szEntry = int(BigEndian::readInt16(&src[0x3A]));
723+
const int64 posSection = BigEndian::readLong64(&src[0x28]);
724+
725+
if ((szEntry <= 0) || (posSection < 0) || (posSection > int64(count) - 0x28))
726+
return false;
704727

705728
for (int i = 0; i < nbEntries; i++) {
706-
int startEntry = posSection + i * szEntry;
729+
const int64 startEntry = posSection + int64(i) * int64(szEntry);
707730

708-
if (startEntry + 0x28 >= count)
731+
if (startEntry > int64(count) - 0x28)
709732
return false;
710733

711734
int typeSection = int(BigEndian::readInt32(&src[startEntry + 4]));
712-
int offSection = int(BigEndian::readLong64(&src[startEntry + 0x18]));
713-
int lenSection = int(BigEndian::readLong64(&src[startEntry + 0x20]));
735+
const int64 offSection = BigEndian::readLong64(&src[startEntry + 0x18]);
736+
const int64 lenSection = BigEndian::readLong64(&src[startEntry + 0x20]);
714737

715738
if ((typeSection == 1) && (lenSection >= 64)) {
716-
if (codeStart == 0)
717-
codeStart = offSection;
718-
719-
codeEnd = offSection + lenSection;
739+
if (setCodeRange(count, codeStart, codeEnd, offSection, lenSection) == false)
740+
return false;
720741
}
721742
}
722743
} else {
723744
// 32 bits
724-
int nbEntries = int(BigEndian::readInt16(&src[0x30]));
725-
int szEntry = int(BigEndian::readInt16(&src[0x2E]));
726-
int posSection = int(BigEndian::readInt32(&src[0x20]));
745+
const int nbEntries = int(BigEndian::readInt16(&src[0x30]));
746+
const int szEntry = int(BigEndian::readInt16(&src[0x2E]));
747+
const int64 posSection = int64(BigEndian::readInt32(&src[0x20]));
748+
749+
if ((szEntry <= 0) || (posSection < 0) || (posSection > int64(count) - 0x18))
750+
return false;
727751

728752
for (int i = 0; i < nbEntries; i++) {
729-
int startEntry = posSection + i * szEntry;
753+
const int64 startEntry = posSection + int64(i) * int64(szEntry);
730754

731-
if (startEntry + 0x18 >= count)
755+
if (startEntry > int64(count) - 0x18)
732756
return false;
733757

734758
int typeSection = int(BigEndian::readInt32(&src[startEntry + 4]));
735-
int offSection = int(BigEndian::readInt32(&src[startEntry + 0x10]));
736-
int lenSection = int(BigEndian::readInt32(&src[startEntry + 0x14]));
759+
const int64 offSection = int64(BigEndian::readInt32(&src[startEntry + 0x10]));
760+
const int64 lenSection = int64(BigEndian::readInt32(&src[startEntry + 0x14]));
737761

738762
if ((typeSection == 1) && (lenSection >= 64)) {
739-
if (codeStart == 0)
740-
codeStart = offSection;
741-
742-
codeEnd = offSection + lenSection;
763+
if (setCodeRange(count, codeStart, codeEnd, offSection, lenSection) == false)
764+
return false;
743765
}
744766
}
745767
}
@@ -770,29 +792,43 @@ bool EXECodec::parseHeader(const kanzi::byte src[], int count, uint magic, int&
770792
int cmd = 0;
771793

772794
while (cmd < nbCmds) {
795+
if ((pos < 0) || (pos > count - 8))
796+
return false;
797+
773798
int ldCmd = LittleEndian::readInt32(&src[pos]);
774799
int szCmd = LittleEndian::readInt32(&src[pos + 4]);
775800
int szSegHdr = (is64Bits == true) ? 0x48 : 0x38;
776801

802+
if ((szCmd < 8) || (szCmd > count - pos))
803+
return false;
804+
777805
if ((ldCmd == MAC_LC_SEGMENT) || (ldCmd == MAC_LC_SEGMENT64)) {
778-
if (pos + 14 >= count)
806+
if ((pos > count - 14) || (pos > count - szSegHdr))
779807
return false;
780808

781809
if (memcmp(&src[pos + 8], reinterpret_cast<kanzi::byte*>(MAC_TEXT_SEGMENT), 6) == 0) {
782810
int posSection = pos + szSegHdr;
783811

784-
if (posSection + 0x34 >= count)
812+
const int minSectionSize = (is64Bits == true) ? 0x38 : 0x30;
813+
814+
if (posSection > count - minSectionSize)
785815
return false;
786816

787817
if (memcmp(&src[posSection], reinterpret_cast<kanzi::byte*>(MAC_TEXT_SECTION), 6) == 0) {
788818
// Text section in TEXT segment
789819
if (is64Bits == true) {
790-
codeStart = int(LittleEndian::readLong64(&src[posSection + 0x30]));
791-
codeEnd = codeStart + LittleEndian::readInt32(&src[posSection + 0x28]);
820+
if (setCodeRange(count, codeStart, codeEnd,
821+
LittleEndian::readLong64(&src[posSection + 0x30]),
822+
int64(LittleEndian::readInt32(&src[posSection + 0x28]))) == false)
823+
return false;
824+
792825
break;
793826
} else {
794-
codeStart = LittleEndian::readInt32(&src[posSection + 0x2C]);
795-
codeEnd = codeStart + LittleEndian::readInt32(&src[posSection + 0x28]);
827+
if (setCodeRange(count, codeStart, codeEnd,
828+
int64(LittleEndian::readInt32(&src[posSection + 0x2C])),
829+
int64(LittleEndian::readInt32(&src[posSection + 0x28]))) == false)
830+
return false;
831+
796832
break;
797833
}
798834
}

0 commit comments

Comments
 (0)