Backport of:

From 9c78968498f3321bd50c817bae585bb0936e5e08 Mon Sep 17 00:00:00 2001
From: arpitapanda05 <79362772+arpitapanda05@users.noreply.github.com>
Date: Thu, 15 Jul 2021 22:39:17 +0530
Subject: Security Fixes and Exif Time Zone Metadata Support

Co-authored-by: prigupta <prigupta@adobe.com>
---
 XMPCore/source/XMPUtils-FileInfo.cpp               |   9 +-
 XMPFiles/source/FileHandlers/FLV_Handler.cpp       |   2 +
 XMPFiles/source/FileHandlers/P2_Handler.cpp        |  38 ++++---
 XMPFiles/source/FileHandlers/PSD_Handler.cpp       |  14 ++-
 XMPFiles/source/FileHandlers/SWF_Handler.cpp       |  10 ++
 XMPFiles/source/FileHandlers/TIFF_Handler.cpp      |   2 +-
 XMPFiles/source/FileHandlers/UCF_Handler.cpp       |   7 +-
 XMPFiles/source/FileHandlers/UCF_Handler.hpp       |   1 +
 XMPFiles/source/FormatSupport/ASF_Support.cpp      |  11 +-
 XMPFiles/source/FormatSupport/ID3_Support.cpp      |   2 +-
 XMPFiles/source/FormatSupport/P2_Support.cpp       |   9 +-
 XMPFiles/source/FormatSupport/PNG_Support.cpp      |   2 +-
 .../source/FormatSupport/PostScript_Support.cpp    |   6 +-
 XMPFiles/source/FormatSupport/RIFF.cpp             |   4 +
 XMPFiles/source/FormatSupport/ReconcileLegacy.hpp  |  24 ++--
 XMPFiles/source/FormatSupport/ReconcileTIFF.cpp    | 121 +++++++++++++++++----
 XMPFiles/source/FormatSupport/SVG_Adapter.cpp      |   8 +-
 XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp  |  36 +++---
 .../source/FormatSupport/TIFF_MemoryReader.cpp     |   8 +-
 XMPFiles/source/FormatSupport/TIFF_Support.hpp     |  30 ++++-
 .../source/PluginHandler/FileHandlerInstance.cpp   |   5 +-
 XMPFiles/source/WXMPFiles.cpp                      |  13 ++-
 XMPFiles/source/XMPFiles.cpp                       |   6 +-
 public/include/TXMPFiles.hpp                       |   3 +-
 24 files changed, 264 insertions(+), 107 deletions(-)

--- a/XMPCore/source/XMPUtils-FileInfo.cpp
+++ b/XMPCore/source/XMPUtils-FileInfo.cpp
@@ -782,9 +782,12 @@ AppendSubtree ( const XMP_Node * sourceN
 		for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim && destNode!= NULL; ++sourceNum ) {
 			const XMP_Node * sourceField = sourceNode->children[sourceNum];
 			AppendSubtree ( sourceField, destNode, mergeCompound, replaceOld, deleteEmpty );
-			if ( deleteEmpty && destNode->children.empty() ) {
-				delete ( destNode );
-				destParent->children.erase ( destPos );
+
+			if (deleteEmpty && destNode->children.empty())
+			{
+				delete (destNode);
+				destNode = NULL;
+				destParent->children.erase(destPos);
 			}
 		}
 		
--- a/XMPFiles/source/FileHandlers/FLV_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/FLV_Handler.cpp
@@ -210,6 +210,8 @@ static void GetTagInfo ( XMP_IO* fileRef
 
 static XMP_Uns32 GetASValueLen ( const XMP_Uns8 * asValue, const XMP_Uns8 * asLimit )
 {
+	if (asValue > asLimit)
+		return 0;
 	XMP_Uns32 valueLen = 0;
 	const XMP_Uns8 * itemPtr;
 	XMP_Uns32 arrayCount;
--- a/XMPFiles/source/FileHandlers/P2_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/P2_Handler.cpp
@@ -1129,6 +1129,7 @@ void P2_MetaHandler::ProcessXMP()
 	XML_NodePtr legacyContext, clipMetadata, legacyProp;
 	if ( ! this->p2ClipManager.IsValidP2() ) return;
 	P2_Clip* p2Clip=this->p2ClipManager.GetManagedClip();
+	if( p2Clip->GetP2RootNode() == 0) return;
 	XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str();
 	std::string oldDigest, newDigest;
 	bool digestFound = this->xmpObj.GetStructField ( kXMP_NS_XMP, "NativeDigests", kXMP_NS_XMP, "P2", &oldDigest, 0 );
@@ -1299,24 +1300,27 @@ void P2_MetaHandler::UpdateFile ( bool d
 			if (frameFormat == "50Timecode" || frameFormat == "5994DropTimecode" || frameFormat == "5994NonDropTimecode")
 			{
 				p2Clip = this->p2ClipManager.GetManagedClip();
-				XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str();
-				XML_NodePtr legacyVideoContext = p2Clip->GetEssenceListNode();
-				if (legacyVideoContext != 0)
+				if( p2Clip->GetP2RootNode() != 0 )
 				{
-					legacyVideoContext = legacyVideoContext->GetNamedElement(p2NS, "Video");
-					XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement(p2NS, "StartTimecode");
-					if ((legacyProp != 0) && legacyProp->IsLeafContentNode())
-					{
-						AdjustTimeCode( xmpStartTimeCode, true );
-						if (xmpStartTimeCode != legacyProp->GetLeafContentValue())
-						{
-							legacyProp->SetLeafContentValue(xmpStartTimeCode.c_str());
-							updateLegacyXML = true;
-						}
-					}
-				}
-			}
-		}
+				    XMP_StringPtr p2NS = p2Clip->GetP2RootNode()->ns.c_str();
+				    XML_NodePtr legacyVideoContext = p2Clip->GetEssenceListNode();
+				    if (legacyVideoContext != 0)
+				   {
+					   legacyVideoContext = legacyVideoContext->GetNamedElement(p2NS, "Video");
+					    XML_NodePtr legacyProp = legacyVideoContext->GetNamedElement(p2NS, "StartTimecode");
+					    if ((legacyProp != 0) && legacyProp->IsLeafContentNode())
+					    {
+						    AdjustTimeCode( xmpStartTimeCode, true );
+						    if (xmpStartTimeCode != legacyProp->GetLeafContentValue())
+						    {
+							   legacyProp->SetLeafContentValue(xmpStartTimeCode.c_str());
+							   updateLegacyXML = true;
+						    }
+					    }
+				   }
+			    }
+		    }
+	    }
 
 		std::string newDigest;
 		this->p2ClipManager.GetManagedClip()->CreateDigest ( &newDigest );
--- a/XMPFiles/source/FileHandlers/PSD_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/PSD_Handler.cpp
@@ -140,7 +140,13 @@ void PSD_MetaHandler::CacheFileData()
 
 	cmLen = GetUns32BE ( &psdHeader[26] );
 
-	XMP_Int64 psirOrigin = 26 + 4 + cmLen;
+	XMP_Int64 psirOrigin = 26 + 4 + static_cast<XMP_Int64>(cmLen);
+	XMP_Int64 fileLength = fileRef->Length();
+
+	if (psirOrigin > fileLength)
+	{
+		XMP_Throw("Invalid PSD chunk length", kXMPErr_BadPSD);
+	}
 
 	filePos = fileRef->Seek ( psirOrigin, kXMP_SeekFromStart  );
 	if ( filePos !=  psirOrigin ) return;	// Throw?
@@ -151,7 +157,9 @@ void PSD_MetaHandler::CacheFileData()
 	this->psirMgr.ParseFileResources ( fileRef, psirLen );
 
 	PSIR_Manager::ImgRsrcInfo xmpInfo;
-	bool found = this->psirMgr.GetImgRsrc ( kPSIR_XMP, &xmpInfo );
+	bool found = this->psirMgr.GetImgRsrc(kPSIR_XMP, &xmpInfo);
+	if (psirLen < xmpInfo.dataLen)
+		return;
 
 	if ( found ) {
 
@@ -425,4 +433,4 @@ void PSD_MetaHandler::WriteTempFile ( XM
 
 }	// PSD_MetaHandler::WriteTempFile
 
-// =================================================================================================
+// =================================================================================================
\ No newline at end of file
--- a/XMPFiles/source/FileHandlers/SWF_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/SWF_Handler.cpp
@@ -139,6 +139,7 @@ void SWF_MetaHandler::CacheFileData() {
 	
 	// Look for the FileAttributes and Metadata tags.
 	
+	if(this->expandedSize <= SWF_IO::HeaderPrefixSize ) return; // Throw?
 	this->firstTagOffset = SWF_IO::FileHeaderSize ( this->expandedSWF[SWF_IO::HeaderPrefixSize] );
 	
 	XMP_Uns32 currOffset = this->firstTagOffset;
@@ -235,6 +236,9 @@ void SWF_MetaHandler::UpdateFile ( bool
 		PutUns16LE ( ((SWF_IO::FileAttributesTagID << 6) | 4), &buffer[0] );
 		PutUns32LE ( SWF_IO::HasMetadataMask, &buffer[2] );
 		
+		if(this->expandedSWF.size() < this->firstTagOffset ){
+                     XMP_Throw ( "Index not valid.Invalid SWF, can't update.", kXMPErr_BadIndex );
+                 }
 		this->expandedSWF.insert ( (this->expandedSWF.begin() + this->firstTagOffset), 6, 0 );
 		memcpy ( &this->expandedSWF[this->firstTagOffset], &buffer[0], 6 );
 
@@ -271,6 +275,9 @@ void SWF_MetaHandler::UpdateFile ( bool
 				this->metadataTag.tagOffset += attrTagLength;	// The FileAttributes tag will become in front.
 			}
 			
+			if(this->expandedSWF.size() < this->firstTagOffset ){
+                             XMP_Throw ( "Index not valid.Invalid SWF, can't update.", kXMPErr_BadIndex );
+                        }
 			this->expandedSWF.insert ( (this->expandedSWF.begin() + this->firstTagOffset), attrTagLength, 0 );
 			memcpy ( &this->expandedSWF[this->firstTagOffset], &attrTag[0], attrTagLength );
 			
@@ -301,6 +308,9 @@ void SWF_MetaHandler::UpdateFile ( bool
 	this->metadataTag.contentLength = this->xmpPacket.size();
 
 	XMP_Uns32 newMetaLength = 6 + this->metadataTag.contentLength;	// Always use a long tag header.
+	if(this->expandedSWF.size() < this->metadataTag.tagOffset ){
+             XMP_Throw ( "Index not valid.Invalid SWF, can't update.", kXMPErr_BadIndex );
+	}
 	this->expandedSWF.insert ( (this->expandedSWF.begin() + this->metadataTag.tagOffset), newMetaLength, 0 );
 
 	PutUns16LE ( ((SWF_IO::MetadataTagID << 6) | SWF_IO::TagLengthMask), &this->expandedSWF[this->metadataTag.tagOffset] );
--- a/XMPFiles/source/FileHandlers/UCF_Handler.cpp
+++ b/XMPFiles/source/FileHandlers/UCF_Handler.cpp
@@ -486,6 +486,9 @@ void UCF_MetaHandler::CacheFileData()
 					}
 
 					have = CHUNK - strm.avail_out;
+					if ((bytesWritten + have) > sizeUncompressed){
+						XMP_Throw("UCF Bad XMP block", kXMPErr_BadBlockFormat);
+					}
 					memcpy( (unsigned char*) packetStr + bytesWritten , out , have );
 					bytesWritten += have;
 
@@ -547,7 +550,6 @@ void UCF_MetaHandler::UpdateFile ( bool
 	uncomprPacketLen = (XMP_StringLen) xmpPacket.size();
 	finalPacketStr = uncomprPacketStr;		// will be overriden if compressedXMP==true
 	finalPacketLen = uncomprPacketLen;
-	std::string compressedPacket;	// moot if non-compressed, still here for scope reasons (having to keep a .c_str() alive)
 
 	if ( !x ) // if new XMP...
 	{
@@ -584,6 +586,9 @@ void UCF_MetaHandler::UpdateFile ( bool
 		unsigned int have;
 		z_stream strm;
 		unsigned char out[CHUNK];
+		/* initilalisation for fix to CTECHXMP-4170441*/
+		strm.total_out = 0;
+		strm.total_in = 0;
 
 		/* allocate deflate state */
 		strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL;
--- a/XMPFiles/source/FileHandlers/UCF_Handler.hpp
+++ b/XMPFiles/source/FileHandlers/UCF_Handler.hpp
@@ -710,6 +710,7 @@ private:
 	XMP_StringLen uncomprPacketLen;
 	XMP_StringPtr finalPacketStr;
 	XMP_StringLen finalPacketLen;
+	std::string compressedPacket;
 	std::vector<CDFileHeader> cdEntries;
 	EndOfCD endOfCD;
 	void writeOut( XMP_IO* sourceFile, XMP_IO* targetFile, bool isRewrite, bool isInPlace);
--- a/XMPFiles/source/FormatSupport/ASF_Support.cpp
+++ b/XMPFiles/source/FormatSupport/ASF_Support.cpp
@@ -145,10 +145,11 @@ bool ASF_Support::ReadHeaderObject ( XMP
 		pos += bufferSize;
 
 		// read contained header objects
-		/*XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] );*/
+		XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] );
 		ASF_ObjectBase objectBase;
 
-		while ( read < newObject.len ) {
+		while (read < newObject.len && numberOfHeaders > 0)
+		{
 
 			fileRef->Seek ( pos, kXMP_SeekFromStart );
 			if ( kASF_ObjectBaseLen != fileRef->Read ( &objectBase, kASF_ObjectBaseLen, true ) ) break;
@@ -226,7 +227,7 @@ bool ASF_Support::ReadHeaderObject ( XMP
 				XMP_Uns32 fieldPos = 28;
 
 				// copyright URL is 3. element with variable size
-				for ( int i = 1; i <= 3 ; ++i ) {
+				for ( int i = 1; i <= 3 && fieldPos < buffer.size() ; ++i ) { 
 					XMP_Uns32 len = GetUns32LE ( &buffer[fieldPos] );
 					if ( i == 3 ) {
 						std::string copyrightURLStr = buffer.substr ( fieldPos + 4, len );
@@ -276,8 +277,8 @@ bool ASF_Support::ReadHeaderObject ( XMP
 
 			pos += objectBase.size;
 			read += objectBase.size;
+			numberOfHeaders--;
 		}
-
 	} catch ( ... ) {
 
 		return false;
@@ -316,7 +317,7 @@ bool ASF_Support::WriteHeaderObject ( XM
 		pos += bufferSize;
 
 		// read contained header objects
-		/*XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] );*/
+		XMP_Uns32 numberOfHeaders = GetUns32LE ( &buffer[24] );
 		ASF_ObjectBase objectBase;
 
 		// prepare new header in memory
@@ -328,7 +329,7 @@ bool ASF_Support::WriteHeaderObject ( XM
 
 		header.append ( buffer.c_str(), bufferSize );
 
-		while ( read < object.len ) {
+		while ( read < object.len && numberOfHeaders > 0 ) {
 
 			sourceRef->Seek ( pos, kXMP_SeekFromStart );
 			if ( kASF_ObjectBaseLen != sourceRef->Read ( &objectBase, kASF_ObjectBaseLen, true ) ) break;
@@ -503,7 +504,7 @@ bool ASF_Support::WriteHeaderObject ( XM
 
 			pos += objectBase.size;
 			read += objectBase.size;
-
+			numberOfHeaders--;
 			writtenObjects ++;
 
 		}
--- a/XMPFiles/source/FormatSupport/ID3_Support.cpp
+++ b/XMPFiles/source/FormatSupport/ID3_Support.cpp
@@ -462,7 +462,7 @@ ID3v2Frame::ID3v2Frame ( XMP_Uns32 id_ )
 
 void ID3v2Frame::release()
 {
-	if ( this->content != 0 ) delete [] this->content;
+	if ( this->content != 0 ) delete [] content;
 	this->content = 0;
 	this->contentSize = 0;
 }
--- a/XMPFiles/source/FormatSupport/P2_Support.cpp
+++ b/XMPFiles/source/FormatSupport/P2_Support.cpp
@@ -108,7 +108,10 @@ void P2_Clip::CacheClipContent()
 {
 	if (headContentCached) return;
 	headContentCached = true;
-	XMP_StringPtr p2NameSpace=GetP2RootNode()->ns.c_str();
+	XML_NodePtr p2RootNode = GetP2RootNode();
+	if( p2RootNode == 0 ) return;
+	XMP_StringPtr p2NameSpace = p2RootNode->ns.c_str();
+
 	p2ClipContent = GetP2RootNode()->GetNamedElement ( p2NameSpace, "ClipContent" );
 	if ( p2ClipContent == 0 )  return;
 	XML_NodePtr  p2node;
@@ -356,7 +359,9 @@ void P2_SpannedClip::CreateDigest ( std:
 	digestStr->erase();
 	if ( this->headContent.clipMetadata == 0 ) return;	// Bail if we don't have any legacy XML.
 
-	XMP_StringPtr p2NS = this->GetP2RootNode()->ns.c_str();
+	XML_NodePtr p2RootNode = this->GetP2RootNode(); // Return if there is no root node.
+	if( p2RootNode == 0 ) return;
+	XMP_StringPtr p2NS = p2RootNode->ns.c_str();
 	XML_NodePtr legacyContext;
 	MD5_CTX    md5Context;
 	unsigned char digestBin [16];
--- a/XMPFiles/source/FormatSupport/PostScript_Support.cpp
+++ b/XMPFiles/source/FormatSupport/PostScript_Support.cpp
@@ -1014,14 +1014,7 @@ std::string PostScript_Support::ConvertT
 					if(itr!=tokenzs.end())
 					{
 						++itr;
-						if (itr == tokenzs.end())
-						{
-							// bug 101914 - corrupt file make us
-							// reach the end. -- Hub
-							// https://bugs.freedesktop.org/show_bug.cgi?id=101914
-							break;
-						}
-						if (itr->noOfDelimiter==0 && IsNumeric(itr->token[0]) )
+						if (itr<tokenzs.end() && itr->noOfDelimiter==0 && IsNumeric(itr->token[0]) )
 						{
 							const char * str=itr->token.c_str();
 							short day= GetNumber(&str);
@@ -1030,6 +1023,10 @@ std::string PostScript_Support::ConvertT
 								date.day=day;
 							}
 						}
+						else if (itr == tokenzs.end())
+						{
+							break;
+						}
 					}
 				}
 			}
--- a/XMPFiles/source/FormatSupport/RIFF.cpp
+++ b/XMPFiles/source/FormatSupport/RIFF.cpp
@@ -185,6 +185,10 @@ Chunk::Chunk( ContainerChunk* parent_, R
 		bool repairFile = XMP_OptionIsSet ( handler->parent->openFlags, kXMPFiles_OpenRepairFile );
 		if ( (! isUpdate) || (repairFile && (parent_ == 0)) ) {
 			this->oldSize = chunkLimit - this->oldPos;
+			if (this->oldSize < 8)
+			{
+				XMP_Throw("Invalid RIFF chunk size", kXMPErr_BadFileFormat);
+			}
 		} else {
 			XMP_Throw ( "Bad RIFF chunk size", kXMPErr_BadFileFormat );
 		}
--- a/XMPFiles/source/FormatSupport/ReconcileLegacy.hpp
+++ b/XMPFiles/source/FormatSupport/ReconcileLegacy.hpp
@@ -85,7 +85,7 @@ extern void ExportPhotoData ( XMP_FileFo
 // Here are the primary (0th) IFD tags that get special treatment:
 //
 // 270, 33432 - ASCII mapped to alt-text['x-default']
-// 306 - DateTime master
+// 306 - DateTime main
 // 315 - ASCII mapped to text seq[1]
 //
 // Here are the primary (0th) IFD tags that get mapped by type and count:
@@ -97,7 +97,7 @@ extern void ExportPhotoData ( XMP_FileFo
 //
 // 34856, 41484 - OECF/SFR table
 // 36864, 40960 - 4 ASCII chars to text
-// 36867, 36868 - DateTime master
+// 36867, 36868 - DateTime main
 // 37121 - 4 UInt8 to integer seq
 // 37385 - Flash struct
 // 37510 - explicitly encoded text to alt-text['x-default']
@@ -114,8 +114,8 @@ extern void ExportPhotoData ( XMP_FileFo
 // Here are the GPS IFD tags that get special treatment:
 //
 // 0 - 4 UInt8 to text "n.n.n.n"
-// 2, 4, 20, 22 - Latitude or longitude master
-// 7 - special DateTime master, the time part
+// 2, 4, 20, 22 - Latitude or longitude main
+// 7 - special DateTime main, the time part
 // 27, 28 - explicitly encoded text
 //
 // Here are the GPS IFD tags that get mapped by type and count:
@@ -161,7 +161,7 @@ extern void ExportPhotoData ( XMP_FileFo
 //   296  SHORT            1  ResolutionUnit             integer
 //   301  SHORT        3*256  TransferFunction           integer seq
 //   305  ASCII          Any  Software                   text, xmp:CreatorTool
-//   306  ASCII           20  DateTime                   date, master of 37520, xmp:DateTime
+//   306  ASCII           20  DateTime                   date, main of 37520, xmp:DateTime
 //   315  ASCII          Any  Artist                     text, dc:creator[1]
 //   318  RATIONAL         2  WhitePoint                 rational seq
 //   319  RATIONAL         6  PrimaryChromaticities      rational seq
@@ -181,8 +181,8 @@ extern void ExportPhotoData ( XMP_FileFo
 // 34855  SHORT          Any  ISOSpeedRatings            integer seq
 // 34856  UNDEFINED      Any  OECF                       OECF/SFR table
 // 36864  UNDEFINED        4  ExifVersion                text, Exif has 4 ASCII chars
-// 36867  ASCII           20  DateTimeOriginal           date, master of 37521
-// 36868  ASCII           20  DateTimeDigitized          date, master of 37522
+// 36867  ASCII           20  DateTimeOriginal           date, main of 37521
+// 36868  ASCII           20  DateTimeDigitized          date, main of 37522
 // 37121  UNDEFINED        4  ComponentsConfiguration    integer seq, Exif has 4 UInt8
 // 37122  RATIONAL         1  CompressedBitsPerPixel     rational
 // 37377  SRATIONAL        1  ShutterSpeedValue          rational
@@ -235,12 +235,12 @@ extern void ExportPhotoData ( XMP_FileFo
 //
 //     0  BYTE             4  GPSVersionID               text, "n.n.n.n", Exif has 4 UInt8
 //     1  ASCII            2  GPSLatitudeRef             latitude, with 2
-//     2  RATIONAL         3  GPSLatitude                latitude, master of 2
+//     2  RATIONAL         3  GPSLatitude                latitude, main of 2
 //     3  ASCII            2  GPSLongitudeRef            longitude, with 4
-//     4  RATIONAL         3  GPSLongitude               longitude, master of 3
+//     4  RATIONAL         3  GPSLongitude               longitude, main of 3
 //     5  BYTE             1  GPSAltitudeRef             integer
 //     6  RATIONAL         1  GPSAltitude                rational
-//     7  RATIONAL         3  GPSTimeStamp               date, master of 29
+//     7  RATIONAL         3  GPSTimeStamp               date, main of 29
 //     8  ASCII          Any  GPSSatellites              text
 //     9  ASCII            2  GPSStatus                  text
 //    10  ASCII            2  GPSMeasureMode             text
@@ -253,9 +253,9 @@ extern void ExportPhotoData ( XMP_FileFo
 //    17  RATIONAL         1  GPSImgDirection            rational
 //    18  ASCII          Any  GPSMapDatum                text
 //    19  ASCII            2  GPSDestLatitudeRef         latitude, with 20
-//    20  RATIONAL         3  GPSDestLatitude            latitude, master of 19
+//    20  RATIONAL         3  GPSDestLatitude            latitude, main of 19
 //    21  ASCII            2  GPSDestLongitudeRef        longitude, with 22
-//    22  RATIONAL         3  GPSDestLongitude           logitude, master of 21
+//    22  RATIONAL         3  GPSDestLongitude           logitude, main of 21
 //    23  ASCII            2  GPSDestBearingRef          text
 //    24  RATIONAL         1  GPSDestBearing             rational
 //    25  ASCII            2  GPSDestDistanceRef         text
--- a/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp
+++ b/XMPFiles/source/FormatSupport/ReconcileTIFF.cpp
@@ -116,6 +116,9 @@ static const TIFF_MappingToXMP sExifIFDM
 	{ /* 40964 */ kTIFF_RelatedSoundFile,          kTIFF_ASCIIType,       kAnyCount, kExport_Always,     kXMP_NS_EXIF,  "RelatedSoundFile" },	// ! Exif spec says count of 13.
 	{ /* 36867 */ kTIFF_DateTimeOriginal,          kTIFF_ASCIIType,       20,        kExport_Always,     "", "" },	// ! Has a special mapping.
 	{ /* 36868 */ kTIFF_DateTimeDigitized,         kTIFF_ASCIIType,       20,        kExport_Always,     "", "" },	// ! Has a special mapping.
+	{ /* 36880 */ kTIFF_OffsetTime,                kTIFF_ASCIIType,       7,         kExport_Always,     "", "" },	// ! Has a special mapping.
+	{ /* 36881 */ kTIFF_OffsetTimeOriginal,        kTIFF_ASCIIType,       7,         kExport_Always,     "", "" },	// ! Has a special mapping.
+	{ /* 36882 */ kTIFF_OffsetTimeDigitized,       kTIFF_ASCIIType,       7,         kExport_Always,     "", "" },	// ! Has a special mapping.
 	{ /* 42016 */ kTIFF_ImageUniqueID,             kTIFF_ASCIIType,       33,        kExport_InjectOnly, kXMP_NS_EXIF,  "ImageUniqueID" },
 	{ /* 42032 */ kTIFF_CameraOwnerName,           kTIFF_ASCIIType,       kAnyCount, kExport_InjectOnly, kXMP_NS_ExifEX, "CameraOwnerName" },
 	{ /* 42033 */ kTIFF_BodySerialNumber,          kTIFF_ASCIIType,       kAnyCount, kExport_InjectOnly, kXMP_NS_ExifEX, "BodySerialNumber" },
@@ -1248,6 +1251,14 @@ ImportTIFF_StandardMappings ( XMP_Uns8 i
 			
 			bool found = tiff.GetTag ( ifd, mapInfo.id, &tagInfo );
 			if ( ! found ) continue;
+			/* tag length need to be checked in case of TIFF_MemoryReader, as a possible case
+             the data length value might be changed (flippig because of endianess) by next overlapping IFD leading to crash.
+             To avoid that, rechecking the datalen just before its access. Fixing CTECHXMP-4170409*/
+            if(tiff.IsCheckTagLength() &&
+               tagInfo.dataLen > tiff.GetTiffLength() - ((XMP_Uns8*)tagInfo.dataPtr - tiff.GetTiffStream())) {
+                    continue;    // Bad Tag
+                }
+            
 
 			XMP_Assert ( tagInfo.type != kTIFF_UndefinedType );	// These must have a special mapping.
 			if ( tagInfo.type == kTIFF_UndefinedType ) continue;
@@ -1277,7 +1288,7 @@ ImportTIFF_StandardMappings ( XMP_Uns8 i
 // ImportTIFF_Date
 // ===============
 //
-// Convert an Exif 2.2 master date/time tag plus associated fractional seconds to an XMP date/time.
+// Convert an Exif 2.2 main date/time tag plus associated fractional seconds to an XMP date/time.
 // The Exif date/time part is a 20 byte ASCII value formatted as "YYYY:MM:DD HH:MM:SS" with a
 // terminating nul. Any of the numeric portions can be blanks if unknown. The fractional seconds
 // are a nul terminated ASCII string with possible space padding. They are literally the fractional
@@ -1287,11 +1298,14 @@ static void
 ImportTIFF_Date ( const TIFF_Manager & tiff, const TIFF_Manager::TagInfo & dateInfo,
 				  SXMPMeta * xmp, const char * xmpNS, const char * xmpProp )
 {
-	XMP_Uns16 secID = 0;
+	XMP_Uns16 secID = 0, offsetID = 0;
 	switch ( dateInfo.id ) {
-		case kTIFF_DateTime          : secID = kTIFF_SubSecTime;			break;
-		case kTIFF_DateTimeOriginal  : secID = kTIFF_SubSecTimeOriginal;	break;
-		case kTIFF_DateTimeDigitized : secID = kTIFF_SubSecTimeDigitized;	break;
+		case kTIFF_DateTime          : secID = kTIFF_SubSecTime;
+									    offsetID = kTIFF_OffsetTime; break;
+		case kTIFF_DateTimeOriginal  : secID = kTIFF_SubSecTimeOriginal;
+										offsetID = kTIFF_OffsetTimeOriginal; break;
+		case kTIFF_DateTimeDigitized : secID = kTIFF_SubSecTimeDigitized;
+										offsetID = kTIFF_OffsetTimeDigitized; break;
 	}
 	
 	try {	// Don't let errors with one stop the others.
@@ -1332,7 +1346,31 @@ ImportTIFF_Date ( const TIFF_Manager & t
 			for ( ; digits < 9; ++digits ) binValue.nanoSecond *= 10;
 			if ( binValue.nanoSecond != 0 ) binValue.hasTime = true;
 		}
+		// The offset time tags were added to EXIF spec 2.3.1., therefore we not
+		// supporting read/write in older versions
+		// We need EXIF spec version to figure out the same.
 
+		bool haveOldExif = true;    // Default to old Exif if no version tag.
+		TIFF_Manager::TagInfo tagInfo;
+		bool foundExif = tiff.GetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, &tagInfo );
+		if ( foundExif && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
+			haveOldExif = (strncmp ( (char*)tagInfo.dataPtr, "0231", 4 ) < 0);
+		}
+
+		if (!haveOldExif)
+		{
+			TIFF_Manager::TagInfo timezoneInfo;
+			found = tiff.GetTag ( kTIFF_ExifIFD, offsetID, &timezoneInfo );
+			if ( found && (timezoneInfo.type == kTIFF_ASCIIType) && (timezoneInfo.count == 7) ) {
+				const char * timezoneStr = (const char *) timezoneInfo.dataPtr;
+				if ( (timezoneStr[0] == '+')  || (timezoneStr[0] == '-')  || (timezoneStr[3] == ':') ) {
+					binValue.tzSign     = (timezoneStr[0] == '-') ? -1 : 1;
+					binValue.tzHour      = GatherInt ( &timezoneStr[1], 2 );
+					binValue.tzMinute   = GatherInt ( &timezoneStr[4], 2 );
+					binValue.hasTimeZone = true;
+				}
+			}
+		}
 		xmp->SetProperty_Date ( xmpNS, xmpProp, binValue );
 
 	} catch ( ... ) {
@@ -2205,13 +2243,13 @@ PhotoDataUtils::Import2WayExif ( const T
 		xmp->SetProperty ( kXMP_NS_EXIF, "GPSVersionID", strOut );
 	}
 
-	// 2 GPSLatitude is a GPS coordinate master.
+	// 2 GPSLatitude is a GPS coordinate main.
 	found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSLatitude, &tagInfo );
 	if ( found ) {
 		ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSLatitude" );
 	}
 
-	// 4 GPSLongitude is a GPS coordinate master.
+	// 4 GPSLongitude is a GPS coordinate main.
 	found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSLongitude, &tagInfo );
 	if ( found ) {
 		ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSLongitude" );
@@ -2223,13 +2261,13 @@ PhotoDataUtils::Import2WayExif ( const T
 		ImportTIFF_GPSTimeStamp ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSTimeStamp" );
 	}
 
-	// 20 GPSDestLatitude is a GPS coordinate master.
+	// 20 GPSDestLatitude is a GPS coordinate main.
 	found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLatitude, &tagInfo );
 	if ( found ) {
 		ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLatitude" );
 	}
 
-	// 22 GPSDestLongitude is a GPS coordinate master.
+	// 22 GPSDestLongitude is a GPS coordinate main.
 	found = exif.GetTag ( kTIFF_GPSInfoIFD, kTIFF_GPSDestLongitude, &tagInfo );
 	if ( found ) {
 		ImportTIFF_GPSCoordinate ( exif, tagInfo, xmp, kXMP_NS_EXIF, "GPSDestLongitude" );
@@ -2665,7 +2703,7 @@ ExportTIFF_StandardMappings ( XMP_Uns8 i
 // ExportTIFF_Date
 // ===============
 //
-// Convert  an XMP date/time to an Exif 2.2 master date/time tag plus associated fractional seconds.
+// Convert  an XMP date/time to an Exif 2.2 main date/time tag plus associated fractional seconds.
 // The Exif date/time part is a 20 byte ASCII value formatted as "YYYY:MM:DD HH:MM:SS" with a
 // terminating nul. The fractional seconds are a nul terminated ASCII string with possible space
 // padding. They are literally the fractional part, the digits that would be to the right of the
@@ -2676,10 +2714,18 @@ ExportTIFF_Date ( const SXMPMeta & xmp,
 {
 	XMP_Uns8 mainIFD = kTIFF_ExifIFD;
 	XMP_Uns16 fracID=0;
+	XMP_Uns16 offsetID=0;
 	switch ( mainID ) {
-		case kTIFF_DateTime : mainIFD = kTIFF_PrimaryIFD; fracID = kTIFF_SubSecTime;	break;
-		case kTIFF_DateTimeOriginal  : fracID = kTIFF_SubSecTimeOriginal;	break;
-		case kTIFF_DateTimeDigitized : fracID = kTIFF_SubSecTimeDigitized;	break;
+		case kTIFF_DateTime : mainIFD = kTIFF_PrimaryIFD; 
+		                      fracID = kTIFF_SubSecTime; 
+		                      offsetID = kTIFF_OffsetTime; 
+							  break;
+		case kTIFF_DateTimeOriginal  : fracID = kTIFF_SubSecTimeOriginal; 
+		                               offsetID = kTIFF_OffsetTimeOriginal;	
+									   break;
+		case kTIFF_DateTimeDigitized : fracID = kTIFF_SubSecTimeDigitized; 
+		                               offsetID = kTIFF_OffsetTimeDigitized;	
+									   break;
 	}
 
 	try {	// Don't let errors with one stop the others.
@@ -2689,6 +2735,7 @@ ExportTIFF_Date ( const SXMPMeta & xmp,
 		if ( ! foundXMP ) {
 			tiff->DeleteTag ( mainIFD, mainID );
 			tiff->DeleteTag ( kTIFF_ExifIFD, fracID );	// ! The subseconds are always in the Exif IFD.
+			tiff->DeleteTag ( kTIFF_ExifIFD, offsetID );// ! The offsetTime are always in the Exif IFD.
 			return;
 		}
 
@@ -2733,23 +2780,53 @@ ExportTIFF_Date ( const SXMPMeta & xmp,
 		if ( xmpBin.nanoSecond == 0 ) {
 		
 			tiff->DeleteTag ( kTIFF_ExifIFD, fracID );
-		
-		} else {
-
-			snprintf ( buffer, sizeof(buffer), "%09d", xmpBin.nanoSecond );	// AUDIT: Use of sizeof(buffer) is safe.
-			for ( size_t i = strlen(buffer)-1; i > 0; --i ) {
-				if ( buffer[i] != '0' ) break;
-				buffer[i] = 0;	// Strip trailing zero digits.
+		}
+		else
+		{
+			snprintf(buffer, sizeof(buffer), "%09d", xmpBin.nanoSecond); // AUDIT: Use of sizeof(buffer) is safe.
+			for (size_t i = strlen(buffer) - 1; i > 0; --i)
+			{
+				if (buffer[i] != '0')
+					break;
+				buffer[i] = 0; // Strip trailing zero digits.
 			}
+			tiff->SetTag_ASCII(kTIFF_ExifIFD, fracID, buffer); // ! The subseconds are always in the Exif IFD.
+		}
 
-			tiff->SetTag_ASCII ( kTIFF_ExifIFD, fracID, buffer );	// ! The subseconds are always in the Exif IFD.
+		bool haveOldExif = true;    // Default to old Exif if no version tag.
+		TIFF_Manager::TagInfo tagInfo;
+		bool foundExif = tiff->GetTag ( kTIFF_ExifIFD, kTIFF_ExifVersion, &tagInfo );
+		if ( foundExif && (tagInfo.type == kTIFF_UndefinedType) && (tagInfo.count == 4) ) {
+			haveOldExif = (strncmp ( (char*)tagInfo.dataPtr, "0231", 4 ) < 0);
+		}
+		if (!haveOldExif)
+		{
+			// The offset time tags were added to EXIF spec 2.3.1., therefore we are not
+			// supporting read/write in older versions
+			// We need EXIF spec version to figure out the same.
+
+			if ( xmpBin.hasTimeZone == 0 || (xmpBin.tzSign != -1 && xmpBin.tzSign != 1) ){
+
+				tiff->DeleteTag ( kTIFF_ExifIFD, offsetID );
+
+			}else
+			{
+				char tzSign = '+';
+				if (xmpBin.tzSign == -1)
+					tzSign = '-';
+
+				char offsetBuffer[7];
+				snprintf(offsetBuffer, sizeof(offsetBuffer), "%c%02d:%02d", // AUDIT: Use of sizeof(offsetBuffer) is safe.
+						 tzSign, xmpBin.tzHour, xmpBin.tzMinute);
 
+				tiff->SetTag_ASCII(kTIFF_ExifIFD, offsetID, offsetBuffer); // ! The OffsetTime are always in the Exif IFD.
+			}
 		}
 
 	} catch ( ... ) {
 		// Do nothing, let other exports proceed.
 		// ? Notify client?
-	}
+	 }
 
 }	// ExportTIFF_Date
 
--- a/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp
+++ b/XMPFiles/source/FormatSupport/TIFF_FileWriter.cpp
@@ -971,17 +971,17 @@ void TIFF_FileWriter::IntegrateFromPShop
 }	// TIFF_FileWriter::IntegrateFromPShop6
 
 // =================================================================================================
-// TIFF_FileWriter::CopyTagToMasterIFD
+// TIFF_FileWriter::CopyTagToMainIFD
 // ===================================
 //
-// Create a new master IFD entry from a buried Photoshop 6 IFD entry. Don't try to get clever with
+// Create a new main IFD entry from a buried Photoshop 6 IFD entry. Don't try to get clever with
 // large values, just create a new copy. This preserves a clean separation between the memory-based
 // and file-based TIFF processing.
 
-void* TIFF_FileWriter::CopyTagToMasterIFD ( const TagInfo & ps6Tag, InternalIFDInfo * masterIFD )
+void* TIFF_FileWriter::CopyTagToMainIFD ( const TagInfo & ps6Tag, InternalIFDInfo * mainIFD )
 {
 	InternalTagMap::value_type mapValue ( ps6Tag.id, InternalTagInfo ( ps6Tag.id, ps6Tag.type, ps6Tag.count, this->fileParsed ) );
-	InternalTagMap::iterator newPos = masterIFD->tagMap.insert ( masterIFD->tagMap.end(), mapValue );
+	InternalTagMap::iterator newPos = mainIFD->tagMap.insert ( mainIFD->tagMap.end(), mapValue );
 	InternalTagInfo& newTag = newPos->second;
 
 	newTag.dataLen = ps6Tag.dataLen;
@@ -998,11 +998,11 @@ void* TIFF_FileWriter::CopyTagToMasterIF
 	newTag.changed = true;	// ! See comments with ProcessPShop6IFD.
 	XMP_Assert ( (newTag.origDataLen == 0) && (newTag.origDataOffset == 0) );
 
-	masterIFD->changed = true;
+	mainIFD->changed = true;
 
 	return newPos->second.dataPtr;	// ! Return the address within the map entry for small values.
 
-}	// TIFF_FileWriter::CopyTagToMasterIFD
+}	// TIFF_FileWriter::CopyTagToMainIFD
 
 // =================================================================================================
 // FlipCFATable
@@ -1016,10 +1016,10 @@ static bool FlipCFATable ( void* voidPtr
 
 	XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr;
 
-	Flip2 ( &u16Ptr[0] );	// Flip the counts to match the master TIFF.
+	Flip2 ( &u16Ptr[0] );	// Flip the counts to match the main TIFF.
 	Flip2 ( &u16Ptr[1] );
 
-	XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] );	// Fetch using the master TIFF's routine.
+	XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] );	// Fetch using the main TIFF's routine.
 	XMP_Uns16 rows    = GetUns16 ( &u16Ptr[1] );
 
 	if ( tagLen != (XMP_Uns32)(4 + columns*rows) ) return false;
@@ -1061,10 +1061,10 @@ static bool FlipOECFSFRTable ( void* voi
 {
 	XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr;
 
-	Flip2 ( &u16Ptr[0] );	// Flip the data to match the master TIFF.
+	Flip2 ( &u16Ptr[0] );	// Flip the data to match the main TIFF.
 	Flip2 ( &u16Ptr[1] );
 
-	XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] );	// Fetch using the master TIFF's routine.
+	XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] );	// Fetch using the main TIFF's routine.
 	XMP_Uns16 rows    = GetUns16 ( &u16Ptr[1] );
 
 	XMP_Uns32 minLen = 4 + columns + (8 * columns * rows);	// Minimum legit tag size.
@@ -1111,25 +1111,25 @@ void TIFF_FileWriter::ProcessPShop6IFD (
 
 	bool needsFlipping = (this->bigEndian != buriedExif.IsBigEndian());
 
-	InternalIFDInfo* masterIFD = &this->containedIFDs[ifd];
+	InternalIFDInfo* mainIFD = &this->containedIFDs[ifd];
 
 	TagInfoMap::const_iterator ps6Pos = ps6IFD.begin();
 	TagInfoMap::const_iterator ps6End = ps6IFD.end();
 
 	for ( ; ps6Pos != ps6End; ++ps6Pos ) {
 
-		// Copy buried tags to the master IFD if they don't already exist there.
+		// Copy buried tags to the main IFD if they don't already exist there.
 
 		const TagInfo& ps6Tag = ps6Pos->second;
 
-		if ( this->FindTagInIFD ( ifd, ps6Tag.id ) != 0 ) continue;	// Keep existing master tags.
+		if ( this->FindTagInIFD ( ifd, ps6Tag.id ) != 0 ) continue;	// Keep existing main tags.
 		if ( needsFlipping && (ps6Tag.id == 37500) ) continue;	// Don't copy an unflipped MakerNote.
 		if ( (ps6Tag.id == kTIFF_ExifIFDPointer) ||	// Skip the tags that are explicit offsets.
 			 (ps6Tag.id == kTIFF_GPSInfoIFDPointer) ||
 			 (ps6Tag.id == kTIFF_JPEGInterchangeFormat) ||
 			 (ps6Tag.id == kTIFF_InteroperabilityIFDPointer) ) continue;
 
-		void* voidPtr = this->CopyTagToMasterIFD ( ps6Tag, masterIFD );
+		void* voidPtr = this->CopyTagToMainIFD ( ps6Tag, mainIFD );
 
 		if ( needsFlipping ) {
 			switch ( ps6Tag.type ) {
@@ -1509,8 +1509,9 @@ void TIFF_FileWriter::UpdateMemByAppend
 				if ( (appendAll | currTag.changed) && (currTag.dataLen > 4) ) {
 
 					XMP_Uns32 valueOffset = this->GetUns32 ( &currTag.smallValue );
+					bool inplaceUpdate = (currTag.dataLen <= currTag.origDataLen) && (! appendAll);
 
-					if ( (currTag.dataLen <= currTag.origDataLen) && (! appendAll) ) {
+					if ( inplaceUpdate ) {
 						XMP_Assert ( valueOffset == currTag.origDataOffset );
 					} else {
 						XMP_Assert ( valueOffset == appendedOffset );
@@ -1520,7 +1521,10 @@ void TIFF_FileWriter::UpdateMemByAppend
 					XMP_Assert ( valueOffset <= newLength );	// Provably true, valueOffset is in the old span, newLength is the new bigger span.
 					if ( currTag.dataLen > (newLength - valueOffset) ) XMP_Throw ( "Buffer overrun", kXMPErr_InternalFailure );
 					memcpy ( (newStream + valueOffset), currTag.dataPtr, currTag.dataLen );	// AUDIT: Protected by the above check.
-					if ( (currTag.dataLen & 1) != 0 ) newStream[valueOffset+currTag.dataLen] = 0;
+					if ( !inplaceUpdate && ((currTag.dataLen & 1) != 0) ) {
+						newStream[valueOffset+currTag.dataLen] = 0;
+
+					}
 
 				}
 
--- a/XMPFiles/source/FormatSupport/TIFF_Support.hpp
+++ b/XMPFiles/source/FormatSupport/TIFF_Support.hpp
@@ -97,9 +97,9 @@ enum {	// Constants for the type field o
 
 static const size_t kTIFF_TypeSizes[]    = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4 };
 
-static const bool kTIFF_IsIntegerType[]  = { 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0 };
-static const bool kTIFF_IsRationalType[] = { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 };
-static const bool kTIFF_IsFloatType[]    = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
+static const bool kTIFF_IsIntegerType[]  = { 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0 ,0 };
+static const bool kTIFF_IsRationalType[] = { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 ,0 };
+static const bool kTIFF_IsFloatType[]    = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 ,0 };
 
 enum {	// Encodings for SetTag_EncodedString.
 	kTIFF_EncodeUndefined = 0,
@@ -187,6 +187,9 @@ enum {
 	kTIFF_RelatedSoundFile = 40964,
 	kTIFF_DateTimeOriginal = 36867,
 	kTIFF_DateTimeDigitized = 36868,
+	kTIFF_OffsetTime = 36880,
+	kTIFF_OffsetTimeOriginal = 36881,
+	kTIFF_OffsetTimeDigitized = 36882,
 	kTIFF_SubSecTime = 37520,
 	kTIFF_SubSecTimeOriginal = 37521,
 	kTIFF_SubSecTimeDigitized = 37522,
@@ -360,6 +363,9 @@ static const XMP_Uns16 sKnownExifIFDTags
 	kTIFF_ExifVersion,					// 36864
 	kTIFF_DateTimeOriginal,				// 36867
 	kTIFF_DateTimeDigitized,			// 36868
+	kTIFF_OffsetTime,                   // 36880
+	kTIFF_OffsetTimeOriginal,           // 36881
+	kTIFF_OffsetTimeDigitized,          // 36882
 	kTIFF_ComponentsConfiguration,		// 37121
 	kTIFF_CompressedBitsPerPixel,		// 37122
 	kTIFF_ShutterSpeedValue,			// 37377
@@ -512,6 +518,8 @@ public:
 	bool IsBigEndian() const { return this->bigEndian; };
 	bool IsLittleEndian() const { return (! this->bigEndian); };
 	bool IsNativeEndian() const { return this->nativeEndian; };
+	bool IsCheckTagLength () const { return this->checkTagLength; }
+
 
 	// ---------------------------------------------------------------------------------------------
 	// The TIFF_Manager only keeps explicit knowledge of up to 4 IFDs:
@@ -557,6 +565,9 @@ public:
 	// new tag will have type short or long.
 
 	virtual bool GetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32* data ) const = 0;
+	virtual XMP_Uns32 GetTiffLength() const = 0;
+
+	virtual XMP_Uns8 *GetTiffStream() const = 0;
 
 	void SetTag_Integer ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_Uns32 data );
 
@@ -671,6 +682,7 @@ public:
 protected:
 
 	bool bigEndian, nativeEndian;
+	bool checkTagLength { false }; 
 
 	XMP_Uns32 CheckTIFFHeader ( const XMP_Uns8* tiffPtr, XMP_Uns32 length );
 		// The pointer is to a buffer of the first 8 bytes. The length is the overall length, used
@@ -732,6 +744,9 @@ public:
 	bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const;
 
 	bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const;
+	XMP_Uns32 GetTiffLength() const { return tiffLength; }
+
+	XMP_Uns8 *GetTiffStream() const { return tiffStream; }
 
 	void SetTag_EncodedString ( XMP_Uns8 /*ifd*/, XMP_Uns16 /*id*/, const std::string& /*utf8Str*/, XMP_Uns8 /*encoding*/ ) { NotAppropriate(); };
 
@@ -747,7 +762,9 @@ public:
 	XMP_Uns32 UpdateMemoryStream ( void** dataPtr, bool condenseStream = false ) { IgnoreParam(condenseStream); if ( dataPtr != 0 ) *dataPtr = tiffStream; return tiffLength; };
 	void      UpdateFileStream   ( XMP_IO* /*fileRef*/, XMP_ProgressTracker* /*progressTracker*/ ) { NotAppropriate(); };
 
-	TIFF_MemoryReader() : ownedStream(false), tiffStream(0), tiffLength(0) {};
+	TIFF_MemoryReader() : ownedStream(false), tiffStream(0), tiffLength(0) { 
+        checkTagLength = true;
+    };
 
 	virtual ~TIFF_MemoryReader() { if ( this->ownedStream ) free ( this->tiffStream ); };
 
@@ -847,6 +864,9 @@ public:
 	bool GetTag_ASCII ( XMP_Uns8 ifd, XMP_Uns16 id, XMP_StringPtr* dataPtr, XMP_StringLen* dataLen ) const;
 
 	bool GetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, std::string* utf8Str ) const;
+	XMP_Uns32 GetTiffLength() const { return tiffLength; }
+
+	XMP_Uns8 *GetTiffStream() const { return memStream; }
 
 	void SetTag_EncodedString ( XMP_Uns8 ifd, XMP_Uns16 id, const std::string& utf8Str, XMP_Uns8 encoding );
 
@@ -966,7 +986,7 @@ private:
 
 	void ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XMP_Uns8 ifd );
 
-	void* CopyTagToMasterIFD ( const TagInfo& ps6Tag, InternalIFDInfo* masterIFD );
+	void* CopyTagToMainIFD ( const TagInfo& ps6Tag, InternalIFDInfo* mainIFD );
 
 	void PreflightIFDLinkage();
 
--- a/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp
+++ b/XMPFiles/source/PluginHandler/FileHandlerInstance.cpp
@@ -23,8 +23,9 @@ XMPFileHandler( _parent ), mObject( obje
 FileHandlerInstance::~FileHandlerInstance()
 {
 	WXMP_Error error;
-	mHandler->getModule()->getPluginAPIs()->mTerminateSessionProc( this->mObject, &error );
-	PluginManager::removeHandlerInstance( this->mObject );
+
+	PluginManager::removeHandlerInstance(this->mObject);
+	mHandler->getModule()->getPluginAPIs()->mTerminateSessionProc(this->mObject, &error);
 	CheckError( error );
 }
 
--- a/XMPFiles/source/WXMPFiles.cpp
+++ b/XMPFiles/source/WXMPFiles.cpp
@@ -321,12 +321,15 @@ void WXMPFiles_GetXMP_1 ( XMPFilesRef
 		StartPerfCheck ( kAPIPerf_GetXMP, "" );
 
 		bool hasXMP = false;
-		XMP_StringPtr packetStr;
-		XMP_StringLen packetLen;
-
-		if ( xmpRef == 0 ) {
+		XMP_StringPtr packetStr = NULL;
+		XMP_StringLen packetLen = 0;
+		/*Adding check to handle case where a client might not send XMPMetaRef but still want xmp packet in return. eg. CTECHXMP-4170329*/
+		if ( xmpRef == 0  && clientPacket != 0 ) {
 			hasXMP = thiz->GetXMP ( 0, &packetStr, &packetLen, packetInfo );
-		} else {
+		} else if ( xmpRef == 0 && clientPacket == 0 ) {
+			hasXMP = thiz->GetXMP( 0, 0, 0, packetInfo );
+		}
+		else {
 			SXMPMeta xmpObj ( xmpRef );
 			hasXMP = thiz->GetXMP ( &xmpObj, &packetStr, &packetLen, packetInfo );
 		}
--- a/public/include/TXMPFiles.hpp
+++ b/public/include/TXMPFiles.hpp
@@ -541,7 +541,8 @@ public:
     ///
     /// If the file is opened for update (passing \c #kXMPFiles_OpenForUpdate), the disk file remains
     /// open until \c CloseFile() is called. The disk file is only updated once, when \c CloseFile()
-    /// is called, regardless of how many calls are made to \c PutXMP().
+    /// is called, regardless of how many calls are made to \c PutXMP(). When in-place update is not possible
+    /// we might write into a temporary file and then swap for corruption/crash safety. /* Documenatation update for CTECHXMP-4170278*/
     ///
     /// @param closeFlags Option flags for optional closing actions. This bit-flag constant is
     /// defined:
