Commit message: None
CVE: CVE-2018-7728
CWE: CWE-125
Vulnerability description: An issue was discovered in Exempi through 2.4.4. XMPFiles/source/FileHandlers/TIFF_Handler.cpp mishandles a case of a zero length, leading to a heap-based buffer over-read in the MD5Update() function in third-party/zuid/interfaces/MD5.cpp.

Project: exempi

void TIFF_MetaHandler::CacheFileData()
{
	XMP_IO*      fileRef    = this->parent->ioRef;
	XMP_PacketInfo & packetInfo = this->packetInfo;

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

	XMP_Assert ( ! this->containsXMP );

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

	this->tiffMgr.ParseFileStream ( fileRef );

	TIFF_Manager::TagInfo dngInfo;
	if ( this->tiffMgr.GetTag ( kTIFF_PrimaryIFD, kTIFF_DNGVersion, &dngInfo ) ) {


		XMP_Uns8 majorVersion = *((XMP_Uns8*)dngInfo.dataPtr);	// Start with DNGVersion.
		if ( this->tiffMgr.GetTag ( kTIFF_PrimaryIFD, kTIFF_DNGBackwardVersion, &dngInfo ) ) {
			majorVersion = *((XMP_Uns8*)dngInfo.dataPtr);	// Use DNGBackwardVersion if possible.
		}
		if ( majorVersion > 1 ) XMP_Throw ( "DNG version beyond 1.x", kXMPErr_BadTIFF );

	}

	TIFF_Manager::TagInfo xmpInfo;
	bool found = this->tiffMgr.GetTag ( kTIFF_PrimaryIFD, kTIFF_XMP, &xmpInfo );

	if ( found ) {

		this->packetInfo.offset    = this->tiffMgr.GetValueOffset ( kTIFF_PrimaryIFD, kTIFF_XMP );
		this->packetInfo.length    = xmpInfo.dataLen;
		this->packetInfo.padSize   = 0;				// Assume for now, set these properly in ProcessXMP.
		this->packetInfo.charForm  = kXMP_CharUnknown;
		this->packetInfo.writeable = true;

		this->xmpPacket.assign ( (XMP_StringPtr)xmpInfo.dataPtr, xmpInfo.dataLen );

		this->containsXMP = true;

	}

}	// TIFF_MetaHandler::CacheFileData

Project: exempi

bool TIFF_CheckFormat ( XMP_FileFormat format,
	                    XMP_StringPtr  filePath,
                        XMP_IO*    fileRef,
                        XMPFiles *     parent )
{
	IgnoreParam(format); IgnoreParam(filePath); IgnoreParam(parent);
	XMP_Assert ( format == kXMP_TIFFFile );

	enum { kMinimalTIFFSize = 4+4+2+12+4 };	// Header plus IFD with 1 entry.

	fileRef->Rewind ( );
	if ( ! XIO::CheckFileSpace ( fileRef, kMinimalTIFFSize ) ) return false;

	XMP_Uns8 buffer [4];
	fileRef->Read ( buffer, 4 );
	
	bool leTIFF = CheckBytes ( buffer, "\x49\x49\x2A\x00", 4 );
	bool beTIFF = CheckBytes ( buffer, "\x4D\x4D\x00\x2A", 4 );

	return (leTIFF | beTIFF);

}	// TIFF_CheckFormat

Project: exempi

TIFF_MetaHandler::TIFF_MetaHandler ( XMPFiles * _parent ) : psirMgr(0), iptcMgr(0)
{
	this->parent = _parent;
	this->handlerFlags = kTIFF_HandlerFlags;
	this->stdCharForm  = kXMP_Char8Bit;

}	// TIFF_MetaHandler::TIFF_MetaHandler

Project: exempi

XMPFileHandler * TIFF_MetaHandlerCTor ( XMPFiles * parent )
{
	return new TIFF_MetaHandler ( parent );

}	// TIFF_MetaHandlerCTor

Project: exempi

void TIFF_MetaHandler::UpdateFile ( bool doSafeUpdate )
{
	XMP_Assert ( ! doSafeUpdate );	// This should only be called for "unsafe" updates.

	XMP_IO*   destRef    = this->parent->ioRef;
	XMP_AbortProc abortProc  = this->parent->abortProc;
	void *        abortArg   = this->parent->abortArg;

	XMP_Int64 oldPacketOffset = this->packetInfo.offset;
	XMP_Int32 oldPacketLength = this->packetInfo.length;

	if ( oldPacketOffset == kXMPFiles_UnknownOffset ) oldPacketOffset = 0;	// ! Simplify checks.
	if ( oldPacketLength == kXMPFiles_UnknownLength ) oldPacketLength = 0;

	bool fileHadXMP = ((oldPacketOffset != 0) && (oldPacketLength != 0));


	ExportPhotoData ( kXMP_TIFFFile, &this->xmpObj, &this->tiffMgr, this->iptcMgr, this->psirMgr );

	try {
		XMP_OptionBits options = kXMP_UseCompactFormat;
		if ( fileHadXMP ) options |= kXMP_ExactPacketLength;
		this->xmpObj.SerializeToBuffer ( &this->xmpPacket, options, oldPacketLength );
	} catch ( ... ) {
		this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat );
	}


	bool doInPlace = (fileHadXMP && (this->xmpPacket.size() <= (size_t)oldPacketLength));
	if ( this->tiffMgr.IsLegacyChanged() ) doInPlace = false;
	
	bool localProgressTracking = false;
	XMP_ProgressTracker* progressTracker = this->parent->progressTracker;

	if ( ! doInPlace ) {

		#if GatherPerformanceData
			sAPIPerf->back().extraInfo += ", TIFF append update";
		#endif

		if ( (progressTracker != 0) && (! progressTracker->WorkInProgress()) ) {
			localProgressTracking = true;
			progressTracker->BeginWork();
		}

		this->tiffMgr.SetTag ( kTIFF_PrimaryIFD, kTIFF_XMP, kTIFF_UndefinedType, (XMP_Uns32)this->xmpPacket.size(), this->xmpPacket.c_str() );
		this->tiffMgr.UpdateFileStream ( destRef, progressTracker );

	} else {

		#if GatherPerformanceData
			sAPIPerf->back().extraInfo += ", TIFF in-place update";
		#endif

		if ( this->xmpPacket.size() < (size_t)this->packetInfo.length ) {
			size_t extraSpace = (size_t)this->packetInfo.length - this->xmpPacket.size();
			this->xmpPacket.append ( extraSpace, ' ' );
		}

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

		XMP_Assert ( this->xmpPacket.size() == (size_t)oldPacketLength );	// ! Done by common PutXMP logic.

		if ( progressTracker != 0 ) {
			if ( progressTracker->WorkInProgress() ) {
				progressTracker->AddTotalWork ( this->xmpPacket.size() );
			} else {
				localProgressTracking = true;
				progressTracker->BeginWork ( this->xmpPacket.size() );
			}
		}

		liveFile->Seek ( oldPacketOffset, kXMP_SeekFromStart  );
		liveFile->Write ( this->xmpPacket.c_str(), (XMP_Int32)this->xmpPacket.size() );

	}
	
	if ( localProgressTracking ) progressTracker->WorkComplete();
	this->needsUpdate = false;

}	// TIFF_MetaHandler::UpdateFile

Project: exempi

void TIFF_MetaHandler::WriteTempFile ( XMP_IO* tempRef )
{
	XMP_IO* origRef = this->parent->ioRef;

	XMP_AbortProc abortProc  = this->parent->abortProc;
	void *        abortArg   = this->parent->abortArg;

	XMP_Int64 fileLen = origRef->Length();
	if ( fileLen > 0xFFFFFFFFLL ) {	// Check before making a copy of the file.
		XMP_Throw ( "TIFF fles can't exceed 4GB", kXMPErr_BadTIFF );
	}
	
	XMP_ProgressTracker* progressTracker = this->parent->progressTracker;
	if ( progressTracker != 0 ) progressTracker->BeginWork ( (float)fileLen );

	origRef->Rewind ( );
	tempRef->Truncate ( 0 );
	XIO::Copy ( origRef, tempRef, fileLen, abortProc, abortArg );

	try {
		this->parent->ioRef = tempRef;	// ! Make UpdateFile update the temp.
		this->UpdateFile ( false );
		this->parent->ioRef = origRef;
	} catch ( ... ) {
		this->parent->ioRef = origRef;
		throw;
	}
	
	if ( progressTracker != 0 ) progressTracker->WorkComplete();

}	// TIFF_MetaHandler::WriteTempFile

Project: exempi

TIFF_MetaHandler::~TIFF_MetaHandler()
{

	if ( this->psirMgr != 0 ) delete ( this->psirMgr );
	if ( this->iptcMgr != 0 ) delete ( this->iptcMgr );

}	// TIFF_MetaHandler::~TIFF_MetaHandler