diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/automap.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/automap.cpp index 00ec1e7b5a4..a6bce80485d 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/automap.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/automap.cpp @@ -22,6 +22,8 @@ #include "ttferror.h" /* for error codes */ #include "ttmem.h" #include "automap.h" +#include "intsafe_private_copy.h" +#include "ttf_safe_checks.h" int16 MortAutoMap(TTFACC_FILEBUFFERINFO * pInputBufferInfo, /* ttfacc info */ @@ -41,6 +43,11 @@ int16 errCode = NO_ERROR; ulOffset = TTTableOffset( pInputBufferInfo, MORT_TAG ); ulLength = TTTableLength( pInputBufferInfo, MORT_TAG ); ulLastOffset = ulOffset+ulLength; + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ulLastOffset < ulOffset) /* overflow check */ + return ERR_GENERIC; + } if (ulOffset == DIRECTORY_ERROR || ulLength == 0) /* nothing to map, we're done */ return NO_ERROR; @@ -68,7 +75,7 @@ int16 errCode = NO_ERROR; /* Static function to syncronize the Keep Glyph List with the Coverage list */ /* (add in values if necessary) */ /* ------------------------------------------------------------------- */ -static int16 UpdateKeepWithCoverage(TTFACC_FILEBUFFERINFO * pInputBufferInfo, uint8 * pabKeepGlyphs, uint16 usnGlyphs, uint16 fKeepFlag, uint32 ulBaseOffset, uint32 ulCoverageOffset, uint16 *pArray, uint16 usLookupType, uint16 usSubstFormat) +static int16 UpdateKeepWithCoverage(TTFACC_FILEBUFFERINFO * pInputBufferInfo, uint8 * pabKeepGlyphs, uint16 usnGlyphs, uint16 fKeepFlag, uint32 ulBaseOffset, uint32 ulCoverageOffset, uint16 *pArray, uint16 usArrayCount, uint16 usLookupType, uint16 usSubstFormat) { uint32 ulOffset; uint16 usCoverageFormat; @@ -82,6 +89,7 @@ GSUBRANGERECORD *pRangeRecordArray = NULL; uint16 i, j, k, l; uint16 usBytesRead; uint32 ulBytesRead; +uint32 ulAllocSize; int16 errCode = NO_ERROR; if ((ulCoverageOffset == 0) || (pArray == NULL)) @@ -97,7 +105,16 @@ int16 errCode = NO_ERROR; return errCode; ulOffset += usBytesRead; usCount = Coverage1.GlyphCount; - pGlyphIDArray = (uint16 *)Mem_Alloc(usCount * sizeof(uint16)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)usCount, (uint32)sizeof(uint16), &ulAllocSize) != S_OK) + return ERR_MEM; + pGlyphIDArray = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + pGlyphIDArray = (uint16 *)Mem_Alloc(usCount * sizeof(uint16)); + } if (pGlyphIDArray == NULL) return ERR_MEM; if ((errCode = ReadGenericRepeat( pInputBufferInfo, (uint8 *)pGlyphIDArray, WORD_CONTROL, ulOffset, &ulBytesRead, usCount, sizeof(uint16)) )!= NO_ERROR) @@ -111,7 +128,16 @@ int16 errCode = NO_ERROR; return errCode; ulOffset += usBytesRead; usCount = Coverage2.CoverageRangeCount; - pRangeRecordArray = (GSUBRANGERECORD *)Mem_Alloc(usCount * SIZEOF_GSUBRANGERECORD); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)usCount, (uint32)SIZEOF_GSUBRANGERECORD, &ulAllocSize) != S_OK) + return ERR_MEM; + pRangeRecordArray = (GSUBRANGERECORD *)Mem_Alloc(ulAllocSize); + } + else + { + pRangeRecordArray = (GSUBRANGERECORD *)Mem_Alloc(usCount * SIZEOF_GSUBRANGERECORD); + } if (pRangeRecordArray == NULL) return ERR_MEM; if ((errCode = ReadGenericRepeat( pInputBufferInfo, (uint8 *) pRangeRecordArray, GSUBRANGERECORD_CONTROL, ulOffset, &ulBytesRead, usCount, SIZEOF_GSUBRANGERECORD) )!= NO_ERROR) @@ -154,11 +180,28 @@ int16 errCode = NO_ERROR; case GSUBSingleLookupType: if (usSubstFormat == 1) { - if ((usGlyphID + (int16) *pArray) < usnGlyphs && pabKeepGlyphs[usGlyphID + (int16) *pArray] == 0) - pabKeepGlyphs[usGlyphID + (int16) *pArray] = (uint8)(fKeepFlag + 1); + if (TTF_SAFE_CHECKS_ENABLED()) + { + int32 sTargetGlyphID = (int32)usGlyphID + (int16) *pArray; + if (sTargetGlyphID >= 0 && sTargetGlyphID < usnGlyphs && pabKeepGlyphs[sTargetGlyphID] == 0) + pabKeepGlyphs[sTargetGlyphID] = (uint8)(fKeepFlag + 1); + } + else + { + if ((usGlyphID + (int16) *pArray) < usnGlyphs && pabKeepGlyphs[usGlyphID + (int16) *pArray] == 0) + pabKeepGlyphs[usGlyphID + (int16) *pArray] = (uint8)(fKeepFlag + 1); + } } else { + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (usGlyphCount >= usArrayCount) + { + errCode = ERR_INVALID_GSUB; + break; + } + } if (pArray[usGlyphCount] < usnGlyphs && pabKeepGlyphs[pArray[usGlyphCount]] == 0) pabKeepGlyphs[pArray[usGlyphCount]] = (uint8)(fKeepFlag + 1); } @@ -168,13 +211,33 @@ int16 errCode = NO_ERROR; uint16 usSequenceGlyphCount; uint16 *pausGlyphID = NULL; + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (usGlyphCount >= usArrayCount) + { + errCode = ERR_INVALID_GSUB; + break; + } + } if (pArray[usGlyphCount] == 0) break; ulOffset = ulBaseOffset + pArray[usGlyphCount]; if ((errCode = ReadWord( pInputBufferInfo, &usSequenceGlyphCount, ulOffset ) )!= NO_ERROR) break; ulOffset += sizeof(uint16); - pausGlyphID = (uint16 *)Mem_Alloc(usSequenceGlyphCount * sizeof(uint16)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)usSequenceGlyphCount, (uint32)sizeof(uint16), &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + pausGlyphID = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + pausGlyphID = (uint16 *)Mem_Alloc(usSequenceGlyphCount * sizeof(uint16)); + } if (pausGlyphID == NULL) { errCode = ERR_MEM; @@ -197,13 +260,33 @@ int16 errCode = NO_ERROR; uint16 usAlternateGlyphCount; uint16 *pausGlyphID = NULL; + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (usGlyphCount >= usArrayCount) + { + errCode = ERR_INVALID_GSUB; + break; + } + } if (pArray[usGlyphCount] == 0) break; ulOffset = ulBaseOffset + pArray[usGlyphCount]; if ((errCode = ReadWord( pInputBufferInfo, &usAlternateGlyphCount, ulOffset ) )!= NO_ERROR) break; ulOffset += sizeof(uint16); - pausGlyphID = (uint16 *)Mem_Alloc(usAlternateGlyphCount * sizeof(uint16)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)usAlternateGlyphCount, (uint32)sizeof(uint16), &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + pausGlyphID = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + pausGlyphID = (uint16 *)Mem_Alloc(usAlternateGlyphCount * sizeof(uint16)); + } if (pausGlyphID == NULL) { errCode = ERR_MEM; @@ -229,13 +312,33 @@ int16 errCode = NO_ERROR; uint16 *pausLigatureOffsetArray = NULL; GSUBLIGATURE GSUBLigature; + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (usGlyphCount >= usArrayCount) + { + errCode = ERR_INVALID_GSUB; + break; + } + } if (pArray[usGlyphCount] == 0) break; ulOffset = ulBaseOffset + pArray[usGlyphCount]; if ((errCode = ReadWord( pInputBufferInfo, &usLigatureCount, ulOffset ) )!= NO_ERROR) break; ulOffset += sizeof(uint16); - pausLigatureOffsetArray = (uint16 *)Mem_Alloc(usLigatureCount * sizeof(uint16)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)usLigatureCount, (uint32)sizeof(uint16), &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + pausLigatureOffsetArray = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + pausLigatureOffsetArray = (uint16 *)Mem_Alloc(usLigatureCount * sizeof(uint16)); + } if (pausLigatureOffsetArray == NULL) { errCode = ERR_MEM; @@ -247,7 +350,14 @@ int16 errCode = NO_ERROR; { if (pausLigatureOffsetArray[l] == 0) continue; - ulOffset = ulBaseOffset + (pArray)[usGlyphCount] + pausLigatureOffsetArray[l]; + if (TTF_SAFE_CHECKS_ENABLED()) + { + ulOffset = ulBaseOffset + (uint32)(pArray)[usGlyphCount] + (uint32)pausLigatureOffsetArray[l]; + } + else + { + ulOffset = ulBaseOffset + (pArray)[usGlyphCount] + pausLigatureOffsetArray[l]; + } if ((errCode = ReadGeneric( pInputBufferInfo, (uint8 *)&GSUBLigature, SIZEOF_GSUBLIGATURE, GSUBLIGATURE_CONTROL, ulOffset, &usBytesRead) )!= NO_ERROR) { Mem_Free (pausLigatureOffsetArray); @@ -260,7 +370,30 @@ int16 errCode = NO_ERROR; usLigatureGlyphID = GSUBLigature.GlyphID; if (usLigatureGlyphID >= usnGlyphs || pabKeepGlyphs[usLigatureGlyphID] != 0) continue; /* already in list, go to next ligature */ - pausCompGlyphID = (uint16 *)Mem_Alloc((usLigatureCompCount - 1) * sizeof(uint16)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (usLigatureCompCount < 2) /* need at least 2 components; 0 or 1 means no comp array */ + { + if (usLigatureCompCount == 1 && pabKeepGlyphs[usLigatureGlyphID] == 0) + pabKeepGlyphs[usLigatureGlyphID] = (uint8)(fKeepFlag+1); + continue; + } + } + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)(usLigatureCompCount - 1), (uint32)sizeof(uint16), &ulAllocSize) != S_OK) + { + Mem_Free (pausLigatureOffsetArray); + Mem_Free(pGlyphIDArray); + Mem_Free(pRangeRecordArray); + return ERR_MEM; + } + pausCompGlyphID = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + pausCompGlyphID = (uint16 *)Mem_Alloc((usLigatureCompCount - 1) * sizeof(uint16)); + } if (pausCompGlyphID == NULL) { Mem_Free (pausLigatureOffsetArray); @@ -385,6 +518,7 @@ uint32 ulCurrentOffset; uint32 ulLangSysOffset; uint16 usMaxLookupCount; uint32 ulOffset; +uint32 ulAllocSize; uint16 i, j, k; uint16 usBytesRead; uint32 ulBytesRead; @@ -420,7 +554,20 @@ int16 errCode = NO_ERROR; uint16 *SubstTableOffsetArray = NULL; ulOffset = ulHeaderOffset + GSUBHeader.LookupListOffset; - pGSUBLookupList = (GSUBLOOKUPLIST *)Mem_Alloc(SIZEOF_GSUBLOOKUPLIST + usMaxLookupCount * sizeof(uint16)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)usMaxLookupCount, (uint32)sizeof(uint16), &ulAllocSize) != S_OK || + UIntAdd32(ulAllocSize, (uint32)SIZEOF_GSUBLOOKUPLIST, &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + pGSUBLookupList = (GSUBLOOKUPLIST *)Mem_Alloc(ulAllocSize); + } + else + { + pGSUBLookupList = (GSUBLOOKUPLIST *)Mem_Alloc(SIZEOF_GSUBLOOKUPLIST + usMaxLookupCount * sizeof(uint16)); + } if (pGSUBLookupList == NULL) { errCode = ERR_MEM; @@ -449,7 +596,19 @@ int16 errCode = NO_ERROR; if (GSUBLookup.LookupType == GSUBContextLookupType) /* not looking for context lookups */ continue; usSubTableCount = GSUBLookup.SubTableCount; - SubstTableOffsetArray = (uint16 *)Mem_Alloc(sizeof(uint16) * usSubTableCount); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)sizeof(uint16), (uint32)usSubTableCount, &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + SubstTableOffsetArray = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + SubstTableOffsetArray = (uint16 *)Mem_Alloc(sizeof(uint16) * usSubTableCount); + } if (SubstTableOffsetArray == NULL) { errCode = ERR_MEM; @@ -481,7 +640,7 @@ int16 errCode = NO_ERROR; GSUBSINGLESUBSTFORMAT1 GSUBSubstTable; if ((errCode = ReadGeneric( pInputBufferInfo, (uint8 *)&GSUBSubstTable, SIZEOF_GSUBSINGLESUBSTFORMAT1, GSUBSINGLESUBSTFORMAT1_CONTROL, ulOffset, &usBytesRead) )== NO_ERROR) - errCode = UpdateKeepWithCoverage( pInputBufferInfo, pabKeepGlyphs, usnGlyphs, fKeepFlag, ulOffset, GSUBSubstTable.CoverageOffset , (uint16 *) &(GSUBSubstTable.DeltaGlyphID), GSUBLookup.LookupType, Format); + errCode = UpdateKeepWithCoverage( pInputBufferInfo, pabKeepGlyphs, usnGlyphs, fKeepFlag, ulOffset, GSUBSubstTable.CoverageOffset , (uint16 *) &(GSUBSubstTable.DeltaGlyphID), 1, GSUBLookup.LookupType, Format); break; } case 2: @@ -493,14 +652,26 @@ int16 errCode = NO_ERROR; if ((errCode = ReadGeneric( pInputBufferInfo, (uint8 *)&GSUBSubstTable, SIZEOF_GSUBSINGLESUBSTFORMAT2, GSUBSINGLESUBSTFORMAT2_CONTROL, ulOffset, &usBytesRead) )!= NO_ERROR) break; usGlyphCount = GSUBSubstTable.GlyphCount; - pGlyphIDArray = (uint16 *)Mem_Alloc(sizeof(uint16) * usGlyphCount); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)sizeof(uint16), (uint32)usGlyphCount, &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + pGlyphIDArray = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + pGlyphIDArray = (uint16 *)Mem_Alloc(sizeof(uint16) * usGlyphCount); + } if (pGlyphIDArray == NULL) { errCode = ERR_MEM; break; } if ((errCode = ReadGenericRepeat( pInputBufferInfo, (uint8 *)pGlyphIDArray, WORD_CONTROL, ulOffset + usBytesRead, &ulBytesRead, usGlyphCount, sizeof(uint16)) )== NO_ERROR) - errCode = UpdateKeepWithCoverage( pInputBufferInfo, pabKeepGlyphs, usnGlyphs, fKeepFlag, ulOffset, GSUBSubstTable.CoverageOffset , pGlyphIDArray, GSUBLookup.LookupType, Format); + errCode = UpdateKeepWithCoverage( pInputBufferInfo, pabKeepGlyphs, usnGlyphs, fKeepFlag, ulOffset, GSUBSubstTable.CoverageOffset , pGlyphIDArray, usGlyphCount, GSUBLookup.LookupType, Format); Mem_Free(pGlyphIDArray); break; } @@ -522,14 +693,26 @@ int16 errCode = NO_ERROR; if ((errCode = ReadGeneric( pInputBufferInfo, (uint8 *)&GSUBSubstTable, SIZEOF_GSUBMULTIPLESUBSTFORMAT1, GSUBMULTIPLESUBSTFORMAT1_CONTROL, ulOffset, &usBytesRead) )!= NO_ERROR) break; usCount = GSUBSubstTable.SequenceCount; - pOffsetArray = (uint16 *)Mem_Alloc(sizeof(uint16) * usCount); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)sizeof(uint16), (uint32)usCount, &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + pOffsetArray = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + pOffsetArray = (uint16 *)Mem_Alloc(sizeof(uint16) * usCount); + } if (pOffsetArray == NULL) { errCode = ERR_MEM; break; } if ((errCode = ReadGenericRepeat( pInputBufferInfo, (uint8 *)pOffsetArray, WORD_CONTROL, ulOffset + usBytesRead, &ulBytesRead, usCount, sizeof(uint16)) )== NO_ERROR) - errCode = UpdateKeepWithCoverage( pInputBufferInfo, pabKeepGlyphs, usnGlyphs, fKeepFlag, ulOffset, GSUBSubstTable.CoverageOffset , pOffsetArray, GSUBLookup.LookupType, Format); + errCode = UpdateKeepWithCoverage( pInputBufferInfo, pabKeepGlyphs, usnGlyphs, fKeepFlag, ulOffset, GSUBSubstTable.CoverageOffset , pOffsetArray, usCount, GSUBLookup.LookupType, Format); Mem_Free(pOffsetArray); break; } @@ -544,14 +727,26 @@ int16 errCode = NO_ERROR; if ((errCode = ReadGeneric( pInputBufferInfo, (uint8 *)&GSUBSubstTable, SIZEOF_GSUBALTERNATESUBSTFORMAT1, GSUBALTERNATESUBSTFORMAT1_CONTROL, ulOffset, &usBytesRead) )!= NO_ERROR) break; usCount = GSUBSubstTable.AlternateSetCount; - pOffsetArray = (uint16 *)Mem_Alloc(sizeof(uint16) * usCount); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)sizeof(uint16), (uint32)usCount, &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + pOffsetArray = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + pOffsetArray = (uint16 *)Mem_Alloc(sizeof(uint16) * usCount); + } if (pOffsetArray == NULL) { errCode = ERR_MEM; break; } if ((errCode = ReadGenericRepeat( pInputBufferInfo, (uint8 *)pOffsetArray, WORD_CONTROL, ulOffset + usBytesRead, &ulBytesRead, usCount, sizeof(uint16)) )== NO_ERROR) - errCode = UpdateKeepWithCoverage( pInputBufferInfo, pabKeepGlyphs, usnGlyphs, fKeepFlag, ulOffset, GSUBSubstTable.CoverageOffset , pOffsetArray, GSUBLookup.LookupType, Format); + errCode = UpdateKeepWithCoverage( pInputBufferInfo, pabKeepGlyphs, usnGlyphs, fKeepFlag, ulOffset, GSUBSubstTable.CoverageOffset , pOffsetArray, usCount, GSUBLookup.LookupType, Format); Mem_Free(pOffsetArray); break; } @@ -566,14 +761,26 @@ int16 errCode = NO_ERROR; if ((errCode = ReadGeneric( pInputBufferInfo, (uint8 *)&GSUBSubstTable, SIZEOF_GSUBLIGATURESUBSTFORMAT1, GSUBLIGATURESUBSTFORMAT1_CONTROL, ulOffset, &usBytesRead) )!= NO_ERROR) break; usCount = GSUBSubstTable.LigatureSetCount; - pOffsetArray = (uint16 *)Mem_Alloc(sizeof(uint16) * usCount); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)sizeof(uint16), (uint32)usCount, &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + pOffsetArray = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + pOffsetArray = (uint16 *)Mem_Alloc(sizeof(uint16) * usCount); + } if (pOffsetArray == NULL) { errCode = ERR_MEM; break; } if ((errCode = ReadGenericRepeat( pInputBufferInfo, (uint8 *)pOffsetArray, WORD_CONTROL, ulOffset + usBytesRead, &ulBytesRead, usCount, sizeof(uint16)) )== NO_ERROR) - errCode = UpdateKeepWithCoverage( pInputBufferInfo, pabKeepGlyphs, usnGlyphs, fKeepFlag, ulOffset, GSUBSubstTable.CoverageOffset , pOffsetArray, GSUBLookup.LookupType, Format); + errCode = UpdateKeepWithCoverage( pInputBufferInfo, pabKeepGlyphs, usnGlyphs, fKeepFlag, ulOffset, GSUBSubstTable.CoverageOffset , pOffsetArray, usCount, GSUBLookup.LookupType, Format); Mem_Free(pOffsetArray); break; } @@ -606,7 +813,19 @@ int16 errCode = NO_ERROR; if ((errCode = ReadGeneric( pInputBufferInfo, (uint8 *)&JSTFHeader, SIZEOF_JSTFHEADER, JSTFHEADER_CONTROL, ulHeaderOffset, &usBytesRead ) )!= NO_ERROR) break; - ScriptRecordArray = (JSTFSCRIPTRECORD *)Mem_Alloc(SIZEOF_JSTFSCRIPTRECORD * JSTFHeader.ScriptCount); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)SIZEOF_JSTFSCRIPTRECORD, (uint32)JSTFHeader.ScriptCount, &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + ScriptRecordArray = (JSTFSCRIPTRECORD *)Mem_Alloc(ulAllocSize); + } + else + { + ScriptRecordArray = (JSTFSCRIPTRECORD *)Mem_Alloc(SIZEOF_JSTFSCRIPTRECORD * JSTFHeader.ScriptCount); + } if (ScriptRecordArray == NULL) { errCode = ERR_MEM; @@ -631,7 +850,19 @@ int16 errCode = NO_ERROR; if ((errCode = ReadGeneric( pInputBufferInfo, (uint8 *)&JSTFExtenderGlyph, SIZEOF_JSTFEXTENDERGLYPH, JSTFEXTENDERGLYPH_CONTROL, ulOffset, &usBytesRead) )!= NO_ERROR) break; - GlyphIDArray = (uint16 *)Mem_Alloc((sizeof(uint16) * JSTFExtenderGlyph.ExtenderGlyphCount)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ULongMult32((uint32)sizeof(uint16), (uint32)JSTFExtenderGlyph.ExtenderGlyphCount, &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + GlyphIDArray = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + GlyphIDArray = (uint16 *)Mem_Alloc((sizeof(uint16) * JSTFExtenderGlyph.ExtenderGlyphCount)); + } if (GlyphIDArray == NULL) { errCode = ERR_MEM; diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/intsafe_private_copy.h b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/intsafe_private_copy.h index 7128dfe5eb4..a1eeacbf717 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/intsafe_private_copy.h +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/intsafe_private_copy.h @@ -138,4 +138,18 @@ ULongSub( return hr; } + +// +// Convenience wrappers for the codebase's uint32 type (unsigned long). +// These forward to the UINT/ULONG versions above, bridging the type mismatch. +// +static __inline HRESULT UIntAdd32(uint32 a, uint32 b, uint32 *pResult) +{ + return UIntAdd((UINT)a, (UINT)b, (UINT *)pResult); +} +static __inline HRESULT ULongMult32(uint32 a, uint32 b, uint32 *pResult) +{ + return ULongMult((ULONG)a, (ULONG)b, (ULONG *)pResult); +} + #endif //__INTSAFE_PRIVATE_COPY_H diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/makeglst.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/makeglst.cpp index 6ea0dde0f34..d25c606011c 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/makeglst.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/makeglst.cpp @@ -148,6 +148,8 @@ Thanks, #include "ttferror.h" /* for error codes */ #include "ttfdelta.h" #include "sfntoff.h" +#include "intsafe_private_copy.h" +#include "ttf_safe_checks.h" #define WIN_ANSI_MIDDLEDOT 0xB7 #define WIN_ANSI_BULLET 0x2219 @@ -176,7 +178,18 @@ USHORT usHighByte; { if (usFirstChar >= 0xf000) { - if (*ppulKeepSymbolCodeList = (CHAR_ID *)Mem_Alloc(usCharListCount * sizeof(CHAR_ID))) + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)usCharListCount, (uint32)sizeof(CHAR_ID), &ulAllocSize) != S_OK) + return ERR_MEM; + *ppulKeepSymbolCodeList = (CHAR_ID *)Mem_Alloc(ulAllocSize); + } + else + { + *ppulKeepSymbolCodeList = (CHAR_ID *)Mem_Alloc(usCharListCount * sizeof(CHAR_ID)); + } + if (*ppulKeepSymbolCodeList) { /* In user range -> this is a symbol font so go ahead offseting it */ usHighByte = (unsigned short)(usFirstChar & 0xff00); @@ -220,7 +233,18 @@ int16 EnsureNonEmptyGlyfTable( uint32 * aulLoca; /* allocate memory for and read loca table */ - aulLoca = (uint32 *)Mem_Alloc( (usGlyphCount + 1) * sizeof( uint32 )); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulLocaCount = (uint32)usGlyphCount + 1; + uint32 ulAllocSize; + if (ULongMult32(ulLocaCount, (uint32)sizeof( uint32 ), &ulAllocSize) != S_OK) + return ERR_MEM; + aulLoca = (uint32 *)Mem_Alloc( ulAllocSize ); + } + else + { + aulLoca = (uint32 *)Mem_Alloc( (usGlyphCount + 1) * sizeof(uint32) ); + } if ( aulLoca == NULL ) return ERR_MEM; @@ -328,8 +352,22 @@ CMAP_SUBHEADER_GEN CmapSubHeader; if ((ulGlyfOffset = TTTableOffset( pInputBufferInfo, GLYF_TAG )) == DIRECTORY_ERROR) return (ERR_MISSING_GLYF); - usnMaxComponents = Maxp.maxComponentElements * Maxp.maxComponentDepth; /* maximum total possible */ - pausComponents = (uint16 *)Mem_Alloc(usnMaxComponents * sizeof(uint16)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulMaxComp, ulAllocSize; + if (ULongMult32((uint32)Maxp.maxComponentElements, (uint32)Maxp.maxComponentDepth, &ulMaxComp) != S_OK || + ULongMult32(ulMaxComp, (uint32)sizeof(uint16), &ulAllocSize) != S_OK) + return(ERR_MEM); + if (ulMaxComp > (uint32)USHRT_MAX) + return(ERR_INVALID_MAXP); + usnMaxComponents = (uint16)ulMaxComp; + pausComponents = (uint16 *)Mem_Alloc(ulAllocSize); + } + else + { + usnMaxComponents = Maxp.maxComponentElements * Maxp.maxComponentDepth; + pausComponents = (uint16 *)Mem_Alloc(usnMaxComponents * sizeof(uint16)); + } if (pausComponents == NULL) return(ERR_MEM); diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modcmap.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modcmap.cpp index c0922b8dc52..b79ac87097d 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modcmap.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modcmap.cpp @@ -14,6 +14,8 @@ #include /* for qsort */ #include "typedefs.h" +#include "intsafe_private_copy.h" +#include "ttf_safe_checks.h" #include "ttff.h" #include "ttfacc.h" #include "ttfcntrl.h" @@ -46,7 +48,17 @@ struct cmapoffsetrecordkeeper /* housekeeping structure */ PRIVATE int16 InitCmapOffsetArray(PCMAPOFFSETRECORDKEEPER pKeeper, uint16 usRecordCount) { - pKeeper->pCmapOffsetArray = (CmapOffsetRecord *) Mem_Alloc(usRecordCount * sizeof(*(pKeeper->pCmapOffsetArray))); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)usRecordCount, (uint32)sizeof(*(pKeeper->pCmapOffsetArray)), &ulAllocSize) != S_OK) + return ERR_MEM; + pKeeper->pCmapOffsetArray = (CmapOffsetRecord *) Mem_Alloc(ulAllocSize); + } + else + { + pKeeper->pCmapOffsetArray = (CmapOffsetRecord *) Mem_Alloc(usRecordCount * sizeof(*(pKeeper->pCmapOffsetArray))); + } if (pKeeper->pCmapOffsetArray == NULL) return ERR_MEM; pKeeper->usCmapOffsetArrayLen = usRecordCount; @@ -152,7 +164,17 @@ uint16 i,j; uint16 usBytesRead; uint16 usPadBytes; - pIndexArray = (IndexOffset *) Mem_Alloc(usSubTableCount * sizeof(*pIndexArray)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)usSubTableCount, (uint32)sizeof(*pIndexArray), &ulAllocSize) != S_OK) + return ERR_MEM; + pIndexArray = (IndexOffset *) Mem_Alloc(ulAllocSize); + } + else + { + pIndexArray = (IndexOffset *) Mem_Alloc(usSubTableCount * sizeof(*pIndexArray)); + } if (pIndexArray == NULL) return ERR_MEM; @@ -384,7 +406,17 @@ uint16 usBytesRead; return ERR_INVALID_CMAP; /* huh?*/ usSubTableCount = GetCmapSubtableCount(pOutputBufferInfo, ulCmapOffset); - pCmapTableLoc = (CMAP_TABLELOC *)Mem_Alloc(SIZEOF_CMAP_TABLELOC * usSubTableCount); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)SIZEOF_CMAP_TABLELOC, (uint32)usSubTableCount, &ulAllocSize) != S_OK) + return ERR_MEM; + pCmapTableLoc = (CMAP_TABLELOC *)Mem_Alloc(ulAllocSize); + } + else + { + pCmapTableLoc = (CMAP_TABLELOC *)Mem_Alloc(SIZEOF_CMAP_TABLELOC * usSubTableCount); + } if (pCmapTableLoc == NULL) return ERR_MEM; ulCmapSubTableDirOffset = ulCmapOffset + GetGenericSize( CMAP_HEADER_CONTROL ); @@ -434,8 +466,24 @@ uint16 usBytesRead; if (errCode != NO_ERROR) break; - NewFormat4Segments = (FORMAT4_SEGMENTS *) Mem_Alloc( (usnCharGlyphMapListCount+1) * SIZEOF_FORMAT4_SEGMENTS ); /* add one for the extra dummy segment */ - NewFormat4GlyphIdArray = (GLYPH_ID *) Mem_Alloc( usnCharGlyphMapListCount * sizeof( *NewFormat4GlyphIdArray ) ); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulSegAllocSize, ulGlyphAllocSize; + uint32 ulSegCount32 = (uint32)usnCharGlyphMapListCount + 1; /* add one for extra dummy segment */ + if (ULongMult32(ulSegCount32, (uint32)SIZEOF_FORMAT4_SEGMENTS, &ulSegAllocSize) != S_OK || + ULongMult32((uint32)usnCharGlyphMapListCount, (uint32)sizeof( *NewFormat4GlyphIdArray ), &ulGlyphAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + NewFormat4Segments = (FORMAT4_SEGMENTS *) Mem_Alloc( ulSegAllocSize ); + NewFormat4GlyphIdArray = (GLYPH_ID *) Mem_Alloc( ulGlyphAllocSize ); + } + else + { + NewFormat4Segments = (FORMAT4_SEGMENTS *) Mem_Alloc( (usnCharGlyphMapListCount + 1) * SIZEOF_FORMAT4_SEGMENTS ); /* add one for the extra dummy segment */ + NewFormat4GlyphIdArray = (GLYPH_ID *) Mem_Alloc( usnCharGlyphMapListCount * sizeof( *NewFormat4GlyphIdArray ) ); + } if ( NewFormat4Segments == NULL || NewFormat4GlyphIdArray == NULL ) { @@ -453,7 +501,7 @@ uint16 usBytesRead; if (CmapFormat4.length <= CmapSubHeader.length) /* if the new length is smaller than the old, we can write it in the old place */ { - if (pCmapTableLoc[i].platformID == MS_PLATFORMID) /* only applies to this platform */ + if (pCmapTableLoc[i].platformID == MS_PLATFORMID && (!TTF_SAFE_CHECKS_ENABLED() || usnCharGlyphMapListCount > 0)) /* only applies to this platform */ { *pOS2MinChr = pCharGlyphMapList[0].usCharCode; *pOS2MaxChr = pCharGlyphMapList[usnCharGlyphMapListCount-1].usCharCode; @@ -482,7 +530,20 @@ uint16 usBytesRead; if (errCode != NO_ERROR) break; - NewFormat12Groups = (FORMAT12_GROUPS *) Mem_Alloc( (ulnCharGlyphMapListCount) * SIZEOF_FORMAT12_GROUPS ); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)ulnCharGlyphMapListCount, (uint32)SIZEOF_FORMAT12_GROUPS, &ulAllocSize) != S_OK) + { + errCode = ERR_MEM; + break; + } + NewFormat12Groups = (FORMAT12_GROUPS *) Mem_Alloc(ulAllocSize); + } + else + { + NewFormat12Groups = (FORMAT12_GROUPS *) Mem_Alloc(ulnCharGlyphMapListCount * SIZEOF_FORMAT12_GROUPS); + } if ( NewFormat12Groups == NULL) { errCode = ERR_MEM; @@ -495,7 +556,7 @@ uint16 usBytesRead; /* Donald, if you don't care if the Cmap subtable grows, you could comment out the next line */ if (CmapFormat12.length <= CmapSubHeader.length) /* if the new length is smaller than the old, we can write it in the old place */ { - if (pCmapTableLoc[i].platformID == MS_PLATFORMID) /* only applies to this platform */ + if (pCmapTableLoc[i].platformID == MS_PLATFORMID && (!TTF_SAFE_CHECKS_ENABLED() || ulnCharGlyphMapListCount > 0)) /* only applies to this platform */ { *pOS2MinChr = (uint16)pCharGlyphMapListEx[0].ulCharCode; *pOS2MaxChr = (uint16)pCharGlyphMapListEx[ulnCharGlyphMapListCount-1].ulCharCode; diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modglyf.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modglyf.cpp index eaf87f2de70..0cef3bbf07b 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modglyf.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modglyf.cpp @@ -23,6 +23,8 @@ #include "util.h" #include "modglyf.h" #include "ttferror.h" /* for error codes */ +#include "intsafe_private_copy.h" +#include "ttf_safe_checks.h" /* ------------------------------------------------------------------- */ /* this function modifies the glyf and loca tables by copying only glyfs @@ -60,7 +62,18 @@ HEAD Head; /* allocate memory for and read loca table */ - aulLoca = (uint32 *)Mem_Alloc( (usGlyphCount + 1) * sizeof( uint32 )); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulLocaCount = (uint32)usGlyphCount + 1; + uint32 ulAllocSize; + if (ULongMult32(ulLocaCount, (uint32)sizeof( uint32 ), &ulAllocSize) != S_OK) + return ERR_MEM; + aulLoca = (uint32 *)Mem_Alloc( ulAllocSize ); + } + else + { + aulLoca = (uint32 *)Mem_Alloc( (usGlyphCount + 1) * sizeof( uint32 )); + } if ( aulLoca == NULL ) return ERR_MEM; @@ -120,18 +133,56 @@ HEAD Head; if ( ulGlyphLength ) { - if ((errCode = CopyBlockOver( pOutputBufferInfo, pInputBufferInfo, ulOutGlyfOffset + ulOutLoca, - ulGlyfOffset + aulLoca[ i ], ulGlyphLength )) != NO_ERROR) - break; + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulOutOff, ulInOff; + if (UIntAdd32(ulOutGlyfOffset, ulOutLoca, &ulOutOff) != S_OK || + UIntAdd32(ulGlyfOffset, aulLoca[i], &ulInOff) != S_OK) + { + errCode = ERR_GENERIC; + break; + } + if ((errCode = CopyBlockOver( pOutputBufferInfo, pInputBufferInfo, ulOutOff, + ulInOff, ulGlyphLength )) != NO_ERROR) + break; + } + else + { + if ((errCode = CopyBlockOver( pOutputBufferInfo, pInputBufferInfo, ulOutGlyfOffset + ulOutLoca, + ulGlyfOffset + aulLoca[ i ], ulGlyphLength )) != NO_ERROR) + break; + } } } assert((ulOutLoca & 1) != 1); aulLoca[ i ] = ulOutLoca; ulOutLoca += ulGlyphLength; + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ulOutLoca < ulGlyphLength) + { + errCode = ERR_GENERIC; + break; + } + } if (ulOutLoca & 1) { /* the glyph offset is on an odd-byte boundry. get ready for next time */ - if ((errCode = WriteByte( pOutputBufferInfo, 0, ulOutGlyfOffset + ulOutLoca)) != NO_ERROR) - break; + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulPadOff; + if (UIntAdd32(ulOutGlyfOffset, ulOutLoca, &ulPadOff) != S_OK) + { + errCode = ERR_GENERIC; + break; + } + if ((errCode = WriteByte( pOutputBufferInfo, 0, ulPadOff)) != NO_ERROR) + break; + } + else + { + if ((errCode = WriteByte( pOutputBufferInfo, 0, ulOutGlyfOffset + ulOutLoca)) != NO_ERROR) + break; + } ++ulOutLoca; } } diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modsbit.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modsbit.cpp index b50aeb50257..c6ea1bd42d9 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modsbit.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modsbit.cpp @@ -24,6 +24,61 @@ // mark them as SecurityCritical because they have unverfiable code and we could // not negotiate with the CRT team to fix it. #include "intsafe_private_copy.h" +#include "ttf_safe_checks.h" + +/* ------------------------------------------------------------------- */ + +/* Helper function for bounds checking EBDT buffer ranges */ +PRIVATE int16 CheckEBDTBounds(uint32 ulOffset, uint32 ulLength, uint32 ulAllocatedSize) +{ + uint32 ulEndOffset; + + /* Check for integer overflow in offset + length */ + if (FAILED(UIntAdd32(ulOffset, ulLength, &ulEndOffset))) + { + return ERR_INVALID_EBLC; + } + + /* Check if write would exceed buffer bounds */ + if (ulEndOffset > ulAllocatedSize) + { + return ERR_INVALID_EBLC; + } + + return NO_ERROR; +} + +/* Helper function for copying EBDT bytes with read/write bounds and overflow checks */ +PRIVATE int16 CopyEBDTBytesChecked( + TTFACC_FILEBUFFERINFO *pInputBufferInfo, + uint8 *puchEBDTDestPtr, + uint32 ulEBDTSrcOffset, + uint32 ulEBDTAllocatedSize, + uint32 ulDestImageDataOffset, + uint32 ulDestGlyphOffset, + uint32 ulSrcImageDataOffset, + uint32 ulSrcGlyphOffset, + uint32 ulGlyphLength) +{ + uint32 ulWriteOffset; + uint32 ulReadRelativeOffset; + uint32 ulReadOffset; + int16 errCode; + + if (FAILED(UIntAdd32(ulDestImageDataOffset, ulDestGlyphOffset, &ulWriteOffset))) + return ERR_INVALID_EBLC; + if ((errCode = CheckEBDTBounds(ulWriteOffset, ulGlyphLength, ulEBDTAllocatedSize)) != NO_ERROR) + return errCode; + + if (FAILED(UIntAdd32(ulSrcImageDataOffset, ulSrcGlyphOffset, &ulReadRelativeOffset))) + return ERR_INVALID_EBLC; + if ((errCode = CheckEBDTBounds(ulReadRelativeOffset, ulGlyphLength, ulEBDTAllocatedSize)) != NO_ERROR) + return errCode; + if (FAILED(UIntAdd32(ulEBDTSrcOffset, ulReadRelativeOffset, &ulReadOffset))) + return ERR_INVALID_EBLC; + + return ReadBytes(pInputBufferInfo, puchEBDTDestPtr + ulWriteOffset, ulReadOffset, ulGlyphLength); +} /* ------------------------------------------------------------------- */ @@ -57,11 +112,30 @@ PRIVATE int16 RecordGlyphOffset(PGLYPHOFFSETRECORDKEEPER pKeeper, if (pKeeper->ulNextArrayIndex >= pKeeper->ulOffsetArrayLen) { - pKeeper->pGlyphOffsetArray = (GlyphOffsetRecord *) Mem_ReAlloc(pKeeper->pGlyphOffsetArray, (pKeeper->ulOffsetArrayLen + 100) * sizeof(*(pKeeper->pGlyphOffsetArray))); - if (pKeeper->pGlyphOffsetArray == NULL) - return ERR_MEM; /* ("EBLC: Not enough memory to allocate Offset Array."); */ - memset((char *)(pKeeper->pGlyphOffsetArray) + (sizeof(*(pKeeper->pGlyphOffsetArray)) * pKeeper->ulOffsetArrayLen), '\0', sizeof(*(pKeeper->pGlyphOffsetArray)) * 100); - pKeeper->ulOffsetArrayLen += 100; + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulNewLen; + uint32 ulAllocSize; + if (UIntAdd32(pKeeper->ulOffsetArrayLen, 100, &ulNewLen) != S_OK || + ULongMult32(ulNewLen, (uint32)sizeof(*(pKeeper->pGlyphOffsetArray)), &ulAllocSize) != S_OK) + return ERR_MEM; + { + GlyphOffsetRecord *pNewArray = (GlyphOffsetRecord *) Mem_ReAlloc(pKeeper->pGlyphOffsetArray, ulAllocSize); + if (pNewArray == NULL) + return ERR_MEM; /* ("EBLC: Not enough memory to allocate Offset Array."); */ + pKeeper->pGlyphOffsetArray = pNewArray; + } + memset((char *)(pKeeper->pGlyphOffsetArray) + (sizeof(*(pKeeper->pGlyphOffsetArray)) * pKeeper->ulOffsetArrayLen), '\0', sizeof(*(pKeeper->pGlyphOffsetArray)) * 100); + pKeeper->ulOffsetArrayLen = ulNewLen; + } + else + { + pKeeper->pGlyphOffsetArray = (GlyphOffsetRecord *) Mem_ReAlloc(pKeeper->pGlyphOffsetArray, (pKeeper->ulOffsetArrayLen + 100) * sizeof(*(pKeeper->pGlyphOffsetArray))); + if (pKeeper->pGlyphOffsetArray == NULL) + return ERR_MEM; /* ("EBLC: Not enough memory to allocate Offset Array."); */ + memset((char *)(pKeeper->pGlyphOffsetArray) + (sizeof(*(pKeeper->pGlyphOffsetArray)) * pKeeper->ulOffsetArrayLen), '\0', sizeof(*(pKeeper->pGlyphOffsetArray)) * 100); + pKeeper->ulOffsetArrayLen += 100; + } } pKeeper->pGlyphOffsetArray[pKeeper->ulNextArrayIndex].ulOldOffset = ulOldOffset; pKeeper->pGlyphOffsetArray[pKeeper->ulNextArrayIndex].ImageDataBlock.ulNewImageDataOffset = pImageDataBlock->ulNewImageDataOffset ; @@ -123,6 +197,7 @@ PRIVATE int16 FixSbitSubTables(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInfo, / uint32 *pulEBDTBytesWritten, /* number of bytes written to the EBDT table buffer */ uint8 *puchEBDTDestPtr, /* EBDT data byffer */ uint32 ulEBDTSrcOffset, /* beginning of EBDT table in the InputBufferInfo */ + uint32 ulEBDTAllocatedSize, /* allocated size of EBDT buffer for bounds checking */ PGLYPHOFFSETRECORDKEEPER pKeeper) /* structure to keep track of multiply referenced EBDT data */ { INDEXSUBHEADER IndexSubHeader; @@ -208,11 +283,23 @@ PRIVATE int16 FixSbitSubTables(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInfo, / return errCode; ulOffset += usBytesRead; IndexSubTable1.header.ulImageDataOffset = ulCurrentImageDataOffset; /* set to the new one */ - if (FAILED(UIntAdd(ulLocalCurrentOffset,SIZEOF_INDEXSUBTABLE1,(UINT *)&ulIndexSubtableAfterEnd)) || - (ulIndexSubtableAfterEnd > *pulIndexSubTableSize) - ) + if (TTF_SAFE_CHECKS_ENABLED()) { - return ERR_INVALID_EBLC; + if (FAILED(UIntAdd32(ulLocalCurrentOffset, SIZEOF_INDEXSUBTABLE1, &ulIndexSubtableAfterEnd)) || + (ulIndexSubtableAfterEnd > *pulIndexSubTableSize) + ) + { + return ERR_INVALID_EBLC; + } + } + else + { + if (FAILED(UIntAdd(ulLocalCurrentOffset, SIZEOF_INDEXSUBTABLE1, (UINT *)&ulIndexSubtableAfterEnd)) || + (ulIndexSubtableAfterEnd > *pulIndexSubTableSize) + ) + { + return ERR_INVALID_EBLC; + } } memcpy(*ppuchIndexSubTable + ulLocalCurrentOffset, &IndexSubTable1, SIZEOF_INDEXSUBTABLE1); ulTableSize = SIZEOF_INDEXSUBTABLE1; @@ -233,12 +320,25 @@ PRIVATE int16 FixSbitSubTables(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInfo, / { /* if the indexTableSize length field was incorrect */ /* use 2* to account for the extra offset at the end */ - if (FAILED(UIntAdd(ulLocalCurrentOffset, ulTableSize, (UINT *)&ulIndexSubtableAfterEnd)) || - FAILED(UIntAdd(ulIndexSubtableAfterEnd, 2*sizeof(ulNewGlyphOffset), (UINT *)&ulIndexSubtableAfterEnd)) || - (ulIndexSubtableAfterEnd > *pulIndexSubTableSize) - ) + if (TTF_SAFE_CHECKS_ENABLED()) { - return ERR_INVALID_EBLC; + if (FAILED(UIntAdd32(ulLocalCurrentOffset, ulTableSize, &ulIndexSubtableAfterEnd)) || + FAILED(UIntAdd32(ulIndexSubtableAfterEnd, 2*sizeof(ulNewGlyphOffset), &ulIndexSubtableAfterEnd)) || + (ulIndexSubtableAfterEnd > *pulIndexSubTableSize) + ) + { + return ERR_INVALID_EBLC; + } + } + else + { + if (FAILED(UIntAdd(ulLocalCurrentOffset, ulTableSize, (UINT *)&ulIndexSubtableAfterEnd)) || + FAILED(UIntAdd(ulIndexSubtableAfterEnd, 2*sizeof(ulNewGlyphOffset), (UINT *)&ulIndexSubtableAfterEnd)) || + (ulIndexSubtableAfterEnd > *pulIndexSubTableSize) + ) + { + return ERR_INVALID_EBLC; + } } memcpy(*ppuchIndexSubTable + ulLocalCurrentOffset+ulTableSize, &ulNewGlyphOffset, @@ -254,10 +354,28 @@ PRIVATE int16 FixSbitSubTables(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInfo, / ulGlyphLength = ulNextGlyphOffset-ulOldGlyphOffset; if (DoCopy) { - if ((errCode = ReadBytes((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, puchEBDTDestPtr + IndexSubTable1.header.ulImageDataOffset + ulNewGlyphOffset, - ulEBDTSrcOffset + ulOldImageDataOffset + ulOldGlyphOffset, - ulGlyphLength)) != NO_ERROR) - return errCode; + if (TTF_SAFE_CHECKS_ENABLED()) + { + errCode = CopyEBDTBytesChecked( + (TTFACC_FILEBUFFERINFO *)pInputBufferInfo, + puchEBDTDestPtr, + ulEBDTSrcOffset, + ulEBDTAllocatedSize, + IndexSubTable1.header.ulImageDataOffset, + ulNewGlyphOffset, + ulOldImageDataOffset, + ulOldGlyphOffset, + ulGlyphLength); + if (errCode != NO_ERROR) + return errCode; + } + else + { + if ((errCode = ReadBytes((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, puchEBDTDestPtr + IndexSubTable1.header.ulImageDataOffset + ulNewGlyphOffset, + ulEBDTSrcOffset + ulOldImageDataOffset + ulOldGlyphOffset, + ulGlyphLength)) != NO_ERROR) + return errCode; + } } ulNewGlyphOffset += ulGlyphLength; } @@ -300,14 +418,38 @@ PRIVATE int16 FixSbitSubTables(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInfo, / /* check if there are any gaps */ if (IndexSubTable5.ulNumGlyphs != (uint32) (*pusNewLastGlyphIndex - *pusNewFirstGlyphIndex + 1)) /* not sparse, we got everyone */ { - ausGlyphCodeArray = (uint16 *) Mem_Alloc(sizeof(uint16) * IndexSubTable5.ulNumGlyphs); - /* Need to enlarge pointer too by the difference between Format 2 and format 5 */ - *pulIndexSubTableSize += (IndexSubTable5.ulNumGlyphs * sizeof(uint16) + sizeof(uint32)); - *ppuchIndexSubTable = (uint8 *)Mem_ReAlloc(*ppuchIndexSubTable, *pulIndexSubTableSize); - if ((ausGlyphCodeArray == NULL) || (*ppuchIndexSubTable == NULL)) + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulGlyphAllocSize, ulExtraSize; + if (ULongMult32((uint32)sizeof(uint16), IndexSubTable5.ulNumGlyphs, &ulGlyphAllocSize) != S_OK) + return ERR_MEM; + ausGlyphCodeArray = (uint16 *) Mem_Alloc(ulGlyphAllocSize); + /* Need to enlarge pointer too by the difference between Format 2 and format 5 */ + if (UIntAdd32(ulGlyphAllocSize, (uint32)sizeof(uint32), &ulExtraSize) != S_OK || + UIntAdd32(*pulIndexSubTableSize, ulExtraSize, pulIndexSubTableSize) != S_OK) + { + Mem_Free(ausGlyphCodeArray); + return ERR_MEM; + } + uint8 *pNewSubTable = (uint8 *)Mem_ReAlloc(*ppuchIndexSubTable, *pulIndexSubTableSize); + if ((ausGlyphCodeArray == NULL) || (pNewSubTable == NULL)) + { + Mem_Free(ausGlyphCodeArray); + return ERR_MEM; + } + *ppuchIndexSubTable = pNewSubTable; + } + else { - Mem_Free(ausGlyphCodeArray); - return ERR_MEM; + ausGlyphCodeArray = (uint16 *) Mem_Alloc(sizeof(uint16) * IndexSubTable5.ulNumGlyphs); + /* Need to enlarge pointer too by the difference between Format 2 and format 5 */ + *pulIndexSubTableSize += (IndexSubTable5.ulNumGlyphs * sizeof(uint16) + sizeof(uint32)); + *ppuchIndexSubTable = (uint8 *)Mem_ReAlloc(*ppuchIndexSubTable, *pulIndexSubTableSize); + if ((ausGlyphCodeArray == NULL) || (*ppuchIndexSubTable == NULL)) + { + Mem_Free(ausGlyphCodeArray); + return ERR_MEM; + } } } @@ -320,12 +462,33 @@ PRIVATE int16 FixSbitSubTables(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInfo, / ausGlyphCodeArray[IndexSubTable5.ulNumGlyphs++] = i; if (DoCopy) /* if this glyph is supposed to be kept, copy glyph. */ { - if ((errCode = ReadBytes( (TTFACC_FILEBUFFERINFO *)pInputBufferInfo, puchEBDTDestPtr + IndexSubTable2.header.ulImageDataOffset + ulNewGlyphOffset, - ulEBDTSrcOffset + ulOldImageDataOffset + ulOldGlyphOffset, - ulGlyphLength)) != NO_ERROR) + if (TTF_SAFE_CHECKS_ENABLED()) + { + errCode = CopyEBDTBytesChecked( + (TTFACC_FILEBUFFERINFO *)pInputBufferInfo, + puchEBDTDestPtr, + ulEBDTSrcOffset, + ulEBDTAllocatedSize, + IndexSubTable2.header.ulImageDataOffset, + ulNewGlyphOffset, + ulOldImageDataOffset, + ulOldGlyphOffset, + ulGlyphLength); + if (errCode != NO_ERROR) + { + Mem_Free(ausGlyphCodeArray); /* in case it got allocated */ + return errCode; + } + } + else { - Mem_Free(ausGlyphCodeArray); /* in case it got allocated */ - return errCode; + if ((errCode = ReadBytes((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, puchEBDTDestPtr + IndexSubTable2.header.ulImageDataOffset + ulNewGlyphOffset, + ulEBDTSrcOffset + ulOldImageDataOffset + ulOldGlyphOffset, + ulGlyphLength)) != NO_ERROR) + { + Mem_Free(ausGlyphCodeArray); /* in case it got allocated */ + return errCode; + } } } ulNewGlyphOffset += ulGlyphLength; @@ -340,14 +503,34 @@ PRIVATE int16 FixSbitSubTables(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInfo, / if (ausGlyphCodeArray != NULL) /* we changed to format 5 */ { - ulTableSize = SIZEOF_INDEXSUBTABLE5 + sizeof(*ausGlyphCodeArray) * IndexSubTable5.ulNumGlyphs; - if (ulLocalCurrentOffset + ulTableSize > *pulIndexSubTableSize) + if (TTF_SAFE_CHECKS_ENABLED()) { - Mem_Free(ausGlyphCodeArray); /* in case it got allocated */ - return ERR_INVALID_EBLC; + uint32 ulGlyphDataSize; + if (ULongMult32((uint32)sizeof(*ausGlyphCodeArray), IndexSubTable5.ulNumGlyphs, &ulGlyphDataSize) != S_OK || + UIntAdd32(SIZEOF_INDEXSUBTABLE5, ulGlyphDataSize, &ulTableSize) != S_OK) + { + Mem_Free(ausGlyphCodeArray); + return ERR_INVALID_EBLC; + } + if (ulLocalCurrentOffset + ulTableSize > *pulIndexSubTableSize) + { + Mem_Free(ausGlyphCodeArray); /* in case it got allocated */ + return ERR_INVALID_EBLC; + } + memcpy(*ppuchIndexSubTable + ulLocalCurrentOffset, &IndexSubTable5, SIZEOF_INDEXSUBTABLE5); + memcpy(*ppuchIndexSubTable + ulLocalCurrentOffset + SIZEOF_INDEXSUBTABLE5, ausGlyphCodeArray, ulGlyphDataSize); + } + else + { + ulTableSize = SIZEOF_INDEXSUBTABLE5 + sizeof(*ausGlyphCodeArray) * IndexSubTable5.ulNumGlyphs; + if (ulLocalCurrentOffset + ulTableSize > *pulIndexSubTableSize) + { + Mem_Free(ausGlyphCodeArray); /* in case it got allocated */ + return ERR_INVALID_EBLC; + } + memcpy(*ppuchIndexSubTable + ulLocalCurrentOffset, &IndexSubTable5, SIZEOF_INDEXSUBTABLE5); + memcpy(*ppuchIndexSubTable + ulLocalCurrentOffset + SIZEOF_INDEXSUBTABLE5, ausGlyphCodeArray, sizeof(*ausGlyphCodeArray) * IndexSubTable5.ulNumGlyphs); } - memcpy(*ppuchIndexSubTable + ulLocalCurrentOffset, &IndexSubTable5, SIZEOF_INDEXSUBTABLE5); - memcpy(*ppuchIndexSubTable + ulLocalCurrentOffset + SIZEOF_INDEXSUBTABLE5, ausGlyphCodeArray, sizeof(*ausGlyphCodeArray) * IndexSubTable5.ulNumGlyphs); Mem_Free(ausGlyphCodeArray); } else @@ -396,16 +579,47 @@ PRIVATE int16 FixSbitSubTables(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInfo, / ulTableSize +=sizeof(usNewGlyphOffset); /* update the size of the table */ if (usGlyphIndex < usGlyphListCount && puchKeepGlyphList[usGlyphIndex]) /* if this glyph is supposed to be kept, copy glyph. */ { - assert(usNextGlyphOffset>=usOldGlyphOffset); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (usNextGlyphOffset < usOldGlyphOffset) + return ERR_INVALID_EBLC; + } + else + { + assert(usNextGlyphOffset>=usOldGlyphOffset); + } usGlyphLength = usNextGlyphOffset-usOldGlyphOffset; if (DoCopy) { - if ((errCode = ReadBytes( (TTFACC_FILEBUFFERINFO *) pInputBufferInfo, puchEBDTDestPtr + IndexSubTable3.header.ulImageDataOffset + usNewGlyphOffset, - ulEBDTSrcOffset + ulOldImageDataOffset + usOldGlyphOffset, - usGlyphLength)) != NO_ERROR) - return errCode; + if (TTF_SAFE_CHECKS_ENABLED()) + { + errCode = CopyEBDTBytesChecked( + (TTFACC_FILEBUFFERINFO *)pInputBufferInfo, + puchEBDTDestPtr, + ulEBDTSrcOffset, + ulEBDTAllocatedSize, + IndexSubTable3.header.ulImageDataOffset, + (uint32)usNewGlyphOffset, + ulOldImageDataOffset, + (uint32)usOldGlyphOffset, + (uint32)usGlyphLength); + if (errCode != NO_ERROR) + return errCode; + } + else + { + if ((errCode = ReadBytes((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, puchEBDTDestPtr + IndexSubTable3.header.ulImageDataOffset + usNewGlyphOffset, + ulEBDTSrcOffset + ulOldImageDataOffset + usOldGlyphOffset, + usGlyphLength)) != NO_ERROR) + return errCode; + } } usNewGlyphOffset = (uint16)(usNewGlyphOffset + usGlyphLength); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (usNewGlyphOffset < usGlyphLength) /* uint16 wrap check */ + return ERR_INVALID_EBLC; + } } } usOldGlyphOffset = usNextGlyphOffset; @@ -457,16 +671,44 @@ PRIVATE int16 FixSbitSubTables(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInfo, / ulTableSize +=sizeof(usGlyphIndex); memcpy(*ppuchIndexSubTable + ulLocalCurrentOffset+ulTableSize, &usNewGlyphOffset, sizeof(usNewGlyphOffset)); ulTableSize +=sizeof(usNewGlyphOffset); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (usNextGlyphOffset < usOldGlyphOffset) + return ERR_INVALID_EBLC; + } usGlyphLength = usNextGlyphOffset-usOldGlyphOffset; if (DoCopy) { - if ((errCode = ReadBytes( (TTFACC_FILEBUFFERINFO *) pInputBufferInfo, puchEBDTDestPtr + IndexSubTable4.header.ulImageDataOffset + usNewGlyphOffset, - ulEBDTSrcOffset + ulOldImageDataOffset + usOldGlyphOffset, - usGlyphLength)) != NO_ERROR) - return errCode; + if (TTF_SAFE_CHECKS_ENABLED()) + { + errCode = CopyEBDTBytesChecked( + (TTFACC_FILEBUFFERINFO *)pInputBufferInfo, + puchEBDTDestPtr, + ulEBDTSrcOffset, + ulEBDTAllocatedSize, + IndexSubTable4.header.ulImageDataOffset, + (uint32)usNewGlyphOffset, + ulOldImageDataOffset, + (uint32)usOldGlyphOffset, + (uint32)usGlyphLength); + if (errCode != NO_ERROR) + return errCode; + } + else + { + if ((errCode = ReadBytes((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, puchEBDTDestPtr + IndexSubTable4.header.ulImageDataOffset + usNewGlyphOffset, + ulEBDTSrcOffset + ulOldImageDataOffset + usOldGlyphOffset, + usGlyphLength)) != NO_ERROR) + return errCode; + } } usNewGlyphOffset = (uint16)(usNewGlyphOffset + usGlyphLength); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (usNewGlyphOffset < usGlyphLength) /* uint16 wrap check */ + return ERR_INVALID_EBLC; + } ++ulNumGlyphs; *pusNewLastGlyphIndex = usGlyphIndex; } @@ -521,10 +763,28 @@ PRIVATE int16 FixSbitSubTables(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInfo, / ulTableSize += sizeof(usGlyphIndex); if (DoCopy) { - if ((errCode = ReadBytes( (TTFACC_FILEBUFFERINFO *) pInputBufferInfo, puchEBDTDestPtr + IndexSubTable5.header.ulImageDataOffset + ulNewGlyphOffset, - ulEBDTSrcOffset + ulOldImageDataOffset + ulOldGlyphOffset, - ulGlyphLength)) != NO_ERROR) - return errCode; + if (TTF_SAFE_CHECKS_ENABLED()) + { + errCode = CopyEBDTBytesChecked( + (TTFACC_FILEBUFFERINFO *)pInputBufferInfo, + puchEBDTDestPtr, + ulEBDTSrcOffset, + ulEBDTAllocatedSize, + IndexSubTable5.header.ulImageDataOffset, + ulNewGlyphOffset, + ulOldImageDataOffset, + ulOldGlyphOffset, + ulGlyphLength); + if (errCode != NO_ERROR) + return errCode; + } + else + { + if ((errCode = ReadBytes((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, puchEBDTDestPtr + IndexSubTable5.header.ulImageDataOffset + ulNewGlyphOffset, + ulEBDTSrcOffset + ulOldImageDataOffset + ulOldGlyphOffset, + ulGlyphLength)) != NO_ERROR) + return errCode; + } } ++ulNumGlyphs; ulNewGlyphOffset += ulGlyphLength; @@ -580,6 +840,7 @@ typedef struct { PRIVATE uint32 FixSbitSubTableFormat1(uint16 usFirstIndex, /* index of first Glyph in table */ uint16 * pusLastIndex, /* pointer to index of last glyph in table - will set if not all table will fit */ uint8 * puchIndexSubTable, /* buffer into which to stuff the Format 3 table(s) - does not include IndexSubTableArray */ + uint32 ulIndexSubTableBufferSize, /* size of puchIndexSubTable buffer for bounds checking */ uint16 usImageFormat, /* in order to set the Format 3 header */ uint32 ulCurrAdditionalOffset, /* offset from indexSubTableArray of the IndexSubTable */ uint32 ulInitialOffset, /* relative offset from IndexSubTableArray of first IndexSubTable - same as CurrAdditionalOffset for first SubTable */ @@ -593,21 +854,42 @@ uint32 ulLocalCurrentOffset; uint32 ulTableSize; uint32 ulAdjustGlyphOffset; /* amount to subtract to get the relative offset */ uint16 usIndex; +uint32 ulWriteEnd; ulLocalCurrentOffset = ulCurrAdditionalOffset - ulInitialOffset; /* offset within memory buffer */ + /* Bounds check: ensure header write fits in buffer */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ulLocalCurrentOffset + SIZEOF_INDEXSUBTABLE3 > ulIndexSubTableBufferSize) + return 0; + } + IndexSubTable3.header.usImageFormat = usImageFormat; IndexSubTable3.header.usIndexFormat = 3; IndexSubTable3.header.ulImageDataOffset = *pulNewImageDataOffset; /* set to the new one */ memcpy(puchIndexSubTable + ulLocalCurrentOffset, &IndexSubTable3, SIZEOF_INDEXSUBTABLE3); ulTableSize = SIZEOF_INDEXSUBTABLE3; + /* Bounds check: ensure source reads are within buffer */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (*pulSourceOffset + sizeof(uint32) > ulIndexSubTableBufferSize) + return 0; + } ulAdjustGlyphOffset = * ((uint32 *) (puchIndexSubTable + *pulSourceOffset)); /* first offset is what we adjust from */ ulNewGlyphOffset = (* ((uint32 *) (puchIndexSubTable + *pulSourceOffset))) - ulAdjustGlyphOffset; /* first one of array */ *pulSourceOffset += sizeof(uint32); for( usIndex = usFirstIndex; usIndex <= (* pusLastIndex); ++usIndex ) { + /* Bounds check: ensure source read fits in buffer */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (*pulSourceOffset + sizeof(uint32) > ulIndexSubTableBufferSize) + return 0; + } + usNewGlyphOffset = (uint16) ulNewGlyphOffset; /* short version of the new glyph offset */ /* now grab the next one */ ulNewGlyphOffset = (* ((uint32 *) (puchIndexSubTable + *pulSourceOffset))) - ulAdjustGlyphOffset; @@ -617,6 +899,14 @@ uint16 usIndex; *pulSourceOffset += sizeof(uint32); /* copied this one */ + /* Bounds check: ensure write fits in buffer */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + ulWriteEnd = ulLocalCurrentOffset + ulTableSize + sizeof(usNewGlyphOffset); + if (ulWriteEnd > ulIndexSubTableBufferSize) + return 0; + } + memcpy(puchIndexSubTable + ulLocalCurrentOffset+ulTableSize, &usNewGlyphOffset, sizeof(usNewGlyphOffset)); /* copy over the table entry */ ulTableSize +=sizeof(usNewGlyphOffset); /* update the size of the table */ @@ -630,6 +920,13 @@ uint16 usIndex; if (usIndex > (* pusLastIndex)) /* we need to copy one more */ /* Do the last table entry, which is just for Glyph size calculation purposes */ { + /* Bounds check for final entry */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + ulWriteEnd = ulLocalCurrentOffset + ulTableSize + sizeof(usNewGlyphOffset); + if (ulWriteEnd > ulIndexSubTableBufferSize) + return 0; + } memcpy(puchIndexSubTable + ulLocalCurrentOffset+ulTableSize, &usNewGlyphOffset,sizeof(usNewGlyphOffset)); ulTableSize +=sizeof(usNewGlyphOffset); } @@ -652,6 +949,7 @@ PRIVATE int16 FixSbitSubTableArray(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInf uint32 *pulNewImageDataOffset, uint8 *puchEBDTDestPtr, uint32 ulEBDTSrcOffset, + uint32 ulEBDTAllocatedSize, PGLYPHOFFSETRECORDKEEPER pKeeper, uint32 ulEBLCEndOffset) { @@ -724,6 +1022,7 @@ PRIVATE int16 FixSbitSubTableArray(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInf &ulEBDTBytesWritten, puchEBDTDestPtr, ulEBDTSrcOffset, + ulEBDTAllocatedSize, pKeeper); if (errCode != NO_ERROR) return errCode; @@ -753,6 +1052,7 @@ PRIVATE int16 FixSbitSubTableArray(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInf usFirstIndex, &usLastIndex, pSubTablePointers->puchIndexSubTables, + pSubTablePointers->nIndexSubTablesLen, ((INDEXSUBHEADER *) (pSubTablePointers->puchIndexSubTables + ulCurrAdditionalOffset-ulInitialOffset))->usImageFormat, ulCurrAdditionalOffset, ulInitialOffset, @@ -777,9 +1077,19 @@ PRIVATE int16 FixSbitSubTableArray(CONST_TTFACC_FILEBUFFERINFO * pInputBufferInf { return ERR_MEM; } - pSubTablePointers->pIndexSubTableArray = (INDEXSUBTABLEARRAY *)Mem_ReAlloc(pSubTablePointers->pIndexSubTableArray, ulSubTableArrayLength); - if (pSubTablePointers->pIndexSubTableArray == NULL) - return ERR_MEM; /* ("EBLC: Unable to allocate memory for IndexSubTableArray."); */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + INDEXSUBTABLEARRAY *pNewArray = (INDEXSUBTABLEARRAY *)Mem_ReAlloc(pSubTablePointers->pIndexSubTableArray, ulSubTableArrayLength); + if (pNewArray == NULL) + return ERR_MEM; /* ("EBLC: Unable to allocate memory for IndexSubTableArray."); */ + pSubTablePointers->pIndexSubTableArray = pNewArray; + } + else + { + pSubTablePointers->pIndexSubTableArray = (INDEXSUBTABLEARRAY *)Mem_ReAlloc(pSubTablePointers->pIndexSubTableArray, ulSubTableArrayLength); + if (pSubTablePointers->pIndexSubTableArray == NULL) + return ERR_MEM; + } usFirstIndex = usLastIndex; usLastIndex = usSaveLastIndex; } @@ -887,7 +1197,7 @@ uint32 ulStartOffset; /* ------------------------------------------------------------------- */ void Cleanup_SubTablePointers(SubTablePointers *pSubTablePointers,uint32 ulNumSizes) { -uint16 ulSizeIndex; +uint32 ulSizeIndex; if (pSubTablePointers == NULL) return; @@ -954,6 +1264,7 @@ uint32 ulIndexSubTableArrayLength; uint32 ulIndexSubTablesDataSize; uint32 ulSubtablePointersArraySize; uint16 i; +uint32 ulEBDTAllocatedSize = 0; /* Track allocated EBDT buffer size for bounds checking */ GLYPHOFFSETRECORDKEEPER keeper; char *EBDTTag; char *EBLCTag; @@ -1031,8 +1342,24 @@ uint32 ulEBLCEndOffset; keeper.ulOffsetArrayLen = 0; keeper.ulNextArrayIndex = 0; - /* create a buffer for the EBDT table */ - puchEBDTDestPtr = (uint8 *) Mem_Alloc(TTTableLength((TTFACC_FILEBUFFERINFO *) pInputBufferInfo, EBDTTag)); /* we'll be copying the EBDT (raw bytes) table here temporarily */ + /* Validate EBDT table size before allocation */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + /* Validate EBDT table size before allocation */ + ulEBDTAllocatedSize = TTTableLength((TTFACC_FILEBUFFERINFO*)pInputBufferInfo, EBDTTag); + if (ulEBDTAllocatedSize < SIZEOF_EBDTHEADER) + { + errCode = ERR_INVALID_EBLC; + break; + } + /* create a buffer for the EBDT table */ + puchEBDTDestPtr = (uint8*)Mem_Alloc(ulEBDTAllocatedSize); /* we'll be copying the EBDT (raw bytes) table here temporarily */ + } + else + { + /* create a buffer for the EBDT table */ + puchEBDTDestPtr = (uint8 *) Mem_Alloc(TTTableLength((TTFACC_FILEBUFFERINFO *) pInputBufferInfo, EBDTTag)); /* we'll be copying the EBDT (raw bytes) table here temporarily */ + } if (!puchEBDTDestPtr) { errCode = ERR_MEM; @@ -1163,6 +1490,7 @@ uint32 ulEBLCEndOffset; &ulNewImageDataOffset, puchEBDTDestPtr, ulEBDTSrcOffset, + ulEBDTAllocatedSize, &keeper, ulEBLCEndOffset) == NO_ERROR) /* valid table */ && diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modtable.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modtable.cpp index f61c38cd718..0eda170eb15 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modtable.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/modtable.cpp @@ -37,6 +37,8 @@ #include "ttmem.h" #include "ttfdelta.h" /* for format */ #include "ttferror.h" /* for error codes */ +#include "intsafe_private_copy.h" +#include "ttf_safe_checks.h" /* here's the deal: This function may do one of many things. @@ -170,7 +172,17 @@ const char * xhea_tag; /* now collapse the table if we are in Compact form for Subsetting and Delta fonts */ /* we will use an interrum table for simplification */ ulCrntOffset = ulXmtxOffset; - LongMetricsArray = (LONGXMETRIC *)Mem_Alloc(sizeof(LONGXMETRIC) * usDttfGlyphIndexCount); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)sizeof(LONGXMETRIC), (uint32)usDttfGlyphIndexCount, &ulAllocSize) != S_OK) + return ERR_MEM; + LongMetricsArray = (LONGXMETRIC *)Mem_Alloc(ulAllocSize); + } + else + { + LongMetricsArray = (LONGXMETRIC *)Mem_Alloc(sizeof(LONGXMETRIC) * usDttfGlyphIndexCount); + } if (LongMetricsArray == NULL) return ERR_MEM; nNewLongMetrics = 0; @@ -278,8 +290,22 @@ uint16 usBytesWritten; /* recompute maxp info */ /* figure a conservative maximum total possible. 3x3 at minimum */ - usnMaxComponents = max(3,MaxP.maxComponentElements) * max(3,MaxP.maxComponentDepth); - pausComponents = (uint16 *) Mem_Alloc(usnMaxComponents * sizeof(uint16)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulMaxComp, ulAllocSize; + if (ULongMult32((uint32)max(3,MaxP.maxComponentElements), (uint32)max(3,MaxP.maxComponentDepth), &ulMaxComp) != S_OK || + ULongMult32(ulMaxComp, (uint32)sizeof(uint16), &ulAllocSize) != S_OK) + return ERR_MEM; + if (ulMaxComp > (uint32)USHRT_MAX) + return ERR_INVALID_MAXP; + usnMaxComponents = (uint16)ulMaxComp; + pausComponents = (uint16 *) Mem_Alloc(ulAllocSize); + } + else + { + usnMaxComponents = max(3,MaxP.maxComponentElements) * max(3,MaxP.maxComponentDepth); + pausComponents = (uint16 *) Mem_Alloc(usnMaxComponents * sizeof(uint16)); + } if (pausComponents == NULL) return ERR_MEM; @@ -716,7 +742,16 @@ uint32 ulOutSizeDeviceRecord; DevRecord.maxWidth = maxWidth; if ((errCode = WriteGeneric( pOutputBufferInfo, (uint8 *)&DevRecord, SIZEOF_HDMX_DEVICE_REC, HDMX_DEVICE_REC_CONTROL, ulOutDevOffset, &usBytesWritten )) != NO_ERROR) return errCode; - ulInOffset = ulInDevOffset + Hdmx.sizeDeviceRecord; + if (TTF_SAFE_CHECKS_ENABLED()) + { + ulInOffset = ulInDevOffset + (uint32)Hdmx.sizeDeviceRecord; + if (ulInOffset < ulInDevOffset) /* overflow check */ + return ERR_GENERIC; + } + else + { + ulInOffset = ulInDevOffset + Hdmx.sizeDeviceRecord; + } ulOutOffset = ulOutDevOffset + ulOutSizeDeviceRecord; } /* now need to update hdmx record */ @@ -762,7 +797,16 @@ uint32 ulOutSizeDeviceRecord; if ((errCode = WriteGeneric( pOutputBufferInfo, (uint8 *)&DevRecord, SIZEOF_HDMX_DEVICE_REC, HDMX_DEVICE_REC_CONTROL, ulDevOffset, &usBytesWritten )) != NO_ERROR) return errCode; } - ulOffset = ulDevOffset + Hdmx.sizeDeviceRecord; + if (TTF_SAFE_CHECKS_ENABLED()) + { + ulOffset = ulDevOffset + (uint32)Hdmx.sizeDeviceRecord; + if (ulOffset < ulDevOffset) /* overflow check */ + return ERR_GENERIC; + } + else + { + ulOffset = ulDevOffset + Hdmx.sizeDeviceRecord; + } } *pulNewOutOffset = ulOffset; } @@ -893,7 +937,17 @@ struct groupoffsetrecordkeeper /* housekeeping structure */ PRIVATE int16 InitGroupOffsetArray(PGROUPOFFSETRECORDKEEPER pKeeper, uint16 usRecordCount) { - pKeeper->pGroupOffsetArray = (GroupOffsetRecord *) Mem_Alloc(usRecordCount * sizeof(*(pKeeper->pGroupOffsetArray))); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)usRecordCount, (uint32)sizeof(*(pKeeper->pGroupOffsetArray)), &ulAllocSize) != S_OK) + return ERR_MEM; + pKeeper->pGroupOffsetArray = (GroupOffsetRecord *) Mem_Alloc(ulAllocSize); + } + else + { + pKeeper->pGroupOffsetArray = (GroupOffsetRecord *) Mem_Alloc(usRecordCount * sizeof(*(pKeeper->pGroupOffsetArray))); + } if (pKeeper->pGroupOffsetArray == NULL) return ERR_MEM; pKeeper->usGroupOffsetArrayLen = usRecordCount; @@ -1018,7 +1072,19 @@ TTFACC_FILEBUFFERINFO * pUnCONSTInputBufferInfo; ulSrcOffsetGroups = ulSrcOffsetOffsets + sizeof(uint16) * Vdmx.numRatios; memset(&keeper, 0, sizeof(keeper)); - SrcRatioArray = (VDMXRatio *)Mem_Alloc(Vdmx.numRatios * sizeof(VDMXRatio)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)Vdmx.numRatios, (uint32)sizeof(VDMXRatio), &ulAllocSize) != S_OK) + { + return ERR_MEM; + } + SrcRatioArray = (VDMXRatio *)Mem_Alloc(ulAllocSize); + } + else + { + SrcRatioArray = (VDMXRatio *)Mem_Alloc(Vdmx.numRatios * sizeof(VDMXRatio)); + } if (SrcRatioArray == NULL) errCode = ERR_MEM; else @@ -1111,7 +1177,19 @@ TTFACC_FILEBUFFERINFO * pUnCONSTInputBufferInfo; if ((errCode = ReadGeneric(pUnCONSTInputBufferInfo, (uint8 *) &GroupHeader, SIZEOF_VDMXGROUP, VDMXGROUP_CONTROL, ulSrcOffset + usCurrGroupSrcOffset, &usBytesRead)) != NO_ERROR) break; - ulGroupLength = usBytesRead + (GroupHeader.recs * GetGenericSize(VDMXVTABLE_CONTROL)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + ulGroupLength = usBytesRead + ((uint32)GroupHeader.recs * GetGenericSize(VDMXVTABLE_CONTROL)); + if (ulGroupLength < usBytesRead) /* overflow check */ + { + errCode = ERR_INVALID_VDMX; + break; + } + } + else + { + ulGroupLength = usBytesRead + (GroupHeader.recs * GetGenericSize(VDMXVTABLE_CONTROL)); + } /* read the group data into a buffer */ if (ulGroupLength > ulGroupBufferLength) { diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttf_safe_checks.h b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttf_safe_checks.h new file mode 100644 index 00000000000..947624e5fde --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttf_safe_checks.h @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef TTF_SAFE_CHECKS_H +#define TTF_SAFE_CHECKS_H + +/* + * TtfDelta safety switch — read once from managed code (AppContext), + * then checked from native C code via the global. + * + * AppContext switch name: + * Switch.MS.Internal.TtfDelta.DisableDirectWriteForwarderBoundsCheckProtection + * + * Default (false / absent): Safe checks ON (new behavior). + * Set to true: Safe checks OFF (old behavior / opt-out). + */ + +/* -1 = uninitialized, 0 = safe checks disabled (old), 1 = safe checks enabled (new) */ +extern int g_fDWFBoundsCheckEnabled; + +#define TTF_SAFE_CHECKS_ENABLED() (g_fDWFBoundsCheckEnabled != 0) + +#endif /* TTF_SAFE_CHECKS_H */ diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttfacc.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttfacc.cpp index 8a2bce89d10..23562d736b0 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttfacc.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttfacc.cpp @@ -23,6 +23,8 @@ #ifdef _DEBUG #include #endif +#include "intsafe_private_copy.h" +#include "ttf_safe_checks.h" #if 0 /* turn back into a macro, because it gets called so much */ @@ -73,7 +75,7 @@ int16 CheckOutOffset(TTFACC_FILEBUFFERINFO *a, register uint32 b, register uint3 { #ifdef _DEBUG #if !defined(ARGITERATOR_SUPPORTED) || (defined(ARGITERATOR_SUPPORTED) && ARGITERATOR_SUPPORTED) - printf("we're reallocating 10 percent (%lu) more bytes\n", (uint32)(a->ulBufferSize * .1)); + printf("we're reallocating 10 percent (%lu) more bytes\n", (uint32)(a->ulBufferSize * .1)); #endif #endif a->ulBufferSize = (uint32) (a->ulBufferSize * 11/10); @@ -86,14 +88,14 @@ int16 CheckOutOffset(TTFACC_FILEBUFFERINFO *a, register uint32 b, register uint3 #endif #endif a->ulBufferSize = b + c; - } - + } + if ((a->puchBuffer = (uint8 *)a->lpfnReAllocate(a->puchBuffer, a->ulBufferSize)) == NULL) { a->ulBufferSize = 0L; return ERR_MEM; } - } + } return NO_ERROR; } @@ -242,14 +244,30 @@ UNALIGNED uint32 *pulBuffer; uint16 i; int16 errCode; - usControlCount = puchControl[0]; +if (TTF_SAFE_CHECKS_ENABLED()) +{ + if (pInputBufferInfo == NULL || puchControl == NULL || pusBytesRead == NULL) + return ERR_READCONTROL; + if (usBufferSize != 0 && puchBuffer == NULL) + return ERR_READCONTROL; +} + + usControlCount = puchControl[0]; for (i = 1; i <= usControlCount; ++i) { switch (puchControl[i] & TTFACC_DATA) { case TTFACC_BYTE: - if (usBufferOffset + sizeof(uint8) > usBufferSize) - return ERR_READCONTROL; /* trying to stuff too many bytes into target buffer */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (static_cast(usBufferOffset) + static_cast(sizeof(uint8)) > static_cast(usBufferSize)) + return ERR_READCONTROL; /* trying to stuff too many bytes into target buffer */ + } + else + { + if (usBufferOffset + sizeof(uint8) > usBufferSize) + return ERR_READCONTROL; /* trying to stuff too many bytes into target buffer */ + } if (puchControl[i] & TTFACC_PAD) /* don't read, just pad */ *(puchBuffer + usBufferOffset) = 0; else @@ -257,13 +275,30 @@ int16 errCode; if ((errCode = ReadByte(pInputBufferInfo, puchBuffer + usBufferOffset, ulCurrOffset))!=NO_ERROR) return errCode; - ulCurrOffset += sizeof(uint8); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ulCurrOffset + static_cast(sizeof(uint8)) < ulCurrOffset) + return ERR_READOUTOFBOUNDS; + ulCurrOffset += static_cast(sizeof(uint8)); + } + else + { + ulCurrOffset += sizeof(uint8); + } } usBufferOffset += sizeof(uint8); break; case TTFACC_WORD: - if (usBufferOffset + sizeof(uint16) > usBufferSize) - return ERR_READCONTROL; /* trying to stuff too many bytes into target buffer */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (static_cast(usBufferOffset) + static_cast(sizeof(uint16)) > static_cast(usBufferSize)) + return ERR_READCONTROL; /* trying to stuff too many bytes into target buffer */ + } + else + { + if (usBufferOffset + sizeof(uint16) > usBufferSize) + return ERR_READCONTROL; /* trying to stuff too many bytes into target buffer */ + } pusBuffer = (uint16 *) (puchBuffer + usBufferOffset); if (puchControl[i] & TTFACC_PAD) /* don't read, just pad */ *pusBuffer = 0; @@ -279,13 +314,30 @@ int16 errCode; if ((errCode = ReadWord(pInputBufferInfo, pusBuffer, ulCurrOffset))!=NO_ERROR) return errCode; } - ulCurrOffset += sizeof(uint16); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ulCurrOffset + static_cast(sizeof(uint16)) < ulCurrOffset) + return ERR_READOUTOFBOUNDS; + ulCurrOffset += static_cast(sizeof(uint16)); + } + else + { + ulCurrOffset += sizeof(uint16); + } } usBufferOffset += sizeof(uint16); break; case TTFACC_LONG: - if (usBufferOffset + sizeof(uint32) > usBufferSize) - return ERR_READCONTROL; /* trying to stuff too many bytes into target buffer */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (static_cast(usBufferOffset) + static_cast(sizeof(uint32)) > static_cast(usBufferSize)) + return ERR_READCONTROL; /* trying to stuff too many bytes into target buffer */ + } + else + { + if (usBufferOffset + sizeof(uint32) > usBufferSize) + return ERR_READCONTROL; /* trying to stuff too many bytes into target buffer */ + } pulBuffer = (uint32 *) (puchBuffer + usBufferOffset); if (puchControl[i] & TTFACC_PAD) /* don't read, just pad */ *pulBuffer = 0; @@ -302,7 +354,16 @@ int16 errCode; if ((errCode = ReadLong(pInputBufferInfo, pulBuffer, ulCurrOffset))!=NO_ERROR) return errCode; } - ulCurrOffset += sizeof(uint32); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ulCurrOffset + static_cast(sizeof(uint32)) < ulCurrOffset) + return ERR_READOUTOFBOUNDS; + ulCurrOffset += static_cast(sizeof(uint32)); + } + else + { + ulCurrOffset += sizeof(uint32); + } } usBufferOffset += sizeof(uint32); break; @@ -312,7 +373,17 @@ int16 errCode; } /* end for i */ if (usBufferOffset < usBufferSize) /* didn't fill up the buffer */ return ERR_READCONTROL; /* control thing doesn't fit the buffer */ - * pusBytesRead = (uint16) (ulCurrOffset - ulOffset); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulBytesRead = ulCurrOffset - ulOffset; + *pusBytesRead = static_cast(ulBytesRead); + if (static_cast(*pusBytesRead) != ulBytesRead) + return ERR_READCONTROL; + } + else + { + * pusBytesRead = (uint16) (ulCurrOffset - ulOffset); + } return NO_ERROR; } /* ---------------------------------------------------------------------- */ @@ -348,7 +419,17 @@ uint16 usBytesRead; puchBuffer += usItemSize; } - *pulBytesRead = usItemSize * usItemCount; + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulTotalBytesRead; + if (ULongMult32((uint32)usItemSize, (uint32)usItemCount, &ulTotalBytesRead) != S_OK) + return ERR_READOUTOFBOUNDS; + *pulBytesRead = ulTotalBytesRead; + } + else + { + *pulBytesRead = usItemSize * usItemCount; + } return NO_ERROR; } /* ---------------------------------------------------------------------- */ @@ -378,7 +459,15 @@ uint16 i; uint32 ulBytesWritten; int16 errCode; - usControlCount = puchControl[0]; +if (TTF_SAFE_CHECKS_ENABLED()) +{ + if (pOutputBufferInfo == NULL || puchControl == NULL || pusBytesWritten == NULL) + return ERR_WRITECONTROL; + if (usBufferSize != 0 && puchBuffer == NULL) + return ERR_WRITECONTROL; +} + + usControlCount = puchControl[0]; for (i = 1; i <= usControlCount; ++i) { switch (puchControl[i] & TTFACC_DATA) @@ -386,20 +475,45 @@ int16 errCode; case TTFACC_BYTE: if (!(puchControl[i] & TTFACC_PAD)) { - if (usBufferOffset + sizeof(uint8) > usBufferSize) - return ERR_WRITECONTROL; /* trying to read too many bytes from source buffer */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (static_cast(usBufferOffset) + static_cast(sizeof(uint8)) > static_cast(usBufferSize)) + return ERR_WRITECONTROL; /* trying to read too many bytes from source buffer */ + } + else + { + if (usBufferOffset + sizeof(uint8) > usBufferSize) + return ERR_WRITECONTROL; /* trying to read too many bytes from source buffer */ + } if ((errCode = WriteByte(pOutputBufferInfo, *(puchBuffer + usBufferOffset), ulCurrOffset))!=NO_ERROR) return errCode; - ulCurrOffset += sizeof(uint8); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ulCurrOffset + static_cast(sizeof(uint8)) < ulCurrOffset) + return ERR_WRITEOUTOFBOUNDS; + ulCurrOffset += static_cast(sizeof(uint8)); + } + else + { + ulCurrOffset += sizeof(uint8); + } } usBufferOffset += sizeof(uint8); break; case TTFACC_WORD: if (!(puchControl[i] & TTFACC_PAD)) { - if (usBufferOffset + sizeof(uint16) > usBufferSize) - return ERR_WRITECONTROL; /* trying to read too many bytes from source buffer */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (static_cast(usBufferOffset) + static_cast(sizeof(uint16)) > static_cast(usBufferSize)) + return ERR_WRITECONTROL; /* trying to read too many bytes from source buffer */ + } + else + { + if (usBufferOffset + sizeof(uint16) > usBufferSize) + return ERR_WRITECONTROL; /* trying to read too many bytes from source buffer */ + } pusBuffer = (uint16 *) (puchBuffer + usBufferOffset); if (puchControl[i] & TTFACC_NO_XLATE) @@ -412,15 +526,32 @@ int16 errCode; if ((errCode = WriteWord(pOutputBufferInfo, *pusBuffer, ulCurrOffset))!=NO_ERROR) return errCode; } - ulCurrOffset += sizeof(uint16); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ulCurrOffset + static_cast(sizeof(uint16)) < ulCurrOffset) + return ERR_WRITEOUTOFBOUNDS; + ulCurrOffset += static_cast(sizeof(uint16)); + } + else + { + ulCurrOffset += sizeof(uint16); + } } usBufferOffset += sizeof(uint16); break; case TTFACC_LONG: if (!(puchControl[i] & TTFACC_PAD)) { - if (usBufferOffset + sizeof(uint32) > usBufferSize) - return ERR_WRITECONTROL; /* trying to read too many bytes from source buffer */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (static_cast(usBufferOffset) + static_cast(sizeof(uint32)) > static_cast(usBufferSize)) + return ERR_WRITECONTROL; /* trying to read too many bytes from source buffer */ + } + else + { + if (usBufferOffset + sizeof(uint32) > usBufferSize) + return ERR_WRITECONTROL; /* trying to read too many bytes from source buffer */ + } pulBuffer = (uint32 *) (puchBuffer + usBufferOffset); if (puchControl[i] & TTFACC_NO_XLATE) @@ -433,7 +564,16 @@ int16 errCode; if ((errCode = WriteLong(pOutputBufferInfo, *pulBuffer, ulCurrOffset))!=NO_ERROR) return errCode; } - ulCurrOffset += sizeof(uint32); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ulCurrOffset + static_cast(sizeof(uint32)) < ulCurrOffset) + return ERR_WRITEOUTOFBOUNDS; + ulCurrOffset += static_cast(sizeof(uint32)); + } + else + { + ulCurrOffset += sizeof(uint32); + } } usBufferOffset += sizeof(uint32); break; @@ -482,7 +622,17 @@ uint16 usBytesWritten; puchBuffer += usItemSize; } - *pulBytesWritten = usItemSize * usItemCount; + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulTotalBytesWritten; + if (ULongMult32((uint32)usItemSize, (uint32)usItemCount, &ulTotalBytesWritten) != S_OK) + return ERR_WRITEOUTOFBOUNDS; + *pulBytesWritten = ulTotalBytesWritten; + } + else + { + *pulBytesWritten = usItemSize * usItemCount; + } return NO_ERROR; } diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttfdelta.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttfdelta.cpp index 3e3530dbec6..a841c171915 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttfdelta.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttfdelta.cpp @@ -19,6 +19,9 @@ #include /* for memcpy */ #include "typedefs.h" +#include "intsafe_private_copy.h" +#include "ttf_safe_checks.h" + #include "ttff.h" #include "ttfacc.h" #include "ttfcntrl.h" @@ -111,7 +114,14 @@ int16 errCode; ulOffset += usBytesRead; /* Create a list of valid tables */ - aDirectory = (DIRECTORY *) Mem_Alloc((usnTables + (ulDttfOffset == 0)) * sizeof(DIRECTORY)); /* one extra for possible private table */ + if (TTF_SAFE_CHECKS_ENABLED()) { + uint32 ulAllocSize; + if (ULongMult32((uint32)(usnTables + (ulDttfOffset == 0)), (uint32)sizeof(DIRECTORY), &ulAllocSize) != S_OK) + return(ERR_MEM); + aDirectory = (DIRECTORY *) Mem_Alloc(ulAllocSize); /* one extra for possible private table */ + } else { + aDirectory = (DIRECTORY *) Mem_Alloc((usnTables + (ulDttfOffset == 0)) * sizeof(DIRECTORY)); + } if (aDirectory == NULL) return(ERR_MEM); @@ -213,7 +223,14 @@ char szTag[5]; usnTables = OffsetTable.numTables; /* Create a list of valid tables */ - aDirectory = (DIRECTORY *) Mem_Alloc((usnTables) * sizeof(DIRECTORY)); + if (TTF_SAFE_CHECKS_ENABLED()) { + uint32 ulAllocSize; + if (ULongMult32((uint32)usnTables, (uint32)sizeof(DIRECTORY), &ulAllocSize) != S_OK) + return(ERR_MEM); + aDirectory = (DIRECTORY *) Mem_Alloc(ulAllocSize); + } else { + aDirectory = (DIRECTORY *) Mem_Alloc((usnTables) * sizeof(DIRECTORY)); + } if (aDirectory == NULL) return(ERR_MEM); @@ -297,7 +314,15 @@ HEAD Head; if ((ulHeadOffset = GetHead(pOutputBufferInfo, &Head)) == 0L) return ERR_MISSING_HEAD; - aulLoca = (uint32 *)Mem_Alloc( (usGlyphListCount + 1) * sizeof( uint32 )); + if (TTF_SAFE_CHECKS_ENABLED()) { + uint32 ulLocaCount = (uint32)usGlyphListCount + 1; + uint32 ulAllocSize; + if (ULongMult32(ulLocaCount, (uint32)sizeof( uint32 ), &ulAllocSize) != S_OK) + return ERR_MEM; + aulLoca = (uint32 *)Mem_Alloc( ulAllocSize ); + } else { + aulLoca = (uint32 *)Mem_Alloc( (usGlyphListCount + 1) * sizeof( uint32 ) ); + } if ( aulLoca == NULL ) return ERR_MEM; @@ -316,8 +341,16 @@ HEAD Head; if ((j == usDttfGlyphIndexCount) || (i < usGlyphListCount && puchKeepGlyphList[i])) { usOffset = (uint16) (aulLoca[ i ] / 2L); - if ((errCode = WriteWord( pOutputBufferInfo, usOffset, ulLocaOffset + j*sizeof(uint16) )) != NO_ERROR) - break; + if (TTF_SAFE_CHECKS_ENABLED()) + { + if ((errCode = WriteWord( pOutputBufferInfo, usOffset, ulLocaOffset + (uint32)j*sizeof(uint16) )) != NO_ERROR) + break; + } + else + { + if ((errCode = WriteWord( pOutputBufferInfo, usOffset, ulLocaOffset + j*sizeof(uint16) )) != NO_ERROR) + break; + } ++j; } } @@ -329,8 +362,16 @@ HEAD Head; { if ((j == usDttfGlyphIndexCount) || (i < usGlyphListCount && puchKeepGlyphList[i])) { - if ((errCode = WriteLong( pOutputBufferInfo, aulLoca[ i ], ulLocaOffset + j*sizeof(uint32) )) != NO_ERROR) - break; + if (TTF_SAFE_CHECKS_ENABLED()) + { + if ((errCode = WriteLong( pOutputBufferInfo, aulLoca[ i ], ulLocaOffset + (uint32)j*sizeof(uint32) )) != NO_ERROR) + break; + } + else + { + if ((errCode = WriteLong( pOutputBufferInfo, aulLoca[ i ], ulLocaOffset + j*sizeof(uint32) )) != NO_ERROR) + break; + } ++j; } } @@ -385,11 +426,23 @@ uint16 usBytesWritten; if (usFormat != TTFDELTA_SUBSET1 && usFormat != TTFDELTA_DELTA) /* formats with dttf tables */ return NO_ERROR; + if (TTF_SAFE_CHECKS_ENABLED()) { + if (usDttfGlyphIndexCount == 0) + return ERR_GENERIC; + } + ulOffset = GetTTDirectory( pOutputBufferInfo, DTTF_TAG, &DttfDirectory); if ((errCode = ZeroLongWordAlign(pOutputBufferInfo, *pulNewOutOffset, &(DttfDirectory.offset))) != NO_ERROR) return errCode; - DttfDirectory.length = GetGenericSize(DTTF_HEADER_CONTROL) + usDttfGlyphIndexCount * sizeof(uint16); + if (TTF_SAFE_CHECKS_ENABLED()) + { + DttfDirectory.length = GetGenericSize(DTTF_HEADER_CONTROL) + (uint32)usDttfGlyphIndexCount * sizeof(uint16); + } + else + { + DttfDirectory.length = GetGenericSize(DTTF_HEADER_CONTROL) + usDttfGlyphIndexCount * sizeof(uint16); + } if (ulOffset == DIRECTORY_ERROR) /* there wasn't one there - don't really need this code - its obsolete */ return ERR_GENERIC; @@ -427,7 +480,7 @@ uint16 usBytesWritten; /* in addition any array tables (LTSH, loca, hmtx, hdmx, vmtx) will have a percentage discarded */ /* Format Delta will keep only a list of tables, and the Subset1 compacted and Glyf tables will keep only a portion */ /* ---------------------------------------------------------------------- */ -PRIVATE void CalcOutputBufferSize(CONST_TTFACC_FILEBUFFERINFO *pInputBufferInfo, +PRIVATE int16 CalcOutputBufferSize(CONST_TTFACC_FILEBUFFERINFO *pInputBufferInfo, uint16 usGlyphListCount, uint16 usGlyphKeepCount, uint16 usFormat, @@ -458,37 +511,93 @@ uint32 ulKeepTablesLength = 0; if (ulEBDTTableOffset != TTTableOffset((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, BDAT_TAG)) ulBdatTableLength = 0; } - ulAllGlyphsLength = ulEBDTTableLength + ulBdatTableLength; - ulAllGlyphsLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, GLYF_TAG); + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (UIntAdd32(ulEBDTTableLength, ulBdatTableLength, &ulAllGlyphsLength) != S_OK) + return ERR_GENERIC; + if (UIntAdd32(ulAllGlyphsLength, TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, GLYF_TAG), &ulAllGlyphsLength) != S_OK) + return ERR_GENERIC; + + if (usFormat == TTFDELTA_DELTA || usFormat == TTFDELTA_SUBSET1) + { /* these formats will compact some tables, discarding a percentage of these tables as well */ + /* tables compacted */ + ulGlyphDependentDataLength = TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, LTSH_TAG); + if (UIntAdd32(ulGlyphDependentDataLength, TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HMTX_TAG), &ulGlyphDependentDataLength) != S_OK) + return ERR_GENERIC; + if (UIntAdd32(ulGlyphDependentDataLength, TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, VMTX_TAG), &ulGlyphDependentDataLength) != S_OK) + return ERR_GENERIC; + if (UIntAdd32(ulGlyphDependentDataLength, TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HDMX_TAG), &ulGlyphDependentDataLength) != S_OK) + return ERR_GENERIC; + if (UIntAdd32(ulGlyphDependentDataLength, TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, LOCA_TAG), &ulGlyphDependentDataLength) != S_OK) + return ERR_GENERIC; + } + if (UIntAdd32(ulGlyphDependentDataLength, ulAllGlyphsLength, &ulGlyphDependentDataLength) != S_OK) /* all formats will discard a percentage of the glyph data */ + return ERR_GENERIC; - if (usFormat == TTFDELTA_DELTA || usFormat == TTFDELTA_SUBSET1) - { /* these formats will compact some tables, discarding a percentage of these tables as well */ - /* tables compacted */ - ulGlyphDependentDataLength = TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, LTSH_TAG); - ulGlyphDependentDataLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HMTX_TAG); - ulGlyphDependentDataLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, VMTX_TAG); - ulGlyphDependentDataLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HDMX_TAG); - ulGlyphDependentDataLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, LOCA_TAG); + if (usFormat == TTFDELTA_DELTA) /* we're going to keep just a handfull of tables tables */ + { + ulKeepTablesLength = TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HEAD_TAG); + if (UIntAdd32(ulKeepTablesLength, TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, MAXP_TAG), &ulKeepTablesLength) != S_OK) + return ERR_GENERIC; + if (UIntAdd32(ulKeepTablesLength, TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HHEA_TAG), &ulKeepTablesLength) != S_OK) + return ERR_GENERIC; + if (UIntAdd32(ulKeepTablesLength, TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, VHEA_TAG), &ulKeepTablesLength) != S_OK) + return ERR_GENERIC; + if (UIntAdd32(ulKeepTablesLength, TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, CMAP_TAG), &ulKeepTablesLength) != S_OK) + return ERR_GENERIC; + if (ulEBDTTableLength > 0) + { + if (UIntAdd32(ulKeepTablesLength, TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, EBLC_TAG), &ulKeepTablesLength) != S_OK) + return ERR_GENERIC; + } + if (ulBdatTableLength > 0) + { + if (UIntAdd32(ulKeepTablesLength, TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, BLOC_TAG), &ulKeepTablesLength) != S_OK) + return ERR_GENERIC; + } + + if (UIntAdd32(ulKeepTablesLength, (uint32)(flKeepPercent * ulGlyphDependentDataLength/100), pulOutputBufferLength) != S_OK) + return ERR_GENERIC; + } + else + /* for straight subset, this will be: ulSrcBufferSize - (discard % * (Glyf table size + EBDT table size + bdat table size)) */ + *pulOutputBufferLength = ulSrcBufferSize - (uint32)(flDiscardPercent * ulGlyphDependentDataLength/100); } - ulGlyphDependentDataLength += ulAllGlyphsLength; /* all formats will discard a percentage of the glyph data */ - - if (usFormat == TTFDELTA_DELTA) /* we're going to keep just a handfull of tables tables */ + else { - ulKeepTablesLength = TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HEAD_TAG); - ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, MAXP_TAG); - ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HHEA_TAG); - ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, VHEA_TAG); - ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, CMAP_TAG); - if (ulEBDTTableLength > 0) - ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, EBLC_TAG); - if (ulBdatTableLength > 0) - ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, BLOC_TAG); - - *pulOutputBufferLength = ulKeepTablesLength + (uint32)(flKeepPercent * ulGlyphDependentDataLength/100); + ulAllGlyphsLength = ulEBDTTableLength + ulBdatTableLength; + ulAllGlyphsLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, GLYF_TAG); + + if (usFormat == TTFDELTA_DELTA || usFormat == TTFDELTA_SUBSET1) + { /* these formats will compact some tables, discarding a percentage of these tables as well */ + /* tables compacted */ + ulGlyphDependentDataLength = TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, LTSH_TAG); + ulGlyphDependentDataLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HMTX_TAG); + ulGlyphDependentDataLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, VMTX_TAG); + ulGlyphDependentDataLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HDMX_TAG); + ulGlyphDependentDataLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, LOCA_TAG); + } + ulGlyphDependentDataLength += ulAllGlyphsLength; /* all formats will discard a percentage of the glyph data */ + + if (usFormat == TTFDELTA_DELTA) /* we're going to keep just a handfull of tables tables */ + { + ulKeepTablesLength = TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HEAD_TAG); + ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, MAXP_TAG); + ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, HHEA_TAG); + ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, VHEA_TAG); + ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, CMAP_TAG); + if (ulEBDTTableLength > 0) + ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, EBLC_TAG); + if (ulBdatTableLength > 0) + ulKeepTablesLength += TTTableLength((TTFACC_FILEBUFFERINFO *)pInputBufferInfo, BLOC_TAG); + + *pulOutputBufferLength = ulKeepTablesLength + (uint32)(flKeepPercent * ulGlyphDependentDataLength/100); + } + else + /* for straight subset, this will be: ulSrcBufferSize - (discard % * (Glyf table size + EBDT table size + bdat table size)) */ + *pulOutputBufferLength = ulSrcBufferSize - (uint32)(flDiscardPercent * ulGlyphDependentDataLength/100); } - else - /* for straight subset, this will be: ulSrcBufferSize - (discard % * (Glyf table size + EBDT table size + bdat table size)) */ - *pulOutputBufferLength = ulSrcBufferSize - (uint32)(flDiscardPercent * ulGlyphDependentDataLength/100); + return NO_ERROR; } @@ -613,8 +722,29 @@ int16 CreateDeltaTTF(CONST uint8 * puchSrcBuffer, } // make room for the extra glyph list - usCharCount = usListCount + usGlyphKeepCount; - pulKeepCharCodeList = (CHAR_ID *)Mem_Alloc(usCharCount * sizeof(CHAR_ID)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulCharCount = (uint32)usListCount + (uint32)usGlyphKeepCount; + if (ulCharCount > (uint32)USHRT_MAX) + { + Mem_Free(puchKeepGlyphList); + return ExitCleanup(ERR_PARAMETER11); + } + usCharCount = (uint16)ulCharCount; + + uint32 ulCharAllocSize; + if (ULongMult32((uint32)usCharCount, (uint32)sizeof(CHAR_ID), &ulCharAllocSize) != S_OK) + { + Mem_Free(puchKeepGlyphList); + return ExitCleanup(ERR_MEM); + } + pulKeepCharCodeList = (CHAR_ID *)Mem_Alloc(ulCharAllocSize); + } + else + { + usCharCount = usListCount + usGlyphKeepCount; + pulKeepCharCodeList = (CHAR_ID *)Mem_Alloc(usCharCount * sizeof(CHAR_ID)); + } if (!pulKeepCharCodeList) { Mem_Free(puchKeepGlyphList); @@ -644,7 +774,15 @@ int16 CreateDeltaTTF(CONST uint8 * puchSrcBuffer, else { // allocate for extra 4 chars - pulKeepCharCodeList = (CHAR_ID *)Mem_Alloc((usListCount + 4) * sizeof(CHAR_ID)); + if (TTF_SAFE_CHECKS_ENABLED()) { + if (usListCount > (uint16)(USHRT_MAX - 4)) + { + return ERR_PARAMETER11; + } + pulKeepCharCodeList = (CHAR_ID *)Mem_Alloc(((size_t)usListCount + 4u) * sizeof(CHAR_ID)); + } else { + pulKeepCharCodeList = (CHAR_ID *)Mem_Alloc((usListCount + 4) * sizeof(CHAR_ID)); + } if (!pulKeepCharCodeList) { return ERR_MEM; @@ -769,7 +907,15 @@ CONST_TTFACC_FILEBUFFERINFO InputBufferInfo; if (*ppuchDestBuffer == NULL || *pulDestBufferSize == 0) /* need to allocate some memory */ { - CalcOutputBufferSize(&InputBufferInfo, usGlyphListCount, usGlyphKeepCount, usFormat, ulSrcBufferSize, pulDestBufferSize); + if (TTF_SAFE_CHECKS_ENABLED()) { + if ((errCode = CalcOutputBufferSize(&InputBufferInfo, usGlyphListCount, usGlyphKeepCount, usFormat, ulSrcBufferSize, pulDestBufferSize)) != NO_ERROR) + { + Mem_Free(puchKeepGlyphList); + return ExitCleanup(errCode); + } + } else { + CalcOutputBufferSize(&InputBufferInfo, usGlyphListCount, usGlyphKeepCount, usFormat, ulSrcBufferSize, pulDestBufferSize); + } #ifdef _DEBUG #if !defined(ARGITERATOR_SUPPORTED) || (defined(ARGITERATOR_SUPPORTED) && ARGITERATOR_SUPPORTED) printf("Allocating %lu bytes for output buffer.\n", *pulDestBufferSize); @@ -896,7 +1042,15 @@ CONST_TTFACC_FILEBUFFERINFO InputBufferInfo; if (errCode == NO_ERROR) { /* now we need to allocate an array to keep a list of the actual glyphs we are keeping in the font */ - pusGlyphIndexArray = (uint16 *)Mem_Alloc(usDttfGlyphIndexCount * sizeof(*pusGlyphIndexArray)); /* big as we would ever need */ + if (TTF_SAFE_CHECKS_ENABLED()) { + uint32 ulGlyphArrayAllocSize; + if (ULongMult32((uint32)usDttfGlyphIndexCount, (uint32)sizeof(*pusGlyphIndexArray), &ulGlyphArrayAllocSize) != S_OK) + errCode = ERR_MEM; + else + pusGlyphIndexArray = (uint16 *)Mem_Alloc(ulGlyphArrayAllocSize); + } else { + pusGlyphIndexArray = (uint16 *)Mem_Alloc(usDttfGlyphIndexCount * sizeof(*pusGlyphIndexArray)); + } if (pusGlyphIndexArray == NULL) errCode = ERR_MEM; else diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttftabl1.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttftabl1.cpp index 54f3800a14d..f3bc5b7bae9 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttftabl1.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttftabl1.cpp @@ -28,6 +28,7 @@ #include "ttftabl1.h" #include "ttfcntrl.h" #include "ControlTableInit.h" +#include "ttf_safe_checks.h" /* if the _INDEX defines are changed, the Control_Table array below must be updated to match */ @@ -251,8 +252,19 @@ uint32 ulLength; for ( ul = 0; ul < (ulLength+3) / 4; ul++ ) { - if ( ReadLong( pInputBufferInfo, &ulWord, ulOffset + ul * sizeof(uint32)) != 0 ) - break; + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulReadOffset = ulOffset + ul * sizeof(uint32); + if (ulReadOffset < ulOffset) /* overflow check */ + break; + if ( ReadLong( pInputBufferInfo, &ulWord, ulReadOffset) != 0 ) + break; + } + else + { + if ( ReadLong( pInputBufferInfo, &ulWord, ulOffset + ul * sizeof(uint32)) != 0 ) + break; + } *pulChecksum = *pulChecksum + ulWord; } return ulOffset; /* any non zero number will do */ diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttftable.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttftable.cpp index cbb2de39061..89dc68877f1 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttftable.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/TtfDelta/ttftable.cpp @@ -28,6 +28,8 @@ #include "ttfdelta.h" /* for Dont care info */ #include "ttferror.h" #include "ttfdcnfg.h" +#include "intsafe_private_copy.h" +#include "ttf_safe_checks.h" /* ---------------------------------------------------------------------- */ PRIVATE int CRTCB AscendingTagCompare( CONST void *arg1, CONST void *arg2 ) @@ -269,6 +271,11 @@ uint32 ulBytesRead; } else { + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (ulGlyphCount + 1 > 0xFFFF) + return 0L; + } if (ReadGenericRepeat(pInputBufferInfo, (uint8 *)pulLoca, LONG_CONTROL, ulOffset, &ulBytesRead, (uint16) (ulGlyphCount + 1), sizeof(uint32)) != NO_ERROR) return 0L; } @@ -315,9 +322,17 @@ FORMAT4_SEGMENTS KeySegment; sIDIdx = (int32)(pFormat4Segment - (Format4Segments + usnSegments)); /* sIDIdx = (uint16) i - (uint16) usnSegments; */ sIDIdx += (int32) (pFormat4Segment->idRangeOffset / 2) + usCharCode - pFormat4Segment->startCount; - /* check against bounds */ - if (sIDIdx >= usnGlyphs) - return INVALID_GLYPH_INDEX; + /* check against bounds (both negative and too large) */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + if (sIDIdx < 0 || sIDIdx >= usnGlyphs) + return INVALID_GLYPH_INDEX; + } + else + { + if (sIDIdx >= usnGlyphs) + return INVALID_GLYPH_INDEX; + } usGlyphIdx = GlyphId[ sIDIdx ]; if (usGlyphIdx) /* Only add in idDelta if we've really got a glyph! */ @@ -401,7 +416,17 @@ int16 errCode; if ( *pusnIds == 0 ) return(NO_ERROR); - *ppGlyphId = (GLYPH_ID *)Mem_Alloc(*pusnIds * sizeof( (*ppGlyphId)[0] )); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)*pusnIds, (uint32)sizeof( (*ppGlyphId)[0] ), &ulAllocSize) != S_OK) + return ERR_MEM; + *ppGlyphId = (GLYPH_ID *)Mem_Alloc(ulAllocSize); + } + else + { + *ppGlyphId = (GLYPH_ID *)Mem_Alloc(*pusnIds * sizeof( (*ppGlyphId)[0] )); + } if ( *ppGlyphId == NULL ) return(ERR_MEM); @@ -431,7 +456,17 @@ uint32 ulBytesRead; /* allocate memory for variable length part of table */ - *Format4Segments = (FORMAT4_SEGMENTS *)Mem_Alloc( usSegCount * SIZEOF_FORMAT4_SEGMENTS); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)usSegCount, (uint32)SIZEOF_FORMAT4_SEGMENTS, &ulAllocSize) != S_OK) + return ERR_MEM; + *Format4Segments = (FORMAT4_SEGMENTS *)Mem_Alloc( ulAllocSize ); + } + else + { + *Format4Segments = (FORMAT4_SEGMENTS *)Mem_Alloc( usSegCount * SIZEOF_FORMAT4_SEGMENTS); + } if ( *Format4Segments == NULL ) return( ERR_MEM ); @@ -661,7 +696,17 @@ int16 errCode; if (pCmap->format != FORMAT6_CMAP_FORMAT) return( ERR_FORMAT ); - *glyphIndexArray = (uint16 *)Mem_Alloc( pCmap->entryCount * sizeof( uint16 )); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)pCmap->entryCount, (uint32)sizeof( uint16 ), &ulAllocSize) != S_OK) + return ERR_MEM; + *glyphIndexArray = (uint16 *)Mem_Alloc( ulAllocSize ); + } + else + { + *glyphIndexArray = (uint16 *)Mem_Alloc( pCmap->entryCount * sizeof( uint16 )); + } if ( *glyphIndexArray == NULL ) return( ERR_MEM ); @@ -1030,7 +1075,20 @@ int16 errCode = NO_ERROR; usCharCodeCount += (pFormat4Segments[ i ].endCount - pFormat4Segments[ i ].startCount + 1); } - *ppCharGlyphMapList = (PCHAR_GLYPH_MAP_LIST)Mem_Alloc(usCharCodeCount * sizeof(**ppCharGlyphMapList)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)usCharCodeCount, (uint32)sizeof(**ppCharGlyphMapList), &ulAllocSize) != S_OK) + { + FreeCmapFormat4(pFormat4Segments, pFormat4GlyphIdArray); + return ERR_MEM; + } + *ppCharGlyphMapList = (PCHAR_GLYPH_MAP_LIST)Mem_Alloc(ulAllocSize); + } + else + { + *ppCharGlyphMapList = (PCHAR_GLYPH_MAP_LIST)Mem_Alloc(usCharCodeCount * sizeof(**ppCharGlyphMapList)); + } if (*ppCharGlyphMapList == NULL) { FreeCmapFormat4(pFormat4Segments, pFormat4GlyphIdArray); @@ -1134,7 +1192,20 @@ int16 errCode = NO_ERROR; ulCharCodeCount += (pFormat12Groups[ i ].endCharCode - pFormat12Groups[ i ].startCharCode + 1); } - *ppCharGlyphMapList = (PCHAR_GLYPH_MAP_LIST_EX)Mem_Alloc(ulCharCodeCount * sizeof(**ppCharGlyphMapList)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32(ulCharCodeCount, (uint32)sizeof(**ppCharGlyphMapList), &ulAllocSize) != S_OK) + { + FreeCmapFormat12Groups( pFormat12Groups ); + return ERR_MEM; + } + *ppCharGlyphMapList = (PCHAR_GLYPH_MAP_LIST_EX)Mem_Alloc(ulAllocSize); + } + else + { + *ppCharGlyphMapList = (PCHAR_GLYPH_MAP_LIST_EX)Mem_Alloc(ulCharCodeCount * sizeof(**ppCharGlyphMapList)); + } if (*ppCharGlyphMapList == NULL) { FreeCmapFormat12Groups( pFormat12Groups ); @@ -1658,7 +1729,17 @@ char *pStr1, *pStr2; /* temps to point to either new or old string from PNAMEREC ulOffset = ulNameOffset + GetGenericSize(NAME_HEADER_CONTROL); /* first create the NameRecordStrings array to sort */ - pNameRecordStrings = (NAMERECORDSTRINGS *)Mem_Alloc(NameRecordCount * sizeof(*pNameRecordStrings)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)NameRecordCount, (uint32)sizeof(*pNameRecordStrings), &ulAllocSize) != S_OK) + return ERR_GENERIC; + pNameRecordStrings = (NAMERECORDSTRINGS *)Mem_Alloc(ulAllocSize); + } + else + { + pNameRecordStrings = (NAMERECORDSTRINGS *)Mem_Alloc(NameRecordCount * sizeof(*pNameRecordStrings)); + } if (pNameRecordStrings == NULL) return ERR_MEM; @@ -1845,7 +1926,17 @@ int32 lCopySize; ulOffset += usBytesRead; - aDirectory = (DIRECTORY *) Mem_Alloc(((int32)usnNewTables) * sizeof(DIRECTORY)); /* one extra for new table */ + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)usnNewTables, (uint32)sizeof(DIRECTORY), &ulAllocSize) != S_OK) + return ERR_MEM; + aDirectory = (DIRECTORY *) Mem_Alloc(ulAllocSize); + } + else + { + aDirectory = (DIRECTORY *) Mem_Alloc(((int32)usnNewTables) * sizeof(DIRECTORY)); /* one extra for new table */ + } if (aDirectory == NULL) return(ERR_MEM); @@ -2150,7 +2241,17 @@ int16 errCode; ulOffset += usBytesRead; /* Create a list of valid tables */ - aDirectory = (DIRECTORY *) Mem_Alloc((usnTables) * sizeof(DIRECTORY)); + if (TTF_SAFE_CHECKS_ENABLED()) + { + uint32 ulAllocSize; + if (ULongMult32((uint32)usnTables, (uint32)sizeof(DIRECTORY), &ulAllocSize) != S_OK) + return ERR_MEM; + aDirectory = (DIRECTORY *) Mem_Alloc(ulAllocSize); + } + else + { + aDirectory = (DIRECTORY *) Mem_Alloc((usnTables) * sizeof(DIRECTORY)); + } if (aDirectory == NULL) return(ERR_MEM); diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/truetype.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/truetype.cpp index 28ca4ab6f4e..3bec436548b 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/truetype.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/CPP/TrueTypeSubsetter/truetype.cpp @@ -57,16 +57,37 @@ using MS::Internal::TtfDelta::Mem_Free; using MS::Internal::TtfDelta::Mem_Alloc; using MS::Internal::TtfDelta::Mem_ReAlloc; using MS::Internal::TtfDelta::CreateDeltaTTF; +using MS::Internal::TtfDelta::g_fDWFBoundsCheckEnabled; namespace MS { namespace Internal { array ^ TrueTypeSubsetter::ComputeSubset(void * fontData, int fileSize, System::Uri ^ sourceUri, int directoryOffset, array ^ glyphArray) { + // Initialize the bounds check switch from AppContext (once). + static bool s_switchInitialized = false; + if (!s_switchInitialized) + { + s_switchInitialized = true; + bool switchValue = false; + System::AppContext::TryGetSwitch( + "Switch.MS.Internal.TtfDelta.DisableDirectWriteForwarderBoundsCheckProtection", + switchValue); + MS::Internal::TtfDelta::g_fDWFBoundsCheckEnabled = switchValue ? 0 : 1; + } + uint8 * puchDestBuffer = NULL; unsigned long ulDestBufferSize = 0, ulBytesWritten = 0; assert(glyphArray != nullptr && glyphArray->Length > 0 && glyphArray->Length <= USHRT_MAX); + if ((g_fDWFBoundsCheckEnabled != 0)) + { + if (fileSize <= 0) + { + throw gcnew FileFormatException(sourceUri); + } + } + pin_ptr pinnedGlyphArray = &glyphArray[0]; int16 errCode = CreateDeltaTTF( static_cast(fontData), @@ -91,10 +112,25 @@ array ^ TrueTypeSubsetter::ComputeSubset(void * fontData, int file try { - if (errCode == NO_ERROR) + if ((g_fDWFBoundsCheckEnabled != 0)) + { + if (errCode == NO_ERROR && ulBytesWritten <= INT_MAX) + { + retArray = gcnew array(ulBytesWritten); + System::Runtime::InteropServices::Marshal::Copy((System::IntPtr)puchDestBuffer, retArray, 0, ulBytesWritten); + } + else if (errCode == NO_ERROR) + { + errCode = static_cast(ERR_GENERIC); + } + } + else { - retArray = gcnew array(ulBytesWritten); - System::Runtime::InteropServices::Marshal::Copy((System::IntPtr)puchDestBuffer, retArray, 0, ulBytesWritten); + if (errCode == NO_ERROR) + { + retArray = gcnew array(ulBytesWritten); + System::Runtime::InteropServices::Marshal::Copy((System::IntPtr)puchDestBuffer, retArray, 0, ulBytesWritten); + } } } finally diff --git a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/main.cpp b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/main.cpp index 61ff8727d81..63426a64279 100644 --- a/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/main.cpp +++ b/src/Microsoft.DotNet.Wpf/src/DirectWriteForwarder/main.cpp @@ -13,6 +13,7 @@ // We cannot simply put this namespace specification in these 2 header files // or elase we will break the compilation of truetype subsetter. namespace MS { namespace Internal { namespace TtfDelta { +int g_fDWFBoundsCheckEnabled = 1; // definition for linker (initialized in truetype.cpp) #include "CPP\TrueTypeSubsetter\TtfDelta\GlobalInit.h" #include "CPP\TrueTypeSubsetter\TtfDelta\ControlTableInit.h" }}} // namespace MS::Internal::TtfDelta diff --git a/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PenImc.cpp b/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PenImc.cpp index 37ef3140bbd..b1de3cb5ca9 100644 --- a/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PenImc.cpp +++ b/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PenImc.cpp @@ -216,3 +216,15 @@ STDMETHODIMP CPimcManagerFactory::LockServer(BOOL fLock) } #endif // WANT_SINGLETON +///////////////////////////////////////////////////////////////////////////// +// Security mitigation switch - static storage and exported setter + +#include "PenImcSwitches.h" + +bool PenImcSwitches::s_boundsCheckDisabled = false; + +extern "C" void WINAPI SetDisablePenImcBoundsCheckProtection(BOOL fDisable) +{ + PenImcSwitches::SetPenImcBoundsCheckProtectionDisabled(!!fDisable); +} + diff --git a/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PenImc.def b/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PenImc.def index 193a703c902..ec3ce6c124d 100644 --- a/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PenImc.def +++ b/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PenImc.def @@ -22,4 +22,5 @@ EXPORTS LockWispObjectFromGit PRIVATE UnlockWispObjectFromGit PRIVATE RegisterDllForSxSCOM PRIVATE + SetDisablePenImcBoundsCheckProtection PRIVATE diff --git a/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PenImcSwitches.h b/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PenImcSwitches.h new file mode 100644 index 00000000000..d9a5f970167 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PenImcSwitches.h @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#pragma once + +/// +/// Switch for PenImc security fixes, set from managed CoreAppContextSwitches +/// via P/Invoke at startup. +/// +class PenImcSwitches +{ +public: + static bool IsPenImcBoundsCheckProtectionDisabled() + { + return s_boundsCheckDisabled; + } + + static void SetPenImcBoundsCheckProtectionDisabled(bool disabled) + { + s_boundsCheckDisabled = disabled; + } + +private: + static bool s_boundsCheckDisabled; +}; diff --git a/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PimcContext.cpp b/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PimcContext.cpp index 9578b17934e..454be79de5a 100644 --- a/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PimcContext.cpp +++ b/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PimcContext.cpp @@ -12,6 +12,9 @@ #include "PimcContext.h" #include "..\tablib\sidutils.h" #include "..\tablib\scopes.h" +#include + +#include "PenImcSwitches.h" using namespace ComUtils; @@ -524,7 +527,16 @@ HRESULT CPimcContext::EnsurePackets(DWORD cb) { if (m_pbPackets) delete [] m_pbPackets; - m_cbPackets = max(256, cb * 2); + if (!PenImcSwitches::IsPenImcBoundsCheckProtectionDisabled()) + { + UINT cbDoubled; + CHR(UIntMult(cb, 2, &cbDoubled)); + m_cbPackets = max(256, (DWORD)cbDoubled); + } + else + { + m_cbPackets = max(256, cb * 2); + } CHR_MEMALLOC(m_pbPackets = new BYTE[m_cbPackets]); } CLEANUP: diff --git a/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PimcTablet.cpp b/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PimcTablet.cpp index 1cb2d9f4901..4f975793d02 100644 --- a/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PimcTablet.cpp +++ b/src/Microsoft.DotNet.Wpf/src/PenImc/dll/PimcTablet.cpp @@ -10,6 +10,7 @@ #include "PimcTablet.h" #include #include +#include "PenImcSwitches.h" using namespace ComUtils; @@ -46,6 +47,11 @@ static const WCHAR* MOUSEDEVICE_BUTTON_ONE_NAME = L"Tip Switch"; static const WCHAR* MOUSEDEVICE_BUTTON_TWO_NAME = L"Barrel Switch"; static const WCHAR* MOUSEDEVICE_PLUGANDPLAYID = L"SCREEN"; +// Reasonable upper bounds for COM-returned counts to guard against +// integer overflow in allocation size calculations. +static const DWORD MAX_TABLET_CURSORS = 128; +static const DWORD MAX_CURSOR_BUTTONS = 64; + static void EnsureNoDuplicateGUIDs(__in GUID *pGUID, __inout ULONG &cGUID) { ULONG iIndex = 0; @@ -545,6 +551,10 @@ STDMETHODIMP CPimcTablet::RefreshCursorInfo() if (m_pTabS) { CHR(m_pTabS->GetCursorCount(&m_cCursors)); + if (!PenImcSwitches::IsPenImcBoundsCheckProtectionDisabled()) + { + CHR(m_cCursors <= MAX_TABLET_CURSORS ? S_OK : E_UNEXPECTED); + } m_apCursorInfo = new PCURSORINFO[m_cCursors](); CHR(m_apCursorInfo ? S_OK : E_OUTOFMEMORY); @@ -566,6 +576,10 @@ STDMETHODIMP CPimcTablet::RefreshCursorInfo() DWORD cButtons; CHR(pCursorS->GetButtonCount(&cButtons)); + if (!PenImcSwitches::IsPenImcBoundsCheckProtectionDisabled()) + { + CHR(cButtons <= MAX_CURSOR_BUTTONS ? S_OK : E_UNEXPECTED); + } pCursorInfo->cButtons = cButtons; pCursorInfo->apButtonInfo = new PCURSORBUTTONINFO[cButtons](); @@ -839,7 +853,14 @@ STDMETHODIMP CPimcTablet::IsPropertySupported(GUID guid, __out BOOL * pfSupporte DHR; CHR(pfSupported ? S_OK : E_INVALIDARG); PROPERTY_METRICS metric; - *pfSupported = S_OK == m_pTabS->GetPropertyMetrics(guid, &metric); + if (!PenImcSwitches::IsPenImcBoundsCheckProtectionDisabled()) + { + *pfSupported = m_pTabS ? (S_OK == m_pTabS->GetPropertyMetrics(guid, &metric)) : FALSE; + } + else + { + *pfSupported = S_OK == m_pTabS->GetPropertyMetrics(guid, &metric); + } CLEANUP: RHR; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/Win32/UnsafeNativeMethodsPenimc.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/Win32/UnsafeNativeMethodsPenimc.cs index e3bf8b4934f..7d19615cb34 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/Win32/UnsafeNativeMethodsPenimc.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/Win32/UnsafeNativeMethodsPenimc.cs @@ -81,6 +81,9 @@ internal static class UnsafeNativeMethods [DllImport(ExternDll.Penimc, CharSet=CharSet.Auto)] internal static extern IntPtr RegisterDllForSxSCOM(); + [DllImport(ExternDll.Penimc, CharSet = CharSet.Auto)] + internal static extern void SetDisablePenImcBoundsCheckProtection([MarshalAs(UnmanagedType.Bool)] bool value); + #endregion /// @@ -107,6 +110,9 @@ internal static void EnsurePenImcClassesActivated() { throw new InvalidOperationException(SR.Format(SR.PenImcSxSRegistrationFailed, ExternDll.Penimc)); } + + // Pass security mitigation switch to native PenImc code. + SetDisablePenImcBoundsCheckProtection(CoreAppContextSwitches.DisablePenImcBoundsCheckProtection); } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs index 98984b5c4bc..c244ffbbc95 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs @@ -400,5 +400,50 @@ public static bool DisableSpecialCharacterLigature } #endregion + + #region DisableDirectWriteForwarderBoundsCheckProtection + + internal const string DisableDirectWriteForwarderBoundsCheckProtectionSwitchName = "Switch.MS.Internal.TtfDelta.DisableDirectWriteForwarderBoundsCheckProtection"; + private static int _disableDirectWriteForwarderBoundsCheckProtection; + public static bool DisableDirectWriteForwarderBoundsCheckProtection + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return LocalAppContext.GetCachedSwitchValue(DisableDirectWriteForwarderBoundsCheckProtectionSwitchName, ref _disableDirectWriteForwarderBoundsCheckProtection); + } + } + + #endregion + + #region DisablePenImcBoundsCheckProtection + + internal const string DisablePenImcBoundsCheckProtectionSwitchName = "Switch.MS.Internal.PenImc.DisablePenImcBoundsCheckProtection"; + private static int _disablePenImcBoundsCheckProtection; + public static bool DisablePenImcBoundsCheckProtection + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return LocalAppContext.GetCachedSwitchValue(DisablePenImcBoundsCheckProtectionSwitchName, ref _disablePenImcBoundsCheckProtection); + } + } + + #endregion + + #region DisableWpfGfxBoundsCheckProtection + + internal const string DisableWpfGfxBoundsCheckProtectionSwitchName = "Switch.MS.Internal.WpfGfx.DisableWpfGfxBoundsCheckProtection"; + private static int _disableWpfGfxBoundsCheckProtection; + public static bool DisableWpfGfxBoundsCheckProtection + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return LocalAppContext.GetCachedSwitchValue(DisableWpfGfxBoundsCheckProtectionSwitchName, ref _disableWpfGfxBoundsCheckProtection); + } + } + + #endregion } } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/MediaSystem.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/MediaSystem.cs index 40edd7976b3..9b980fda541 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/MediaSystem.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/MediaSystem.cs @@ -70,6 +70,9 @@ public static bool Startup(MediaContext mc) // Setting renderOption for Hardware acceleration in RDP as per appcontext switch. UnsafeNativeMethods.RenderOptions_EnableHardwareAccelerationInRdp(CoreAppContextSwitches.EnableHardwareAccelerationInRdp); + // Pass security mitigation switch to native WpfGfx code. + UnsafeNativeMethods.WpfGfx_SetDisableBoundsCheckProtection(CoreAppContextSwitches.DisableWpfGfxBoundsCheckProtection); + // Consider making MediaSystem.ConnectTransport return the state of transport connectedness so // that we can initialize the media system to a disconnected state. diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/UnsafeNativeMethodsMilCoreApi.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/UnsafeNativeMethodsMilCoreApi.cs index a17613aec1c..3b2964686d4 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/UnsafeNativeMethodsMilCoreApi.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/UnsafeNativeMethodsMilCoreApi.cs @@ -198,6 +198,9 @@ internal static extern unsafe void RenderOptions_ForceSoftwareRenderingModeForPr [DllImport(DllImport.MilCore, EntryPoint = "RenderOptions_EnableHardwareAccelerationInRdp")] internal static extern unsafe void RenderOptions_EnableHardwareAccelerationInRdp(bool value); + [DllImport(DllImport.MilCore, EntryPoint = "WpfGfx_SetDisableBoundsCheckProtection")] + internal static extern unsafe void WpfGfx_SetDisableBoundsCheckProtection(bool value); + [DllImport(DllImport.MilCore, EntryPoint = "MilResource_CreateCWICWrapperBitmap")] internal static extern unsafe int /* HRESULT */ CreateCWICWrapperBitmap( BitmapSourceSafeMILHandle /* IWICBitmapSource */ pIWICBitmapSource, diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/effects/effectlist.cpp b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/effects/effectlist.cpp index 4ded9028ff9..dbf5945d138 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/effects/effectlist.cpp +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/effects/effectlist.cpp @@ -21,6 +21,7 @@ **************************************************************************/ #include "precomp.hpp" +#include "..\shared\WpfGfxSwitches.h" MtDefine(CEffectList, MILRender, "CEffectList"); @@ -320,16 +321,47 @@ CEffectList::GetParameters( API_ENTRY_NOFPU(IMILEffectList::GetParameters); HRESULT hr = S_OK; - UINT uiSize = m_rgParamBlock[idxEffect].cbParamSize; - if (idxEffect >= (UINT)m_rgParamBlock.GetCount() || - size < uiSize || - pData == NULL) + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) { - IFC(E_INVALIDARG); + if (idxEffect >= (UINT)m_rgParamBlock.GetCount() || + pData == NULL) + { + IFC(E_INVALIDARG); + } + + UINT uiSize = m_rgParamBlock[idxEffect].cbParamSize; + if (size < uiSize) + { + IFC(E_INVALIDARG); + } + + UINT cbParamOffset = m_rgParamBlock[idxEffect].cbParamOffset; + UINT cbDataBlockSize = m_rgDataBlock.GetCount(); + UINT cbEndOffset; + + // Validate that offset + size does not exceed data block bounds + if (FAILED(UIntAdd(cbParamOffset, uiSize, &cbEndOffset)) || + cbEndOffset > cbDataBlockSize) + { + IFC(E_INVALIDARG); + } + + void* pParamData = m_rgDataBlock.GetDataBuffer() + cbParamOffset; + GpMemcpy(pData, pParamData, uiSize); } + else + { + UINT uiSize = m_rgParamBlock[idxEffect].cbParamSize; + if (idxEffect >= (UINT)m_rgParamBlock.GetCount() || + size < uiSize || + pData == NULL) + { + IFC(E_INVALIDARG); + } - void* pParamData = m_rgDataBlock.GetDataBuffer() + m_rgParamBlock[idxEffect].cbParamOffset; - GpMemcpy(pData, pParamData, uiSize); + void* pParamData = m_rgDataBlock.GetDataBuffer() + m_rgParamBlock[idxEffect].cbParamOffset; + GpMemcpy(pData, pParamData, uiSize); + } Cleanup: API_CHECK(hr); diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/scanop/soconvert.cpp b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/scanop/soconvert.cpp index 67235c49dc3..8129b289e1b 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/scanop/soconvert.cpp +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/scanop/soconvert.cpp @@ -40,6 +40,7 @@ // #include "precomp.hpp" +#include "..\shared\WpfGfxSwitches.h" //+----------------------------------------------------------------------------- // @@ -84,8 +85,17 @@ Convert_1_32bppARGB( UINT n, bits; - ARGB c0 = ppal->Entries[0]; - ARGB c1 = ppal->Entries[1]; + ARGB c0 = 0, c1 = 0; + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + c0 = ppal->Count > 0 ? ppal->Entries[0] : 0; + c1 = ppal->Count > 1 ? ppal->Entries[1] : 0; + } + else + { + c0 = ppal->Entries[0]; + c1 = ppal->Entries[1]; + } // NOTE: We choose code size over speed here @@ -149,6 +159,7 @@ Convert_4_32bppARGB( Assert(ppal); const ARGB* colors = ppal->Entries; + UINT paletteCount = ppal->Count; UINT n = uiCount >> 1; // Handle whole bytes @@ -160,8 +171,20 @@ Convert_4_32bppARGB( Assert((bits >> 4) < ppal->Count); Assert((bits & 0xf) < ppal->Count); - pDest[0] = colors[bits >> 4]; - pDest[1] = colors[bits & 0xf]; + UINT hiNibble = bits >> 4; + UINT loNibble = bits & 0xf; + + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + // Clamp indices to valid palette range to prevent OOB read + pDest[0] = hiNibble < paletteCount ? colors[hiNibble] : 0; + pDest[1] = loNibble < paletteCount ? colors[loNibble] : 0; + } + else + { + pDest[0] = colors[hiNibble]; + pDest[1] = colors[loNibble]; + } pDest += 2; } @@ -169,7 +192,17 @@ Convert_4_32bppARGB( // Handle the last odd nibble, if any if (uiCount & 1) - *pDest = colors[*pSrc >> 4]; + { + UINT lastNibble = *pSrc >> 4; + if(!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + *pDest = lastNibble < paletteCount ? colors[lastNibble] : 0; + } + else + { + *pDest = colors[lastNibble]; + } + } } @@ -186,6 +219,7 @@ Convert_2_32bppARGB( const ColorPalette *ppal = DYNCAST(OSDPalette, pSOP->m_posd)->m_pPalette; Assert(ppal); + UINT paletteCount = ppal->Count; const ARGB* colors = ppal->Entries; @@ -205,7 +239,14 @@ Convert_2_32bppARGB( Assert(i < ppal->Count); - * pDest++ = colors[i]; + if(!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + *pDest++ = i < paletteCount ? colors[i] : 0; + } + else + { + *pDest++ = colors[i]; + } c --; } @@ -226,19 +267,29 @@ Convert_8_32bppARGB( const ColorPalette *ppal = DYNCAST(OSDPalette, pSOP->m_posd)->m_pPalette; Assert(ppal); + UINT paletteCount = ppal->Count; const ARGB* colors = ppal->Entries; while (uiCount--) { + BYTE index = *pSrc++; #if DBG - if (*pSrc >= ppal->Count) + if (index >= paletteCount) { TraceTag((tagMILWarning, "Palette missing entries on conversion from 8bpp to 32bppARGB")); } #endif - *pDest++ = colors[*pSrc++]; + + if(!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + *pDest++ = index < paletteCount ? colors[index] : 0; + } + else + { + *pDest++ = colors[index]; + } } } diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/WpfGfxSwitches.cpp b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/WpfGfxSwitches.cpp new file mode 100644 index 00000000000..189817c241f --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/WpfGfxSwitches.cpp @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include +#include "WpfGfxSwitches.h" + +// Static storage for WpfGfxSwitches +bool WpfGfxSwitches::g_fWpfGfxBoundsCheckProtectionDisabled = false; + +void WINAPI +WpfGfx_SetDisableBoundsCheckProtection(BOOL fDisable) +{ + WpfGfxSwitches::SetWpfGfxBoundsCheckProtectionDisabled(!!fDisable); +} diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/WpfGfxSwitches.h b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/WpfGfxSwitches.h new file mode 100644 index 00000000000..c041446bf67 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/WpfGfxSwitches.h @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#pragma once + +/// +/// Switch for WpfGfx security fixes, set from managed CoreAppContextSwitches +/// via P/Invoke at startup. +/// +class WpfGfxSwitches +{ +public: + static bool IsWpfGfxBoundsCheckProtectionDisabled() + { + return g_fWpfGfxBoundsCheckProtectionDisabled; + } + + static void SetWpfGfxBoundsCheckProtectionDisabled(bool disabled) + { + g_fWpfGfxBoundsCheckProtectionDisabled = disabled; + } + +private: + static bool g_fWpfGfxBoundsCheckProtectionDisabled; +}; diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/dynarray.h b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/dynarray.h index c65ae22fa26..927e9cef07e 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/dynarray.h +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/dynarray.h @@ -89,16 +89,30 @@ template class DynArray : public DynArray __ecount(this->Count) T &First() const { __pfx_assert(Count > 0, "Buffer overflow accessing empty DynArray"); - Assert(Count > 0); + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + FreAssert(Count > 0); + } + else + { + Assert(Count > 0); + } return GetDataBuffer()[0]; } __ecount(1) T &Last() const { __pfx_assert(Count > 0, "Buffer overflow accessing empty DynArray"); - Assert(Count > 0); + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + FreAssert(Count > 0); + } + else + { + Assert(Count > 0); + } #pragma prefast (push) -#pragma prefast (disable: 37001 37002 37003, "This operation will not overflow becasuse of the Assert above.") +#pragma prefast (disable: 37001 37002 37003, "This operation will not overflow because of the Assert/FreAssert above.") return GetDataBuffer()[Count-1]; #pragma prefast (pop) } diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/shared.h b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/shared.h index 00a0e0808cc..ce6a9b86dcc 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/shared.h +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/shared.h @@ -29,6 +29,7 @@ #include "refcountbase.h" #include "arithmetic.h" #include "dynarrayimpl.h" +#include "WpfGfxSwitches.h" #include "dynarray.h" #include "heap.h" #include "resourcecache.h" diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/shared.vcxproj b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/shared.vcxproj index d20cf37c3ad..8d2cb7bcb1c 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/shared.vcxproj +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/common/shared/shared.vcxproj @@ -71,6 +71,7 @@ + \ No newline at end of file diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/common/engine.cpp b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/common/engine.cpp index ba3244c453f..d8882a0fef0 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/common/engine.cpp +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/common/engine.cpp @@ -303,6 +303,3 @@ CCommonRegistryData::InitializeDWMKeysFromRegistry() } #endif - - - diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/common/gradienttexture.cpp b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/common/gradienttexture.cpp index b16b6d203d2..75ca633d4f3 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/common/gradienttexture.cpp +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/common/gradienttexture.cpp @@ -1176,7 +1176,7 @@ CGradientTextureGenerator::SetFirstStop( Assert(uStopCount <= MAX_GRADIENTSTOP_COUNT); UINT uCurrentIndex; - + // // This method handles all possible cominations to determine the gradient stop at 0.0 // @@ -1450,7 +1450,7 @@ CGradientTextureGenerator::SetLastStop( // This method requires that pStopBuffer contain at least two gradient stops Assert(uStopCount >= 2); Assert(uStopCount <= MAX_GRADIENTSTOP_COUNT); - + // // See the comment in SetFirstStop for the URL containing the Spec Case #'s // This method handles the remaining cominations of stops to determine the diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/dll/wpfgfx.def b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/dll/wpfgfx.def index 518ecd99426..fe56105c301 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/dll/wpfgfx.def +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/dll/wpfgfx.def @@ -138,4 +138,5 @@ EXPORTS RenderOptions_ForceSoftwareRenderingModeForProcess RenderOptions_IsSoftwareRenderingForcedForProcess RenderOptions_EnableHardwareAccelerationInRdp + WpfGfx_SetDisableBoundsCheckProtection diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/d3dglyphbank.cpp b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/d3dglyphbank.cpp index fb7b1fdb0ba..29355c75192 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/d3dglyphbank.cpp +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/d3dglyphbank.cpp @@ -343,9 +343,21 @@ HRESULT CD3DGlyphBank::RectFillAlpha( // pDst00 points to the texel in destination that corresponds // to point (x,y) = (0,0) in source array - const BYTE *pSrc00 = pSrcData - - srcPitch*fullDataRect.top - - fullDataRect.left; + // Use 64-bit arithmetic to avoid integer overflow when srcPitch * fullDataRect.top + // exceeds INT32 range (possible with large ClearType glyphs where srcPitch = width*3). + const BYTE *pSrc00; + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + pSrc00 = pSrcData + - (INT64)srcPitch*fullDataRect.top + - fullDataRect.left; + } + else + { + pSrc00 = pSrcData + - srcPitch*fullDataRect.top + - fullDataRect.left; + } // pSrc00 points to (x,y) = (0,0) in given data array int y; @@ -365,7 +377,15 @@ HRESULT CD3DGlyphBank::RectFillAlpha( for (; y < ymax; y++) { BYTE* pDstRow = pDst00 + lockedRect.Pitch*y; - const BYTE* pSrcRow = pSrc00 + srcPitch*y; + const BYTE* pSrcRow; + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + pSrcRow = pSrc00 + (INT64)srcPitch*y; + } + else + { + pSrcRow = pSrc00 + srcPitch*y; + } memset(pDstRow + srcRect.left, 0, xmin - srcRect.left); memcpy(pDstRow + xmin, pSrcRow + xmin, xmax - xmin); diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/hwbitmapcolorsource.cpp b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/hwbitmapcolorsource.cpp index 73785e9e942..8d77ba689b2 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/hwbitmapcolorsource.cpp +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/hwbitmapcolorsource.cpp @@ -3716,7 +3716,14 @@ CHwBitmapColorSource::PushTheSourceBitsToVideoMemory( rcMilTextureLock.Y = 0; rcMilTextureLock.Width = static_cast(m_d3dsdRequired.Width); rcMilTextureLock.Height = static_cast(m_d3dsdRequired.Height); - cbLockedBufferSize = GetRequiredBufferSize(m_fmtTexture, d3dlrBitmapCopyDestination.Pitch, &rcMilTextureLock); + if(!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + IFC(HrGetRequiredBufferSize(m_fmtTexture, d3dlrBitmapCopyDestination.Pitch, &rcMilTextureLock, &cbLockedBufferSize)); + } + else + { + cbLockedBufferSize = GetRequiredBufferSize(m_fmtTexture, d3dlrBitmapCopyDestination.Pitch, &rcMilTextureLock); + } fLockedSurface = true; } @@ -3774,16 +3781,49 @@ CHwBitmapColorSource::PushTheSourceBitsToVideoMemory( rcDirty.left, rcDirty.top, rcDirty.right - rcDirty.left, rcDirty.bottom - rcDirty.top }; - BYTE *pvDestPixels = reinterpret_cast(d3dlrBitmapCopyDestination.pBits) - + uPixelSize * static_cast(ptDest.x) - + d3dlrBitmapCopyDestination.Pitch * ptDest.y + - // Offset according to border. - uBorderSize * (uPixelSize + d3dlrBitmapCopyDestination.Pitch); + BYTE *pvDestPixels; + UINT cbRemainingBufferSize; + + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + // Compute destination offset with overflow-safe arithmetic. + UINT uDestOffsetX; + UINT uDestOffsetY; + UINT uDestOffset; + UINT uBorderOffset; + + IFC(UIntMult(uPixelSize, static_cast(ptDest.x), &uDestOffsetX)); + IFC(UIntMult(static_cast(d3dlrBitmapCopyDestination.Pitch), static_cast(ptDest.y), &uDestOffsetY)); + IFC(UIntAdd(uDestOffsetX, uDestOffsetY, &uDestOffset)); + + // Border offset: uBorderSize * (uPixelSize + Pitch) + IFC(UIntAdd(uPixelSize, static_cast(d3dlrBitmapCopyDestination.Pitch), &uBorderOffset)); + IFC(UIntMult(uBorderSize, uBorderOffset, &uBorderOffset)); + IFC(UIntAdd(uDestOffset, uBorderOffset, &uDestOffset)); + + // Verify the offset doesn't exceed the locked buffer + if (uDestOffset >= cbLockedBufferSize) + { + IFC(WGXERR_INTERNALERROR); + } + + pvDestPixels = reinterpret_cast(d3dlrBitmapCopyDestination.pBits) + + uDestOffset; + cbRemainingBufferSize = cbLockedBufferSize - uDestOffset; + } + else + { + pvDestPixels = reinterpret_cast(d3dlrBitmapCopyDestination.pBits) + + uPixelSize * static_cast(ptDest.x) + + d3dlrBitmapCopyDestination.Pitch * ptDest.y + + uBorderSize * (uPixelSize + d3dlrBitmapCopyDestination.Pitch); + cbRemainingBufferSize = cbLockedBufferSize; + } IFC(pIBitmapSource->CopyPixels( &rcCopy, d3dlrBitmapCopyDestination.Pitch, - cbLockedBufferSize, + cbRemainingBufferSize, pvDestPixels )); } @@ -4451,7 +4491,8 @@ DbgTintDirtyRectangle( // Function: SelfCopyPixels // // Synopsis: Copy source rectangle to new location (non-overlapping) -// in image. Does not check memory! +// in image. Validates all offsets against buffer size using +// overflow-safe arithmetic before copying. // //----------------------------------------------------------------------------- void @@ -4468,20 +4509,52 @@ SelfCopyPixels( __inout_bcount(cbBufferSize) BYTE *pvPixels // Pointer to start of output ) { - #pragma prefast(suppress: 22013, "Offset calculations may not overflow") - UINT offReadEnd = cbStep * rc.right + cbStride * (rc.bottom-1); - UINT offWriteEnd = cbStep * (x+rc.Width()) + cbStride * (y+rc.Height()-1); - - if (cbBufferSize < offReadEnd) - { - RIP("Buffer size too small for source rectangle"); - } - else if (cbBufferSize < offWriteEnd) - { - RIP("Buffer size too small for destination rectangle"); - } - else + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) { + // Compute the end offsets for both source and destination using + // overflow-safe arithmetic. If any multiplication or addition + // overflows, the bounds check will correctly reject the operation. + UINT offReadEnd = 0; + UINT offWriteEnd = 0; + UINT temp1, temp2; + + bool fOverflow = false; + + // offReadEnd = cbStep * rc.right + cbStride * (rc.bottom - 1) + if (rc.bottom == 0 || + FAILED(UIntMult(cbStep, rc.right, &temp1)) || + FAILED(UIntMult(cbStride, rc.bottom - 1, &temp2)) || + FAILED(UIntAdd(temp1, temp2, &offReadEnd))) + { + fOverflow = true; + } + + // offWriteEnd = cbStep * (x + rc.Width()) + cbStride * (y + rc.Height() - 1) + if (!fOverflow) + { + UINT destRight, destBottom; + if (FAILED(UIntAdd(x, rc.Width(), &destRight)) || + FAILED(UIntMult(cbStep, destRight, &temp1)) || + FAILED(UIntAdd(y, rc.Height(), &destBottom)) || + destBottom == 0 || + FAILED(UIntMult(cbStride, destBottom - 1, &temp2)) || + FAILED(UIntAdd(temp1, temp2, &offWriteEnd))) + { + fOverflow = true; + } + } + + if (fOverflow || cbBufferSize < offReadEnd) + { + RIP("Buffer size too small for source rectangle"); + return; + } + else if (cbBufferSize < offWriteEnd) + { + RIP("Buffer size too small for destination rectangle"); + return; + } + for (UINT i = rc.left; i < rc.right; ++i) { for (UINT j = rc.top; j < rc.bottom; ++j) @@ -4495,6 +4568,36 @@ SelfCopyPixels( } } } + else + { + #pragma prefast(suppress: 22013, "Offset calculations may not overflow") + UINT offReadEnd = cbStep * rc.right + cbStride * (rc.bottom-1); + UINT offWriteEnd = cbStep * (x+rc.Width()) + cbStride * (y+rc.Height()-1); + + if (cbBufferSize < offReadEnd) + { + RIP("Buffer size too small for source rectangle"); + } + else if (cbBufferSize < offWriteEnd) + { + RIP("Buffer size too small for destination rectangle"); + } + else + { + for (UINT i = rc.left; i < rc.right; ++i) + { + for (UINT j = rc.top; j < rc.bottom; ++j) + { + BYTE *pvSrc = pvPixels + j * cbStride + i * cbStep; + BYTE *pvDst = pvPixels + ((j-rc.top)+y) * cbStride + ((i-rc.left)+x) * cbStep; + Assert(pvDst + cbStep <= pvPixels + cbBufferSize); + Assert(pvSrc + cbStep <= pvPixels + cbBufferSize); + + RtlCopyMemory(pvDst, pvSrc, cbStep); + } + } + } + } return; } diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/resources/glyphrunslave.cpp b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/resources/glyphrunslave.cpp index 7c81f299d53..8dabb56c491 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/resources/glyphrunslave.cpp +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/resources/glyphrunslave.cpp @@ -1700,7 +1700,22 @@ CGlyphRunRealization::EnsureValidAlphaMap(__in const EnhancedContrastTable *pECT CMilRectL CTBbox(clearTypeAlphaMapBoundingBox); CMilRectL UnionBBox(BLBbox); UnionBBox.Union(CTBbox); - UINT32 textureSize = UnionBBox.Width() * UnionBBox.Height(); + + UINT32 textureSize; + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + UINT64 textureSize64 = (UINT64)UnionBBox.Width() * UnionBBox.Height(); + if (textureSize64 > UINT32_MAX) + { + IFC(WGXERR_BADNUMBER); + } + textureSize = (UINT32)textureSize64; + } + else + { + textureSize = UnionBBox.Width() * UnionBBox.Height(); + } + BYTE *pCombinedAlphaMap = (BYTE*)WPFAlloc(ProcessHeap, Mt(GlyphBitmapClearType), textureSize @@ -1866,14 +1881,25 @@ CGlyphRunRealization::RealizeAlphaBoundsAndTextures( // UINT32 width = boundingBox.right - boundingBox.left; UINT32 height = boundingBox.bottom - boundingBox.top; - UINT32 textureStride = width; + UINT32 textureStrideU32 = width; if (textureType == DWRITE_TEXTURE_CLEARTYPE_3x1) { - // ClearType bitmaps (DWRITE_TEXTURE_CLEARTYPE_3x1) contain 3 bytes per pixel, - // Aliased bitmaps only contain 1. - textureStride *= 3; + textureStrideU32 *= 3; + } + UINT32 textureSize = 0; + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + UINT64 textureSize64 = (UINT64)textureStrideU32 * height; + if (textureSize64 > UINT32_MAX) + { + IFC(WGXERR_BADNUMBER); + } + textureSize = (UINT32)textureSize64; + } + else + { + textureSize = textureStrideU32 * height; } - UINT32 textureSize = textureStride * height; pAlphaValues = (BYTE *)WPFAlloc(ProcessHeap, (textureType == DWRITE_TEXTURE_CLEARTYPE_3x1 ? Mt(GlyphBitmapClearType) : Mt(GlyphBitmapBiLevel)), @@ -1902,7 +1928,7 @@ CGlyphRunRealization::RealizeAlphaBoundsAndTextures( // pECT may be NULL if the contrast enhancement value is 0. if (pECT) { - pECT->RenormalizeAndApplyContrast(pAlphaValues, boundingBox.right - boundingBox.left, boundingBox.bottom - boundingBox.top, textureStride, textureSize); + pECT->RenormalizeAndApplyContrast(pAlphaValues, boundingBox.right - boundingBox.left, boundingBox.bottom - boundingBox.top, textureStrideU32, textureSize); } } else @@ -1910,10 +1936,27 @@ CGlyphRunRealization::RealizeAlphaBoundsAndTextures( // Future Consideration: probably shouldn't do this texture expansion. Need to write a different // shader and shrink the texture to benefit perf. Aliased text is relatively rare however, so it's // not worth the investment at this point. - BYTE *pNewAlphaValues = (BYTE *)WPFAlloc(ProcessHeap, - Mt(GlyphBitmapBiLevel), - textureSize * 3); - TraceTagText((tagError, "CGlyphRunRealization::RealizeAlphaBoundsAndTextures, allocated bytes: %d", textureSize * 3)); + BYTE *pNewAlphaValues; + if (!WpfGfxSwitches::IsWpfGfxBoundsCheckProtectionDisabled()) + { + UINT64 expandedSize64 = (UINT64)textureSize * 3; + if (expandedSize64 > UINT32_MAX) + { + IFC(WGXERR_BADNUMBER); + } + UINT32 expandedSize = (UINT32)expandedSize64; + pNewAlphaValues = (BYTE *)WPFAlloc(ProcessHeap, + Mt(GlyphBitmapBiLevel), + expandedSize); + TraceTagText((tagError, "CGlyphRunRealization::RealizeAlphaBoundsAndTextures, allocated bytes: %d", expandedSize)); + } + else + { + pNewAlphaValues = (BYTE *)WPFAlloc(ProcessHeap, + Mt(GlyphBitmapBiLevel), + textureSize * 3); + TraceTagText((tagError, "CGlyphRunRealization::RealizeAlphaBoundsAndTextures, allocated bytes: %d", textureSize * 3)); + } for (UINT i = 0; i < textureSize; i++) {