Commit message: None
CVE: CVE-2018-7729
CWE: CWE-125
Vulnerability description: An issue was discovered in Exempi through 2.4.4. There is a stack-based buffer over-read in the PostScript_MetaHandler::ParsePSFile() function in XMPFiles/source/FileHandlers/PostScript_Handler.cpp.

Project: exempi

bool PostScript_MetaHandler::ExtractContainsXMPHint(IOBuffer &ioBuf,XMP_Int64 containsXMPStartpos)
{
	XMP_IO* fileRef = this->parent->ioRef;
	int xmpHint = kPSHint_NoMain;	// ! From here on, a failure means "no main", not "no marker".
	if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
	if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) return false;
	if ( ! PostScript_Support::SkipTabsAndSpaces(fileRef,ioBuf) ) return false;
	if ( IsNewline ( *ioBuf.ptr ) ) return false;	// Reached the end of the ContainsXMP comment.

	if ( ! CheckFileSpace ( fileRef, &ioBuf, 6 ) ) return false ;

	if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("NoMain"), 6 ) ) 
	{
		ioBuf.ptr += 6;
		if ( ! PostScript_Support::SkipTabsAndSpaces(fileRef,ioBuf) ) return false;
		if ( ! IsNewline( *ioBuf.ptr) ) return false;
		this->psHint = kPSHint_NoMain;
		setTokenInfo(kPS_ADOContainsXMP,containsXMPStartpos,ioBuf.filePos+ioBuf.ptr-ioBuf.data-containsXMPStartpos);

	} 
	else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("MainFi"), 6 ) ) 
	{
		ioBuf.ptr += 6;
		if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false;
		if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("rst"), 3 ) ) 
		{
			ioBuf.ptr += 3;
			if ( ! PostScript_Support::SkipTabsAndSpaces(fileRef,ioBuf) ) return false;
			if ( ! IsNewline( *ioBuf.ptr) ) return false;
			this->psHint = kPSHint_MainFirst;
			setTokenInfo(kPS_ADOContainsXMP,containsXMPStartpos,ioBuf.filePos+ioBuf.ptr-ioBuf.data-containsXMPStartpos);
			containsXMPHint=true;
		}
	} 
	else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("MainLa"), 6 ) ) 
	{
		ioBuf.ptr += 6;
		if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return false;
		if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("st"), 2 ) ) {
			ioBuf.ptr += 2;
			if ( ! PostScript_Support::SkipTabsAndSpaces(fileRef,ioBuf) ) return false;
			if ( ! IsNewline( *ioBuf.ptr) ) return false;
			this->psHint = kPSHint_MainLast;
			setTokenInfo(kPS_ADOContainsXMP,containsXMPStartpos,ioBuf.filePos+ioBuf.ptr-ioBuf.data-containsXMPStartpos);
			containsXMPHint=true;
		}
	} 
	else 
	{
		if ( ! PostScript_Support::SkipUntilNewline(fileRef,ioBuf) ) return false;
	}
	return true;
}

Project: exempi

bool PostScript_MetaHandler::ExtractDSCCommentValue(IOBuffer &ioBuf,NativeMetadataIndex index)
{
	
	XMP_IO* fileRef = this->parent->ioRef;
	if ( ! PostScript_Support::SkipTabsAndSpaces(fileRef,ioBuf) ) return false;
	if ( !IsNewline ( *ioBuf.ptr ) )
	{
		do 
		{
			if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
			nativeMeta[index] += *ioBuf.ptr;
			++ioBuf.ptr;
		} while ( ! IsNewline ( *ioBuf.ptr) );
		if (!PostScript_Support::HasCodesGT127(nativeMeta[index]))
		{
			dscFlags|=nativeIndextoFlag[index];
		}
		else
			nativeMeta[index].clear();
	}
	return true;
}

Project: exempi

bool PostScript_MetaHandler::ExtractDocInfoDict(IOBuffer &ioBuf)
{
	XMP_Uns8 ch;
	XMP_IO* fileRef = this->parent->ioRef;
	XMP_Int64 endofDocInfo=(ioBuf.ptr-ioBuf.data)+ioBuf.filePos;
	if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
	if ( IsWhitespace (*ioBuf.ptr))
	{
		if ( ! ( PostScript_Support::SkipTabsAndSpaces(fileRef, ioBuf))) return false;
		if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsPdfmarkString.length() ) ) return false;
		if ( ! CheckBytes ( ioBuf.ptr,  Uns8Ptr(kPSContainsPdfmarkString.c_str()), kPSContainsPdfmarkString.length() ) ) return false;
		while(true)
		{
			if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
			ch=*ioBuf.ptr;
			--ioBuf.ptr;
			if (ch=='/') break;//slash of /DOCINFO
		} 
		while(true)
		{
			if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
			if (!IsWhitespace(*ioBuf.ptr)) break;
			--ioBuf.ptr;
		} 
						
		bool findingkey=false;
		std::string key, value;
		while(true)
		{
			XMP_Uns32 noOfMarks=0;
			if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
			if (*ioBuf.ptr==')')
			{
				--ioBuf.ptr;
				while(true)
				{
					if (*ioBuf.ptr=='(')
					{
						if(findingkey)
						{
							reverse(key.begin(), key.end());
							reverse(value.begin(), value.end());
							RegisterKeyValue(key,value);
						}
						if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
						--ioBuf.ptr;
						findingkey=!findingkey;
						break;
					}
					else
					{
						if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
						if (findingkey)
							key+=*ioBuf.ptr;
						else
							value+=*ioBuf.ptr;
						--ioBuf.ptr;
					}
				}
			}
			else if(*ioBuf.ptr=='[')
			{
				break;
			}
			else
			{
				while(true)
				{
					if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
					if (findingkey)
						key+=*ioBuf.ptr;
					else
						value+=*ioBuf.ptr;
					--ioBuf.ptr;
					if (*ioBuf.ptr=='/')
					{
						if(findingkey)
						{
							reverse(key.begin(), key.end());
							reverse(value.begin(), value.end());
							RegisterKeyValue(key,value);
						}
						if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
						--ioBuf.ptr;
						findingkey=!findingkey;
						break;
					}
					else if(IsWhitespace(*ioBuf.ptr))
					{
						break;
					}
				}
			}
			while(true)
			{
				if ( ! PostScript_Support::RevCheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
				if (!IsWhitespace(*ioBuf.ptr)) break;
				--ioBuf.ptr;
			} 
						
		}
						
		fileRef->Rewind();	
		FillBuffer (fileRef, endofDocInfo, &ioBuf );
		return true;
	}//white space not after DOCINFO
	return false;
}

Project: exempi

bool PostScript_MetaHandler::FindFirstPacket()
{
	int		snipCount;
	bool 	found	= false;
	size_t	bufPos, bufLen;

	XMP_IO* fileRef = this->parent->ioRef;
	XMP_Int64   fileLen = fileRef->Length();
	XMP_PacketInfo & packetInfo = this->packetInfo;

	XMPScanner scanner ( fileLen );
	XMPScanner::SnipInfoVector snips;

	enum { kBufferSize = 64*1024 };
	XMP_Uns8	buffer [kBufferSize];

	XMP_AbortProc abortProc  = this->parent->abortProc;
	void *        abortArg   = this->parent->abortArg;
	const bool    checkAbort = (abortProc != 0);

	bufPos = 0;
	bufLen = 0;

	fileRef->Rewind();	// Seek back to the beginning of the file.
	bool firstfound=false;

	while ( true ) 
	{

		if ( checkAbort && abortProc(abortArg) ) 
		{
			XMP_Throw ( "PostScript_MetaHandler::FindFirstPacket - User abort", kXMPErr_UserAbort );
		}

		bufPos += bufLen;
		bufLen = fileRef->Read ( buffer, kBufferSize );
		if ( bufLen == 0 ) return firstfound;	// Must be at EoF, no packets found.

		scanner.Scan ( buffer, bufPos, bufLen );
		snipCount = scanner.GetSnipCount();
		scanner.Report ( snips );
		for ( int i = 0; i < snipCount; ++i ) 
		{
			if ( snips[i].fState == XMPScanner::eValidPacketSnip ) 
			{
				if (!firstfound)
				{
					if ( snips[i].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindFirstPacket: Oversize packet", kXMPErr_BadXMP );
					packetInfo.offset = snips[i].fOffset;
					packetInfo.length = (XMP_Int32)snips[i].fLength;
					packetInfo.charForm  = snips[i].fCharForm;
					packetInfo.writeable = (snips[i].fAccess == 'w');
					firstPacketInfo=packetInfo;
					lastPacketInfo=packetInfo;
					firstfound=true;
				}
				else
				{					
					lastPacketInfo.offset = snips[i].fOffset;
					lastPacketInfo.length = (XMP_Int32)snips[i].fLength;
					lastPacketInfo.charForm  = snips[i].fCharForm;
					lastPacketInfo.writeable = (snips[i].fAccess == 'w');
				}
			}
		}

	}
	
	return firstfound;

}	// FindFirstPacket

Project: exempi

bool PostScript_MetaHandler::FindLastPacket()
{
	size_t	bufPos, bufLen;

	XMP_IO* fileRef = this->parent->ioRef;
	XMP_Int64   fileLen = fileRef->Length();
	XMP_PacketInfo & packetInfo = this->packetInfo;


	XMPScanner	scanner ( fileLen );

	enum { kBufferSize = 64*1024 };
	XMP_Uns8	buffer [kBufferSize];

	XMP_AbortProc abortProc  = this->parent->abortProc;
	void *        abortArg   = this->parent->abortArg;
	const bool    checkAbort = (abortProc != 0);

	fileRef->Rewind();	// Seek back to the beginning of the file.

	for ( bufPos = 0; bufPos < (size_t)fileLen; bufPos += bufLen )
	{
		if ( checkAbort && abortProc(abortArg) ) 
		{
			XMP_Throw ( "PostScript_MetaHandler::FindLastPacket - User abort", kXMPErr_UserAbort );
		}
		bufLen = fileRef->Read ( buffer, kBufferSize );
		if ( bufLen == 0 ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Read failure", kXMPErr_ExternalFailure );
		scanner.Scan ( buffer, bufPos, bufLen );
	}


	int snipCount = scanner.GetSnipCount();

	XMPScanner::SnipInfoVector snips ( snipCount );
	scanner.Report ( snips );

	bool lastfound=false;
	for ( int i = 0; i < snipCount; ++i ) 
	{
		if ( snips[i].fState == XMPScanner::eValidPacketSnip ) 
		{
			if (!lastfound)
			{
				if ( snips[i].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Oversize packet", kXMPErr_BadXMP );
				packetInfo.offset = snips[i].fOffset;
				packetInfo.length = (XMP_Int32)snips[i].fLength;
				packetInfo.charForm  = snips[i].fCharForm;
				packetInfo.writeable = (snips[i].fAccess == 'w');
				firstPacketInfo=packetInfo;
				lastPacketInfo=packetInfo;
				lastfound=true;
			}
			else
			{					
				lastPacketInfo.offset = snips[i].fOffset;
				lastPacketInfo.length = (XMP_Int32)snips[i].fLength;
				lastPacketInfo.charForm  = snips[i].fCharForm;
				lastPacketInfo.writeable = (snips[i].fAccess == 'w');
				packetInfo=lastPacketInfo;
			}
		}
	}
	return lastfound;

}	// PostScript_MetaHandler::FindLastPacket

Project: exempi

int PostScript_MetaHandler::FindPostScriptHint()
{
	bool     found = false;
	IOBuffer ioBuf;
	XMP_Uns8 ch;

	XMP_IO* fileRef = this->parent->ioRef;

	XMP_AbortProc abortProc  = this->parent->abortProc;
	void *        abortArg   = this->parent->abortArg;
	const bool    checkAbort = (abortProc != 0);


	fileRef->Rewind();
	if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false;
	XMP_Uns32 fileheader = GetUns32BE ( ioBuf.ptr );

	if ( fileheader == 0xC5D0D3C6 ) {

		if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return false;

		XMP_Uns32 psOffset = GetUns32LE ( ioBuf.ptr+4 );	// PostScript offset.
		XMP_Uns32 psLength = GetUns32LE ( ioBuf.ptr+8 );	// PostScript length.

		MoveToOffset ( fileRef, psOffset, &ioBuf );

	}


	while ( true ) {

		if ( checkAbort && abortProc(abortArg) ) {
			XMP_Throw ( "PostScript_MetaHandler::FindPostScriptHint - User abort", kXMPErr_UserAbort );
		}

		if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsXMPString.length() ) ) return kPSHint_NoMarker;

		if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSEndCommentString.c_str()), kPSEndCommentString.length() ) ) {

			return kPSHint_NoMarker;

		} else if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsXMPString.c_str()), kPSContainsXMPString.length() ) ) {

			do {
				if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMarker;
				ch = *ioBuf.ptr;
				++ioBuf.ptr;
			} while ( ! IsNewline ( ch ) );

		} else {


			ioBuf.ptr += kPSContainsXMPString.length();
			int xmpHint = kPSHint_NoMain;	// ! From here on, a failure means "no main", not "no marker".
			if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain;
			if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) return kPSHint_NoMain;

			while ( true ) {

				while ( true ) {	// Skip leading spaces and tabs.
					if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain;
					if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) break;
					++ioBuf.ptr;
				}
				if ( IsNewline ( *ioBuf.ptr ) ) return kPSHint_NoMain;	// Reached the end of the ContainsXMP comment.

				if ( ! CheckFileSpace ( fileRef, &ioBuf, 6 ) ) return kPSHint_NoMain;

				if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("NoMain"), 6 ) ) {

					ioBuf.ptr += 6;
					xmpHint = kPSHint_NoMain;
					break;

				} else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("MainFi"), 6 ) ) {

					ioBuf.ptr += 6;
					if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return kPSHint_NoMain;
					if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("rst"), 3 ) ) {
						ioBuf.ptr += 3;
						xmpHint = kPSHint_MainFirst;
					}
					break;

				} else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("MainLa"), 6 ) ) {

					ioBuf.ptr += 6;
					if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return kPSHint_NoMain;
					if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("st"), 2 ) ) {
						ioBuf.ptr += 2;
						xmpHint = kPSHint_MainLast;
					}
					break;

				} else {

					while ( true ) {	// Skip until whitespace.
						if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain;
						if ( IsWhitespace ( *ioBuf.ptr ) ) break;
						++ioBuf.ptr;
					}

				}

			}	// Look for the main packet location option.

			if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain;
			if ( ! IsWhitespace ( *ioBuf.ptr ) ) return kPSHint_NoMain;
			return xmpHint;

		}	// Found "%ADO_ContainsXMP:".

	}	// Outer marker loop.

	return kPSHint_NoMarker;	// Should never reach here.

}	// PostScript_MetaHandler::FindPostScriptHint

Project: exempi

bool PostScript_CheckFormat ( XMP_FileFormat format,
	                          XMP_StringPtr  filePath,
	                          XMP_IO*    fileRef,
	                          XMPFiles *     parent )
{
	IgnoreParam(filePath); IgnoreParam(parent);
	XMP_Assert ( (format == kXMP_EPSFile) || (format == kXMP_PostScriptFile) );

	return PostScript_Support::IsValidPSFile(fileRef,format) ;

}	// PostScript_CheckFormat

Project: exempi

PostScript_MetaHandler::PostScript_MetaHandler ( XMPFiles * _parent ):dscFlags(0),docInfoFlags(0)
	,containsXMPHint(false),fileformat(kXMP_UnknownFile)
{
	this->parent = _parent;
	this->handlerFlags = kPostScript_HandlerFlags;
	this->stdCharForm = kXMP_Char8Bit;
	this->psHint = kPSHint_NoMarker;

}	// PostScript_MetaHandler::PostScript_MetaHandler

Project: exempi

XMPFileHandler * PostScript_MetaHandlerCTor ( XMPFiles * parent )
{
	XMPFileHandler * newHandler = new PostScript_MetaHandler ( parent );

	return newHandler;

}	// PostScript_MetaHandlerCTor

Project: exempi

PostScript_MetaHandler::TokenLocation& PostScript_MetaHandler::getTokenInfo(TokenFlag tFlag)
{
	if ((docInfoFlags&tFlag)&&tFlag>=kPS_ADOContainsXMP && tFlag<=kPS_EndPostScript)
	{
		size_t index=0;
		XMP_Uns64 flag=tFlag;
		while(flag>>=1) index++;
		return fileTokenInfo[index];
	}
	return fileTokenInfo[kPS_NoData];
}

Project: exempi

void PostScript_MetaHandler::setTokenInfo(TokenFlag tFlag,XMP_Int64 offset,XMP_Int64 length)
{
	if (!(docInfoFlags&tFlag)&&tFlag>=kPS_ADOContainsXMP && tFlag<=kPS_EndPostScript)
	{
		size_t index=0;
		XMP_Uns64 flag=tFlag;
		while(flag>>=1) index++;
		fileTokenInfo[index].offsetStart=offset;
		fileTokenInfo[index].tokenlen=length;
		docInfoFlags|=tFlag;
	}
}

Project: exempi

PostScript_MetaHandler::~PostScript_MetaHandler()
{

}	// PostScript_MetaHandler::~PostScript_MetaHandler