From a4dcb0ecf632832258ebb523c6bc39b7b94f8775 Mon Sep 17 00:00:00 2001 From: Daniel Brahneborg Date: Sun, 3 Mar 2002 22:02:40 +0000 Subject: Add buffer overflow checks to handle truncated and corrupted sis files. --- lib/siscomponentrecord.cpp | 23 ++++++++++++++++---- lib/siscomponentrecord.h | 4 +++- lib/sisfile.cpp | 52 +++++++++++++++++++++++++++++++++++++++------ lib/sisfile.h | 4 +++- lib/sisfileheader.cpp | 21 +++++++++++++----- lib/sisfileheader.h | 4 +++- lib/sisfilerecord.cpp | 22 +++++++++++++------ lib/sisfilerecord.h | 4 +++- lib/sislangrecord.cpp | 9 ++++++-- lib/sislangrecord.h | 4 +++- lib/sisreqrecord.cpp | 15 ++++++++++--- lib/sisreqrecord.h | 4 +++- lib/sistypes.h | 9 ++++++++ sisinstall/Makefile.am | 3 +-- sisinstall/sisinstaller.cpp | 21 ++++++++++++------ sisinstall/sisinstaller.h | 6 ++++-- sisinstall/sismain.cpp | 17 ++++++++++----- 17 files changed, 173 insertions(+), 49 deletions(-) diff --git a/lib/siscomponentrecord.cpp b/lib/siscomponentrecord.cpp index 7ec468d..8078886 100644 --- a/lib/siscomponentrecord.cpp +++ b/lib/siscomponentrecord.cpp @@ -31,30 +31,44 @@ SISComponentNameRecord::~SISComponentNameRecord() delete[] m_names; } -void -SISComponentNameRecord::fillFrom(uchar* buf, int base, SISFile* sisFile) +SisRC +SISComponentNameRecord::fillFrom(uchar* buf, int base, off_t len, + SISFile* sisFile) { + int n = sisFile->m_header.m_nlangs; + if (base + 8 + n * 4 * 2) + return SIS_TRUNCATED; + uchar* p = buf + base; int size = 0; - int n = sisFile->m_header.m_nlangs; m_nameLengths = new uint32[n]; m_namePtrs = new uint32[n]; - m_names = new uchar*[n]; // First read lengths. // for (int i = 0; i < n; ++i) { m_nameLengths[i] = read32(p + size); + if (m_nameLengths[i] > len) + { + printf("Length too large for name record %d.\n", i); + return SIS_TRUNCATED; + } size += 4; } // Then read ptrs. // + m_names = new uchar*[n]; for (int i = 0; i < n; ++i) { m_namePtrs[i] = read32(p + size); + if (m_namePtrs[i] + m_nameLengths[i] > len) + { + printf("Position/length too large for name record %d.\n", i); + return SIS_TRUNCATED; + } size += 4; if (logLevel >= 2) printf("Name %d (for %s) is %.*s\n", @@ -69,6 +83,7 @@ SISComponentNameRecord::fillFrom(uchar* buf, int base, SISFile* sisFile) } if (logLevel >= 1) printf("%d .. %d (%d bytes): Name records\n", base, base + size, size); + return SIS_OK; } uchar* diff --git a/lib/siscomponentrecord.h b/lib/siscomponentrecord.h index 4b03793..f2a85da 100644 --- a/lib/siscomponentrecord.h +++ b/lib/siscomponentrecord.h @@ -25,6 +25,8 @@ #include "sistypes.h" +#include + class SISFile; /** @@ -40,7 +42,7 @@ public: /** * Populate the fields. */ - void fillFrom(uchar* buf, int base, SISFile* sisFile); + SisRC fillFrom(uchar* buf, int base, off_t len, SISFile* sisFile); /** * Return the name for the given language. diff --git a/lib/sisfile.cpp b/lib/sisfile.cpp index 05551b9..f9a19fb 100644 --- a/lib/sisfile.cpp +++ b/lib/sisfile.cpp @@ -27,11 +27,16 @@ #include -void -SISFile::fillFrom(uchar* buf) +SisRC +SISFile::fillFrom(uchar* buf, off_t len) { int ix = 0; - m_header.fillFrom(buf, &ix); + SisRC rc = m_header.fillFrom(buf, &ix, len); + if (rc != SIS_OK) + { + printf("Could not read header, rc = %d\n", rc); + return rc; + } if (logLevel >= 2) printf("Ate header, got ix = %d\n", ix); int n; @@ -42,7 +47,16 @@ SISFile::fillFrom(uchar* buf) m_langRecords = new SISLangRecord[n]; ix = m_header.m_languagePtr; for (int i = 0; i < n; ++i) - m_langRecords[i].fillFrom(buf, &ix); + { + if (ix >= len) + return SIS_TRUNCATED; + rc = m_langRecords[i].fillFrom(buf, &ix, len); + if (rc != SIS_OK) + { + printf("Problem reading language record %d, rc = %d.\n", i, rc); + return rc; + } + } // Read requisites. // @@ -50,12 +64,26 @@ SISFile::fillFrom(uchar* buf) m_reqRecords = new SISReqRecord[n]; ix = m_header.m_reqPtr; for (int i = 0; i < n; ++i) - m_reqRecords[i].fillFrom(buf, &ix, this); + { + if (ix >= len) + return SIS_TRUNCATED; + rc = m_reqRecords[i].fillFrom(buf, &ix, len, this); + if (rc != SIS_OK) + { + printf("Problem reading requisite record %d, rc = %d.\n", i, rc); + return rc; + } + } // Read component names, by language. // ix = m_header.m_componentPtr; - m_componentRecord.fillFrom(buf, ix, this); + rc = m_componentRecord.fillFrom(buf, ix, len, this); + if (rc != SIS_OK) + { + printf("Problem reading the name record, rc = %d.\n", rc); + return rc; + } // Read files. // @@ -63,8 +91,18 @@ SISFile::fillFrom(uchar* buf) m_fileRecords = new SISFileRecord[n]; ix = m_header.m_filesPtr; for (int i = 0; i < n; ++i) - m_fileRecords[i].fillFrom(buf, &ix, this); + { + if (ix >= len) + return SIS_TRUNCATED; + rc = m_fileRecords[i].fillFrom(buf, &ix, len, this); + if (rc != SIS_OK) + { + printf("Problem reading file record %d, rc = %d.\n", i, rc); + return rc; + } + } + return SIS_OK; } int diff --git a/lib/sisfile.h b/lib/sisfile.h index 0bfa000..45490da 100644 --- a/lib/sisfile.h +++ b/lib/sisfile.h @@ -27,6 +27,8 @@ #include "sisfileheader.h" #include "siscomponentrecord.h" +#include + class SISLangRecord; class SISFileRecord; class SISReqRecord; @@ -41,7 +43,7 @@ public: /** * Populate the fields. */ - void fillFrom(uchar* buf); + SisRC fillFrom(uchar* buf, off_t len); /** * Return the currently selected installation language. diff --git a/lib/sisfileheader.cpp b/lib/sisfileheader.cpp index f8b88d0..3279c38 100644 --- a/lib/sisfileheader.cpp +++ b/lib/sisfileheader.cpp @@ -28,9 +28,11 @@ const int OFF_NUMBER_OF_FILES = 26; const int OFF_INSTALLATION_DRIVE = 28; -void -SISFileHeader::fillFrom(uchar* buf, int* base) +SisRC +SISFileHeader::fillFrom(uchar* buf, int* base, off_t len) { + if (*base + 68 > len) + return SIS_TRUNCATED; uchar* start = buf + *base; m_buf = buf; m_uid1 = read32(start); @@ -40,7 +42,7 @@ SISFileHeader::fillFrom(uchar* buf, int* base) if (m_uid2 != 0x1000006d) { printf("Got bad uid2.\n"); - exit(1); + return SIS_CORRUPTED; } if (logLevel >= 2) printf("Got uid2 = %08x\n", m_uid2); @@ -48,7 +50,7 @@ SISFileHeader::fillFrom(uchar* buf, int* base) if (m_uid3 != 0x10000419) { printf("Got bad uid3.\n"); - exit(1); + return SIS_CORRUPTED; } if (logLevel >= 2) printf("Got uid3 = %08x\n", m_uid3); @@ -66,7 +68,7 @@ SISFileHeader::fillFrom(uchar* buf, int* base) if ((crc2 << 16 | crc1) != m_uid4) { printf("Got bad crc.\n"); - exit(1); + return SIS_CORRUPTED; } m_crc = read16(start + 16); m_nlangs = read16(start + 18); @@ -108,17 +110,26 @@ SISFileHeader::fillFrom(uchar* buf, int* base) m_languagePtr = read32(start + 48); if (logLevel >= 2) printf("Languages begin at %d\n", m_languagePtr); + if (m_languagePtr >= len) + return SIS_TRUNCATED; m_filesPtr = read32(start + 52); if (logLevel >= 2) printf("Files begin at %d\n", m_filesPtr); + if (m_filesPtr >= len) + return SIS_TRUNCATED; m_reqPtr = read32(start + 56); if (logLevel >= 2) printf("Requisites begin at %d\n", m_reqPtr); + if (m_reqPtr >= len) + return SIS_TRUNCATED; m_unknown = read32(start + 60); m_componentPtr = read32(start + 64); if (logLevel >= 2) printf("Components begin at %d\n", m_componentPtr); + if (m_componentPtr >= len) + return SIS_TRUNCATED; *base += 68; + return SIS_OK; } void diff --git a/lib/sisfileheader.h b/lib/sisfileheader.h index 6f22c59..8c90511 100644 --- a/lib/sisfileheader.h +++ b/lib/sisfileheader.h @@ -23,6 +23,8 @@ #ifndef _SISFILEHEADER_H #define _SISFILEHEADER_H +#include + #include "sistypes.h" /** @@ -35,7 +37,7 @@ public: /** * Populate the fields. */ - void fillFrom(uchar* buf, int* base); + SisRC fillFrom(uchar* buf, int* base, off_t len); /** * Update the drive letter, and patch the parsed buffer. diff --git a/lib/sisfilerecord.cpp b/lib/sisfilerecord.cpp index cb2665c..baa5776 100644 --- a/lib/sisfilerecord.cpp +++ b/lib/sisfilerecord.cpp @@ -25,9 +25,12 @@ #include -void -SISFileRecord::fillFrom(uchar* buf, int* base, SISFile* sisFile) +SisRC +SISFileRecord::fillFrom(uchar* buf, int* base, off_t len, SISFile* sisFile) { + if (*base + 28 + 4 * 2 > len) + return SIS_TRUNCATED; + uchar* p = buf + *base; int size = 0; m_flags = read32(p); @@ -79,27 +82,33 @@ SISFileRecord::fillFrom(uchar* buf, int* base, SISFile* sisFile) int n = sisFile->m_header.m_nlangs; m_fileLengths = new uint32[n]; m_filePtrs = new uint32[n]; + if (*base + size + n * 8 > len) + return SIS_TRUNCATED; for (int i = 0; i < n; ++i) { m_fileLengths[i] = read32(p + size); + if (m_fileLengths[i] > len) + return SIS_TRUNCATED; size += 4; } for (int i = 0; i < n; ++i) { m_filePtrs[i] = read32(p + size); + int fileLen = m_fileLengths[i]; + if (m_filePtrs[i] + fileLen > len) + return SIS_TRUNCATED; size += 4; - int len = m_fileLengths[i]; if (logLevel >= 2) printf("File %d (for %s) is %d bytes long (at %d)\n", i, sisFile->getLanguage(i)->m_name, - len, + fileLen, m_filePtrs[i]); if (logLevel >= 1) printf("%d .. %d (%d bytes): File record (%s) for %.*s\n", m_filePtrs[i], - m_filePtrs[i] + len, - len, + m_filePtrs[i] + fileLen, + fileLen, sisFile->getLanguage(i)->m_name, m_destLength, buf + m_destPtr); } @@ -111,5 +120,6 @@ SISFileRecord::fillFrom(uchar* buf, int* base, SISFile* sisFile) printf("Unknown file flags %d\n", m_flags); } *base += size; + return SIS_OK; } diff --git a/lib/sisfilerecord.h b/lib/sisfilerecord.h index d37d64e..ae99836 100644 --- a/lib/sisfilerecord.h +++ b/lib/sisfilerecord.h @@ -25,6 +25,8 @@ #include "sistypes.h" +#include + class SISFile; /** @@ -40,7 +42,7 @@ public: /** * Populate the fields. */ - void fillFrom(uchar* buf, int* base, SISFile* sisFile); + SisRC fillFrom(uchar* buf, int* base, off_t len, SISFile* sisFile); /** * 1 if multiple language versions, otherwise 0. diff --git a/lib/sislangrecord.cpp b/lib/sislangrecord.cpp index 5540e6e..2b6afc4 100644 --- a/lib/sislangrecord.cpp +++ b/lib/sislangrecord.cpp @@ -24,15 +24,20 @@ #include -void -SISLangRecord::fillFrom(uchar* buf, int* base) +SisRC +SISLangRecord::fillFrom(uchar* buf, int* base, off_t len) { + if (*base + 2 > len) + return SIS_TRUNCATED; m_lang = read16(buf + *base); + if (m_lang > 33) // Thai, last language + return SIS_CORRUPTED; if (logLevel >= 2) printf("Got language %d (%s)\n", m_lang, langTable[m_lang].m_name); if (logLevel >= 1) printf("%d .. %d (%d bytes): Language record for %s\n", *base, *base + 2, 2, langTable[m_lang].m_name); *base += 2; + return SIS_OK; } diff --git a/lib/sislangrecord.h b/lib/sislangrecord.h index e8dbd06..9a2b5e9 100644 --- a/lib/sislangrecord.h +++ b/lib/sislangrecord.h @@ -23,6 +23,8 @@ #ifndef _SISLANGRECORD_H #define _SISLANGRECORD_H +#include + #include "sistypes.h" /** @@ -36,7 +38,7 @@ public: /** * Populate the fields. */ - void fillFrom(uchar* buf, int* base); + SisRC fillFrom(uchar* buf, int* base, off_t len); uint16 m_lang; }; diff --git a/lib/sisreqrecord.cpp b/lib/sisreqrecord.cpp index 75d9e51..c13308e 100644 --- a/lib/sisreqrecord.cpp +++ b/lib/sisreqrecord.cpp @@ -25,9 +25,13 @@ #include -void -SISReqRecord::fillFrom(uchar* buf, int* base, SISFile* sisFile) +SisRC +SISReqRecord::fillFrom(uchar* buf, int* base, off_t len, SISFile* sisFile) { + int n = sisFile->m_header.m_nreqs; + if (*base + 12 + n * 4 * 2) + return SIS_TRUNCATED; + uchar* p = buf + *base; int size = 0; @@ -35,7 +39,6 @@ SISReqRecord::fillFrom(uchar* buf, int* base, SISFile* sisFile) m_major = read16(p + 4); m_minor = read16(p + 6); m_variant = read32(p + 8); - int n = sisFile->m_header.m_nreqs; m_nameLengths = new uint32[n]; m_namePtrs = new uint32[n]; @@ -53,6 +56,11 @@ SISReqRecord::fillFrom(uchar* buf, int* base, SISFile* sisFile) for (int i = 0; i < n; ++i) { m_namePtrs[i] = read32(p + size); + if (m_namePtrs[i] + m_nameLengths[i] > len) + { + printf("Position/length too large for req record %d.\n", i); + return SIS_CORRUPTED; + } size += 4; if (logLevel >= 2) printf("Name %d (for %s) is %.*s\n", @@ -65,5 +73,6 @@ SISReqRecord::fillFrom(uchar* buf, int* base, SISFile* sisFile) printf("%d .. %d (%d bytes): Req record\n", *base, *base + size, size); *base += size; + return SIS_OK; } diff --git a/lib/sisreqrecord.h b/lib/sisreqrecord.h index 80481be..44206a1 100644 --- a/lib/sisreqrecord.h +++ b/lib/sisreqrecord.h @@ -25,6 +25,8 @@ #include "sistypes.h" +#include + class SISFile; /** @@ -38,7 +40,7 @@ public: /** * Populate the fields. */ - void fillFrom(uchar* buf, int* base, SISFile* file); + SisRC fillFrom(uchar* buf, int* base, off_t len, SISFile* file); uint32 m_uid; uint16 m_major; diff --git a/lib/sistypes.h b/lib/sistypes.h index ce8b441..c763c4f 100644 --- a/lib/sistypes.h +++ b/lib/sistypes.h @@ -27,6 +27,15 @@ typedef unsigned short uint16; typedef unsigned int uint32; typedef unsigned char uchar; +/** + * Return Codes. + */ +enum SisRC { + SIS_OK = 0, + SIS_TRUNCATED, + SIS_CORRUPTED, +}; + extern uint16 read16(uchar* p); extern uint32 read32(uchar* p); diff --git a/sisinstall/Makefile.am b/sisinstall/Makefile.am index 67c990d..09c2620 100644 --- a/sisinstall/Makefile.am +++ b/sisinstall/Makefile.am @@ -5,8 +5,7 @@ INCLUDES=-I$(top_srcdir)/lib bin_PROGRAMS = sisinstall sisinstall_LDADD = $(top_srcdir)/lib/libplp.la sisinstall_SOURCES = psion.cpp sisinstaller.cpp sismain.cpp \ - fakepsion.cpp -sisinstall_HEADERS = fakepsion.h + fakepsion.cpp fakepsion.h EXTRA_DIST = psion.h sisinstaller.h maintainer-clean-local: diff --git a/sisinstall/sisinstaller.cpp b/sisinstall/sisinstaller.cpp index db58720..a82c9a2 100644 --- a/sisinstall/sisinstaller.cpp +++ b/sisinstall/sisinstaller.cpp @@ -152,10 +152,16 @@ SISInstaller::installFile(SISFileRecord* fileRecord) printf("Recursive sis file...\n"); SISFile sisFile; uchar* buf2 = m_buf + fileRecord->m_filePtrs[m_fileNo]; - sisFile.fillFrom(buf2); + off_t len = fileRecord->m_fileLengths[m_fileNo]; + SisRC rc = sisFile.fillFrom(buf2, len); + if (rc != SIS_OK) + { + printf("Could not read contained sis file, rc = %d\n", rc); + break; + } SISInstaller installer; installer.setPsion(m_psion); - installer.run(&sisFile, buf2, m_file); + rc = installer.run(&sisFile, buf2, len, m_file); if (0 == m_drive) { m_drive = sisFile.m_header.m_installationDrive; @@ -186,14 +192,14 @@ SISInstaller::setPsion(Psion* psion) m_psion = psion; } -void -SISInstaller::run(SISFile* file, uchar* buf) +SisRC +SISInstaller::run(SISFile* file, uchar* buf, off_t len) { - run(file, buf, 0); + return run(file, buf, len, 0); } -void -SISInstaller::run(SISFile* file, uchar* buf, SISFile* parent) +SisRC +SISInstaller::run(SISFile* file, uchar* buf, off_t len, SISFile* parent) { int n; int lang; @@ -318,6 +324,7 @@ SISInstaller::run(SISFile* file, uchar* buf, SISFile* parent) printf("Creating residual sis file %s\n", resname); copyBuf(buf, firstFile, resname); delete[] resname; + return SIS_OK; } void diff --git a/sisinstall/sisinstaller.h b/sisinstall/sisinstaller.h index 6bc6c18..00077f3 100644 --- a/sisinstall/sisinstaller.h +++ b/sisinstall/sisinstaller.h @@ -3,6 +3,8 @@ #include "sistypes.h" +#include + class Psion; class SISFile; class SISFileRecord; @@ -15,9 +17,9 @@ class SISInstaller { public: - void run(SISFile* file, uchar* buf); + SisRC run(SISFile* file, uchar* buf, off_t len); - void run(SISFile* file, uchar* buf, SISFile* parent); + SisRC run(SISFile* file, uchar* buf, off_t len, SISFile* parent); /** * Ask the user which drive to install to. diff --git a/sisinstall/sismain.cpp b/sisinstall/sismain.cpp index 8516b0f..5a5b640 100644 --- a/sisinstall/sismain.cpp +++ b/sisinstall/sismain.cpp @@ -67,12 +67,19 @@ void main(int argc, char* argv[]) } createCRCTable(); SISFile sisFile; - sisFile.fillFrom(buf); - if (!dryrun) + SisRC rc = sisFile.fillFrom(buf, len); + if (rc == SIS_OK) { - SISInstaller installer; - installer.setPsion(psion); - installer.run(&sisFile, buf); + if (!dryrun) + { + SISInstaller installer; + installer.setPsion(psion); + installer.run(&sisFile, buf, len); + } + } + else + { + printf("Could not parse the sis file.\n"); } psion->disconnect(); -- cgit v1.2.3