From 04e7620965694b05561028790eef5cb164981183 Mon Sep 17 00:00:00 2001 From: David Schmidt Date: Sun, 4 Sep 2011 20:21:40 +0000 Subject: [PATCH] Protect against some really badly corrupted AppleWorks files. This fix keeps us from spinning on 100% CPU when the buffer "length" (corrupt, negative) will never decrease. --- reformat/AppleWorks.cpp | 2225 ++++++++++++++++++++------------------- 1 file changed, 1118 insertions(+), 1107 deletions(-) diff --git a/reformat/AppleWorks.cpp b/reformat/AppleWorks.cpp index 44e768e..ebb911b 100644 --- a/reformat/AppleWorks.cpp +++ b/reformat/AppleWorks.cpp @@ -1,1107 +1,1118 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Convert AppleWorks 3.0 documents. - */ -#include "StdAfx.h" -#include "AppleWorks.h" - -/* - * =========================================================================== - * AppleWorks WP - * =========================================================================== - */ - -/* - * AppleWorks word processor file format, from FTN.1A.xxxx. - * - * The overall file format is: - * - * file header - * array of line records - * $ff $ff - * optional tags - */ - -/* - * Decide whether or not we want to handle this file. - */ -void -ReformatAWP::Examine(ReformatHolder* pHolder) -{ - ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot; - - if (pHolder->GetFileType() == kTypeAWP) - applies = ReformatHolder::kApplicProbably; - - pHolder->SetApplic(ReformatHolder::kReformatAWP, applies, - ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); -} - -/* - * Reformat an AppleWorks WP document. - */ -int -ReformatAWP::Process(const ReformatHolder* pHolder, - ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, - ReformatOutput* pOutput) -{ - const unsigned char* srcPtr = pHolder->GetSourceBuf(part); - long srcLen = pHolder->GetSourceLen(part); - long length = srcLen; - int retval = -1; - - bool skipRecord; - uchar lineRecCode, lineRecData; - - if (srcLen > 65536) - fUseRTF = false; - - //fUseRTF = false; - //fShowEmbeds = false; - - /* expect header plus EOF bytes at least */ - if (srcLen <= kFileHeaderSize) { - WMSG0(" AWP truncated?\n"); - goto bail; - } - - RTFBegin(kRTFFlagColorTable); - - /* - * Grab the file header. - */ - assert(sizeof(fFileHeader) == kFileHeaderSize); - - memcpy(&fFileHeader, srcPtr, sizeof(fFileHeader)); - srcPtr += sizeof(fFileHeader); - length -= sizeof(fFileHeader); - - /* do some quick sanity checks */ - if (fFileHeader.seventyNine != kSeventyNine) { - WMSG2("ERROR: expected %d in signature byte, found %d\n", - kSeventyNine, fFileHeader.seventyNine); - goto bail; - } - if (fFileHeader.sfMinVers && fFileHeader.sfMinVers != kSFMinVers30) { - WMSG1("WARNING: unexpected value %d for sfMinVers\n", - fFileHeader.sfMinVers); - /* keep going */ - } - - InitDocState(); - - /* if first line record is invalid, skip it */ - skipRecord = false; - if (fFileHeader.sfMinVers == kSFMinVers30) - skipRecord = true; - - /* set margins to 1.0 inches at 10cpi */ - RTFLeftMargin(10); - RTFRightMargin(10); - - /* - * Read the line records. - */ - while (1) { - lineRecData = Read8(&srcPtr, &length); - lineRecCode = Read8(&srcPtr, &length); - - if (length < 0) { - WMSG0(" AWP truncated file\n"); - goto bail; - } - - if (skipRecord) { - skipRecord = false; - continue; - } - - /* end of data reached? */ - if (lineRecData == kEOFMarker && lineRecCode == kEOFMarker) - break; - - if (ProcessLineRecord(lineRecData, lineRecCode, &srcPtr, &length) != 0) - { - WMSG0("ProcessLineRecord failed, bailing\n"); - goto bail; - } - } - - - /* - * Read the optional tags. - */ - /* (nah) */ - - RTFEnd(); - - SetResultBuffer(pOutput); - retval = 0; - -bail: - return retval; -} - -/* - * Initialize the DocState structure. - */ -void -ReformatAWP::InitDocState(void) -{ - memset(&fDocState, 0, sizeof(fDocState)); - fDocState.line = 1; -} - -/* - * Process a line record. - */ -int -ReformatAWP::ProcessLineRecord(uchar lineRecData, uchar lineRecCode, - const unsigned char** pSrcPtr, long* pLength) -{ - int err = 0; - - //WMSG2(" AWP line rec <0x%02x><0x%02x>\n", lineRecCode, lineRecData); - - if (lineRecCode == kLineRecordCarriageReturn) { - /* ignore the horizontal offset for now */ - RTFNewPara(); - } else if (lineRecCode == kLineRecordText) { - err = HandleTextRecord(lineRecData, pSrcPtr, pLength); - } else if (lineRecCode >= kLineRecordCommandMin && - lineRecCode <= kLineRecordCommandMax) - { - switch (lineRecCode) { - case kLineRecordCommandCenter: - RTFParaCenter(); - break; - case kLineRecordCommandRightJustify: - RTFParaRight(); - break; - case kLineRecordCommandUnjustify: - RTFParaLeft(); - break; - case kLineRecordCommandJustify: - RTFParaJustify(); - break; - case kLineRecordCommandLeftMargin: - RTFLeftMargin(lineRecData); - break; - case kLineRecordCommandRightMargin: - RTFRightMargin(lineRecData); - break; - - /* we handle these by showing them in the text */ - case kLineRecordCommandPageNumber: - if (fShowEmbeds) { - RTFSetColor(kColorBlue); - BufPrintf("", lineRecData); - RTFSetColor(kColorNone); - RTFNewPara(); - } - break; - case kLineRecordCommandPageHeader: - if (fShowEmbeds) { - RTFSetColor(kColorBlue); - BufPrintf(""); - RTFSetColor(kColorNone); - RTFNewPara(); - } - break; - case kLineRecordCommandPageHeaderEnd: - if (fShowEmbeds) { - RTFSetColor(kColorBlue); - BufPrintf(""); - RTFSetColor(kColorNone); - RTFNewPara(); - } - break; - case kLineRecordCommandPageFooter: - if (fShowEmbeds) { - RTFSetColor(kColorBlue); - BufPrintf(""); - RTFSetColor(kColorNone); - RTFNewPara(); - } - break; - case kLineRecordCommandPageFooterEnd: - if (fShowEmbeds) { - RTFSetColor(kColorBlue); - BufPrintf(""); - RTFSetColor(kColorNone); - RTFNewPara(); - } - break; - case kLineRecordCommandNewPage: - if (fUseRTF) - RTFPageBreak(); - else if (fShowEmbeds) { - RTFSetColor(kColorBlue); // won't do anything - BufPrintf(""); - RTFSetColor(kColorNone); - } - break; - - case kLineRecordCommandPlatenWidth: - case kLineRecordCommandCharsPerInch: - case kLineRecordCommandProportional1: - case kLineRecordCommandProportional2: - case kLineRecordCommandIndent: - case kLineRecordCommandPaperLength: - case kLineRecordCommandTopMargin: - case kLineRecordCommandBottomMargin: - case kLineRecordCommandLinesPerInch: - case kLineRecordCommandSingleSpace: - case kLineRecordCommandDoubleSpace: - case kLineRecordCommandTripleSpace: - case kLineRecordCommandGroupBegin: - case kLineRecordCommandGroupEnd: - case kLineRecordCommandSkipLines: - case kLineRecordCommandPauseEachPage: - case kLineRecordCommandPauseHere: - case kLineRecordCommandSetMarker: - case kLineRecordCommandSetPageNumber: - case kLineRecordCommandPageBreak: - case kLineRecordCommandPageBreak256: - case kLineRecordCommandPageBreakPara: - case kLineRecordCommandPageBreakPara256: - default: - WMSG2(" AWP cmd <0x%02x><0x%02x>\n", lineRecCode, lineRecData); - break; - } - } else { - /* bad command */ - WMSG2("WARNING: unrecognized code 0x%02x at 0x%08lx\n", lineRecCode, - *pSrcPtr); - fDocState.softFailures++; - if (fDocState.softFailures > kMaxSoftFailures) { - WMSG0("ERROR: too many failures, giving up\n"); - err = -1; - } - } - - return err; -} - -/* - * Handle a text record. The first two bytes are flags, the rest is - * either the text or a ruler. Special codes may be embedded in the text. - * - * "lineRecData" has the number of bytes of input that we have yet to read. - */ -int -ReformatAWP::HandleTextRecord(uchar lineRecData, - const unsigned char** pSrcPtr, long* pLength) -{ - int err = 0; - uchar tabFlags; - uchar byteCountPlusCR; - int byteCount = lineRecData; - bool noOutput = false; - int ic; - - tabFlags = Read8(pSrcPtr, pLength); - byteCount--; - byteCountPlusCR = Read8(pSrcPtr, pLength); - byteCount--; - if (*pLength < 0) { - err = -1; - goto bail; - } - - if (byteCount <= 0) { - WMSG2("WARNING: line %ld: short line (%d)\n", - fDocState.line, byteCount); - /* this is bad, but keep going anyway */ - } - - if ((byteCountPlusCR & ~kCRatEOL) != byteCount) { - WMSG3("WARNING: line %ld: byteCount now %d, offset 3 count %d\n", - fDocState.line, byteCount, byteCountPlusCR & ~kCRatEOL); - /* not sure why this would legally happen */ - } - - if (tabFlags == kTabFlagsIsRuler) - noOutput = true; - - while (byteCount--) { - ic = Read8(pSrcPtr, pLength); - if (*pLength < 0) { - err = -1; - goto bail; - } - - if (noOutput) - continue; - - if (ic < kMinTextChar) { - switch (ic) { - case kSpecialCharBoldBegin: - RTFBoldOn(); - break; - case kSpecialCharBoldEnd: - RTFBoldOff(); - break; - case kSpecialCharSuperscriptBegin: - RTFSuperscriptOn(); - break; - case kSpecialCharSuperscriptEnd: - RTFSuperscriptOff(); - break; - case kSpecialCharSubscriptBegin: - RTFSubscriptOn(); - break; - case kSpecialCharSubscriptEnd: - RTFSubscriptOff(); - break; - case kSpecialCharUnderlineBegin: - RTFUnderlineOn(); - break; - case kSpecialCharUnderlineEnd: - RTFUnderlineOff(); - break; - case kSpecialCharEnterKeyboard: - if (fShowEmbeds) { - RTFSetColor(kColorBlue); - BufPrintf(""); - RTFSetColor(kColorNone); - } - break; - case kSpecialCharPrintPageNumber: - if (fShowEmbeds) { - RTFSetColor(kColorBlue); - BufPrintf(""); - RTFSetColor(kColorNone); - } - break; - case kSpecialCharStickySpace: - /* MSWord uses "\~", but RichEdit ignores that */ - BufPrintf(" "); - break; - case kSpecialCharMailMerge: - if (fShowEmbeds) { - RTFSetColor(kColorBlue); - BufPrintf(""); - RTFSetColor(kColorNone); - } - case kSpecialCharPrintDate: - if (fShowEmbeds) { - RTFSetColor(kColorBlue); - BufPrintf(""); - RTFSetColor(kColorNone); - } - break; - case kSpecialCharPrintTime: - if (fShowEmbeds) { - RTFSetColor(kColorBlue); - BufPrintf("