Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / MS / Internal / FontCache / FontCacheUtil.cs / 1 / FontCacheUtil.cs
//---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // Description: Miscellaneous utility functions for font handling code. // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Packaging; using System.Reflection; using System.Resources; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using System.Windows; using System.Windows.Markup; // for XmlLanguage using System.Windows.Media; using System.Windows.Navigation; using System.Windows.Threading; using MS.Win32; using MS.Internal; using MS.Internal.FontFace; using MS.Internal.PresentationCore; using MS.Internal.Resources; using MS.Utility; using Microsoft.Win32.SafeHandles; // Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 namespace MS.Internal.FontCache { ////// CheckedPointer is used to reference a fixed size memory block. /// The purpose of the class is to protect the memory block from overruns. /// ArgumentOutOfRangeException is thrown when an overrun is detected. /// ////// This class could potentially contain critical information for the case /// where the data pointed to by "pointer" parameter is obtained under an /// elevation. We consder CheckedPointer class itself to be security Agnostic. /// When someone creates this instance from critical data, they'll need to /// mark the instance as SecurityCritical and track usage. /// [FriendAccessAllowed] internal struct CheckedPointer { ////// Critical: This code yields unverifiable demand /// [SecurityCritical] internal unsafe CheckedPointer(void * pointer, int size) { _pointer = pointer; _size = size; } ////// Critical: This calls into PositionPointer which is link demand protected /// also it constructs data for a checked pointer. /// [SecurityCritical] internal CheckedPointer(UnmanagedMemoryStream stream) { Debug.Assert(stream.Position == 0); unsafe { _pointer = stream.PositionPointer; } long length = stream.Length; if (length < 0 || length > int.MaxValue) throw new ArgumentOutOfRangeException(); _size = (int)length; } ////// Critical: Call into unsafe code block /// TreatAsSafe: This is ok to call /// internal bool IsNull { [SecurityCritical,SecurityTreatAsSafe] get { unsafe { return (_pointer == null); } } } ////// Critical: _size is marked as critical. /// TreatAsSafe: _size is critical only for set. /// internal int Size { [SecurityCritical, SecurityTreatAsSafe] get { return _size; } } ////// Critical: This code yields unverifiable demand. This code can be used to expose contents of arbitrary pointers /// [SecurityCritical] internal byte[] ToArray() { byte[] b = new byte[_size]; unsafe { if (_pointer == null) throw new ArgumentOutOfRangeException(); Marshal.Copy((IntPtr)_pointer, b, 0, Size); } return b; } ////// Critical: This code exposes contents of arbitrary pointers. /// [SecurityCritical] internal void CopyTo(CheckedPointer dest) { unsafe { if (_pointer == null) throw new ArgumentOutOfRangeException(); byte* s = (byte*)_pointer; byte * d = (byte *)dest.Probe(0, _size); for (int i = 0; i < _size; ++i) { d[i] = s[i]; } } } // Returns the offset of the given pointer within this mapping, // with a bounds check. The returned offset may be equal to the size, // but not greater. Throws ArgumentOutOfRangeException if pointer // is not within the bounds of the mapping. ////// Critical: The method takes a pointer and performs pointer arithmetic. /// [SecurityCritical] internal unsafe int OffsetOf(void * pointer) { long offset = (byte*)pointer - (byte*)_pointer; if (offset < 0 || offset > _size || _pointer == null || pointer == null) throw new ArgumentOutOfRangeException(); return (int)offset;//no truncation possible since _size is an int } // Returns the offset of the given pointer within this mapping, // with a bounds check. The returned offset may be equal to the size, // but not greater. Throws ArgumentOutOfRangeException if pointer // is not within the bounds of the mapping. ////// Critical: The method contains unsafe code and calls the critical OffsetOf(void*) method. /// [SecurityCritical] internal int OffsetOf(CheckedPointer pointer) { unsafe { return OffsetOf(pointer._pointer); } } ////// Critical: This code yields unverifiable demand /// TreatAsSafe: This code simply overloads the + operator to increment pointer. /// It is safe because it is bounds checked to ensure it does not corrupt memory /// [SecurityCritical, SecurityTreatAsSafe] public static CheckedPointer operator+(CheckedPointer rhs, int offset) { // In future I'll just use checked context. That'll require modifying callers to expect integer overflow exceptions. unsafe { if (offset < 0 || offset > rhs._size || rhs._pointer == null) throw new ArgumentOutOfRangeException(); rhs._pointer = (byte*)rhs._pointer + offset; } rhs._size -= offset; return rhs; } ////// Critical: This code yields unverifiable demand and returns a pointer /// Although it is bounds checked the fact that it returns a pointer makes it risky. /// [SecurityCritical] internal unsafe void * Probe(int offset, int length) { if (_pointer == null || offset < 0 || offset > _size || offset + length > _size || offset + length < 0) throw new ArgumentOutOfRangeException(); return (byte *)_pointer + offset; } ////// Same as Probe, but returns a CheckedPointer instead of an unsafe pointer /// /// /// ////// /// Critical: This calls into unsafe code and also calls into checkedpointer which has a link demand /// TreatAsSafe: This code returns a checkedpointer and accessing the content is critical. Accessing is tracked in Probe /// [SecurityCritical,SecurityTreatAsSafe] internal CheckedPointer CheckedProbe(int offset, int length) { unsafe { if (_pointer == null || offset < 0 || offset > _size || offset + length > _size || offset + length < 0) throw new ArgumentOutOfRangeException(); return new CheckedPointer((byte*)_pointer + offset, length); } } ///Changes the buffer size of this CheckedPointer object. Used when memory block is resizable, ///like in a file mapping. ////// Critical: Passing an invalid size can calls future calls to Probe() to succeed even if the bounds of the buffer /// is being violated. This can cause the program to write to random memory. /// [SecurityCritical] internal void SetSize(int newSize) { _size = newSize; } ////// Critical: Contains unsafe code, which accesses _pointer field. /// TreatAsSafe: Only compares; does not expose pointer or do unsafe pointer operations. /// [SecurityCritical, SecurityTreatAsSafe] internal bool PointerEquals(CheckedPointer pointer) { unsafe { return _pointer == pointer._pointer; } } ////// Critical: Contains unsafe code. /// TreatAsSafe: Calls Probe and writes only the amount of data it probed for. /// [SecurityCritical, SecurityTreatAsSafe] internal void WriteBool(bool value) { unsafe { *(bool*)this.Probe(0, sizeof(bool)) = value; } } ////// Critical: Contains unsafe code. /// TreatAsSafe: Calls Probe and reads only the amount of data it probed for. /// [SecurityCritical, SecurityTreatAsSafe] internal bool ReadBool() { unsafe { return *(bool*)this.Probe(0, sizeof(bool)); } } ////// Critical: Holds reference to an unsafe pointer /// [SecurityCritical] private unsafe void * _pointer; ////// Critical: Having an invalid size can cause future calls to Probe() to succeed /// even if the bounds of the buffer is being violated. /// This can cause the program to write to random memory. /// This variable should really be SecurityCriticalDataForSet of int, /// but SecurityCriticalDataForSet is a generic defined in another assembly, /// and using it with a value type will cause JITting to happen. /// [SecurityCritical] private int _size; } ////// HashFn is a port of predefined hash functions from LKRHash /// [FriendAccessAllowed] internal static class HashFn { // Small prime number used as a multiplier in the supplied hash functions private const int HASH_MULTIPLIER = 101; internal static int HashMultiply(int hash) { return hash * HASH_MULTIPLIER; } ////// Distributes accumulated hash value across a range. /// Should be called just before returning hash code from a hash function. /// /// Hash value ///Scrambed hash value internal static int HashScramble(int hash) { // Here are 10 primes slightly greater than 10^9 // 1000000007, 1000000009, 1000000021, 1000000033, 1000000087, // 1000000093, 1000000097, 1000000103, 1000000123, 1000000181. // default value for "scrambling constant" const int RANDOM_CONSTANT = 314159269; // large prime number, also used for scrambling const uint RANDOM_PRIME = 1000000007; // we must cast to uint and back to int to correspond to current C++ behavior for operator% // since we have a matching hash function in native code uint a = (uint)(RANDOM_CONSTANT * hash); int b = (int)(a % RANDOM_PRIME); return b; } ////// Computes a hash code for a block of memory. /// One should not forget to call HashScramble before returning the final hash code value to the client. /// /// Pointer to a block of memory /// Size of the memory block in bytes /// Previous hash code to combine with ///Hash code ////// Critical - as this accesses unsafe code blocks and has the potential to /// corrupt memory. /// [SecurityCritical] internal unsafe static int HashMemory(void * pv, int numBytes, int hash) { byte * pb = (byte*)pv; while (numBytes-- > 0) { hash = HashMultiply(hash) + *pb; ++pb; } return hash; } internal static int HashString(string s, int hash) { foreach (char c in s) { hash = HashMultiply(hash) + (ushort)c; } return hash; } } internal unsafe struct HashTable { //Header for every font cache element in the hash table [StructLayout(LayoutKind.Explicit, Size = 8)] private struct ElementHeader { [FieldOffset(0)] internal int nextOffset;//offset of next element, relative to beginning of cache [FieldOffset(4)] internal int type; //type of current element //element data follows here }; //Probes a checked pointer for element header information and asserts //if the pointer is not aligned. ////// Critical - does unsafe pointer manipulation and returns a pointer /// [SecurityCritical] private static ElementHeader* ProbeElementHeader(CheckedPointer p,int offset) { void* res = p.Probe(offset, sizeof(ElementHeader)); Util.AssertAligned4(res); return (ElementHeader*)res; } ////// Retrieves information about the element stored at the given pointer. /// /// On input, *elementOffsetPtr contains the offset of an element in the cache /// or Util.nullOffset. /// If an element is referenced, elementOffsetPtr will be adjusted to point to the next element offset field of the element /// referenced. Otherwise, elementOffsetPtr will be unmodified. /// /// On output, pointer to the header of the referenced element, or null if none /// On output, CheckedPointer to data of referenced element, or null if none ///true if elementOffsetPtr references an element, false if not ////// Critical: takes and returns pointers. /// [SecurityCritical] private unsafe bool GetElementInfo(ref int* elementOffsetPtr, out ElementHeader* elementHeader, out CheckedPointer elementDataPtr) { int elementOffset = *elementOffsetPtr; if (elementOffset == Util.nullOffset) { //No element referenced elementHeader = null; elementDataPtr = new CheckedPointer(null,0); return false; } else { //Obtain the header CheckedPointer elementPtr = _cacher.Mapping + elementOffset; elementHeader = ProbeElementHeader(elementPtr,0); //Get the checked pointer for element data (implicit bounds check) elementDataPtr = elementPtr + sizeof(ElementHeader); //Update elementOffsetPtr elementOffsetPtr = &elementHeader->nextOffset; return true; } } ////// Adds a new element to the hash table. /// /// Pointer to a location that stores the offset of an element in the cache. /// This function modifies the contents of the pointer to add the element to the cache. /// /// Type of the new element to add /// Offset of the header of the new element, relative to the beginning of the cache ////// Critical: Takes a pointer as an argument. Will write to random memory if pointer is corrupt. /// [SecurityCritical] private unsafe void AddNewElement(int* elementOffsetPtr, int newElementType,int newElementOffset) { ElementHeader* newElementHeader = ProbeElementHeader(Mapping, newElementOffset); lock (_cacher.Lock) { //Update the new element first to keep the data in a consistent state //if another thread is iterating through it at the same time newElementHeader->nextOffset = *elementOffsetPtr; newElementHeader->type = newElementType; *elementOffsetPtr = newElementOffset; // flush the cache so that clients have a consistent view of it Thread.MemoryBarrier(); } } private const int sizeHashElement = sizeof(int); private int numberOfBuckets; private ElementCacher _cacher; private CheckedPointer Mapping { get { return _cacher.Mapping;}} private CheckedPointer Table { get { return _cacher.GetCheckedPointer(ElementCacher.offHashTable); } } ////// Returns a pointer to the offset of the first element in the cache of the given hash code. /// Modifying the contents of this pointer will alter the first element associated with the given /// hash code. If *GetHashEntry(hash) is Util.nullOffset then no element is associated with the given /// hash code. /// /// GetHashEntry() uses CheckedPointer.Probe() to ensure that *GetHashEntry(hash) is always /// a safe operation, although no guarentees are made about the contents of the data contained. /// ////// Critical: This code does unsafe pointer manipulation and returns a pointer /// [SecurityCritical] private int* GetHashEntry(int hash) { long offsetInTable = ((uint)hash % numberOfBuckets) * sizeHashElement; Invariant.Assert((offsetInTable >= 0) && (offsetInTable <= int.MaxValue)); int* offsetInCache = ((int*)(Table.Probe((int)offsetInTable,sizeof(int)))); return offsetInCache; } ////// Critical: This code resolves to unverifiable code and throws a demand. It takes an unverified pointer /// [SecurityCritical] internal HashTable(ElementCacher cacher, int numberOfBuckets) { _cacher = cacher; this.numberOfBuckets = numberOfBuckets; Invariant.Assert((numberOfBuckets != 0) && (!Mapping.IsNull)); } ////// Critical: This code resolves to unverifiable code and throws a demand /// TreatAsSafe: Initializes memory with null that is ok /// [SecurityCritical,SecurityTreatAsSafe] internal void Init() { int size = SizeOf; Util.FillMemory(Table.Probe(0,size), size, Util.nullOffset); } internal static int GetTableSize(int numberOfElements) { Debug.Assert(numberOfElements != 0); return numberOfElements * sizeHashElement; } internal int SizeOf { get { return GetTableSize(numberOfBuckets); } } ////// Critical: This code resolves to unverifiable code and throws a demand /// TreatAsSafe: This is safe to expose since it looks up an element in the hash table /// and in case where the boolean is true for add adds it to the cache. /// The risk is in the various function calls that deal with the pointers and /// the functions have been fortified to either be robust to pointers or there is /// checking in this code to ensure that invalid pointers do not go down the stack. /// [SecurityCritical,SecurityTreatAsSafe] internal bool Lookup(IFontCacheElement e, bool add) { int hash = e.GetHashCode(); int* elementOffsetPtr = GetHashEntry(hash); ElementHeader* header; CheckedPointer data; while(GetElementInfo(ref elementOffsetPtr, out header, out data)) { if (header->type == e.Type) { if (e.Match(data)) { e.GetData(data, _cacher); return true; } } } if (add) { // now, construct the new element int newOffset = _cacher.Alloc(e.Size + sizeof(ElementHeader)); CheckedPointer elem = _cacher.GetCheckedPointer(newOffset + sizeof(ElementHeader)); e.AddToCache(elem, _cacher); AddNewElement(elementOffsetPtr, e.Type, newOffset); } return false; } } ////// Utility functions for interaction with font cache service /// [FriendAccessAllowed] internal static class Util { internal const int nullOffset = -1; private static readonly string[] SupportedExtensions = new string[] { // .COMPOSITEFONT must remain the first entry in this array // because IsSupportedFontExtension relies on this. ".COMPOSITEFONT", ".OTF", ".TTC", ".TTF", ".TTE" }; private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars(); internal const UriComponents UriWithoutFragment = UriComponents.AbsoluteUri & ~UriComponents.Fragment; private const string WinDir = "windir"; private const string EmptyFontFamilyReference = "#"; private const string EmptyCanonicalName = ""; ////// Critical: This code elevates to get access to environment variable windir /// and exposes the local Windows fonts directory location. /// TreatAsSafe: This code uses critical fields to store critical data. /// [SecurityCritical, SecurityTreatAsSafe] static Util() { string s; //this code elevates to get access to the windir directory EnvironmentPermission environmentPermission = new EnvironmentPermission(EnvironmentPermissionAccess.Read, "Windir"); environmentPermission.Assert(); //Blessed Assert try { s = Environment.GetEnvironmentVariable(WinDir) + @"\Fonts\"; } finally { EnvironmentPermission.RevertAssert(); } _windowsFontsLocalPath = s.ToUpperInvariant(); _windowsFontsUriObject = new Uri(_windowsFontsLocalPath, UriKind.Absolute); // Characters that have reserved meanings (e.g., '%', '#', '/', etc.) remain escaped in the // string, but all others are not escaped. _windowsFontsUriString = _windowsFontsUriObject.GetComponents(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped); } ////// Windows fonts Uri string in an unescaped form optimized for Uri manipulations. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly string _windowsFontsLocalPath; ////// Critical: Exposes the local Windows fonts directory location. /// internal static string WindowsFontsLocalPath { [SecurityCritical] get { return _windowsFontsLocalPath; } } ////// Windows fonts Uri object. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly Uri _windowsFontsUriObject; ////// Critical: Exposes the local Windows fonts directory location. /// internal static Uri WindowsFontsUriObject { [SecurityCritical] get { return _windowsFontsUriObject; } } ////// Windows fonts Uri string in an unescaped form. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly string _windowsFontsUriString; ////// Critical: Exposes the local Windows fonts directory location. /// internal static string WindowsFontsUriString { [SecurityCritical] get { return _windowsFontsUriString; } } ////// Checks whether the specified location string or font family reference refers to the /// default Windows Fonts folder. /// ////// Returns true if the location part is empty or is a simple file name with no path /// characters (e.g., "#ARIAL" or "arial.ttf#ARIAL"). Returns false if the location /// is invalid or includes a path (e.g., "./#ARIAL", "..#ARIAL", "fonts/#ARIAL"). /// ////// This code is important for correcly interpreting font family references, but is NOT /// security critical. Even if the client somehow spoofs us, we never expose the resulting /// canonical family name and we do a security demand before accessing font data. /// internal static bool IsReferenceToWindowsFonts(string s) { // Empty location always refers to Windows Fonts. if (string.IsNullOrEmpty(s) || s[0] == '#') return true; // Get the length of the location part. int length = s.IndexOf('#'); if (length < 0) length = s.Length; // Check for invalid file name characters in the location. These include reserved path // characters ('/', '\\', ':'), wildcards ('?', '*'), control characters, etc. if (s.IndexOfAny(InvalidFileNameChars, 0, length) >= 0) return false; // A font family reference is a URI reference and may contain escaped characters. We need // to check their unescaped values to protect against cases like this: // 1. Create canonical name by concatenating "file:///c:/windows/Fonts/" + "foo%2fbar.ttf#Arial" // 2. Later we create a Uri from the canonical name and pass to FontSource class // 3. FontSource gets Uri.LocalPath which returns "c:\windows\Fonts\foo\bar.ttf" // 4. Doh! for (int i = s.IndexOf('%', 0, length); i >= 0; i = s.IndexOf('%', i, length - i)) { // Get the unescaped character; this always advances i by at least 1. char unescapedChar = Uri.HexUnescape(s, ref i); // Is it a reserved character? if (Array.IndexOf(InvalidFileNameChars, unescapedChar) >= 0) return false; } // Check for special names "." and ".." which represent directories. Also reject // variations like "...", ".. ", etc., as none of these are valid file names. if (s[0] == '.') { // Advance past one or more '.' int i = 1; while (i < length && s[i] == '.') ++i; // advance past trailing spaces, e.g., ".. #Arial" while (i < length && char.IsWhiteSpace(s[i])) ++i; // return false if the whole location is just repeated '. followed by spaces if (i == length) return false; } // If we fall through to here the location part has no reserved or illegal characters. // The "file name" could still be a reserved device name like CON, PRN, etc., but since // none of these will have a known font extension we won't try to open the device. return true; } internal static bool IsSupportedSchemeForAbsoluteFontFamilyUri(Uri absoluteUri) { // The only absolute URIs we allow in font family references are "file:" URIs. return absoluteUri.IsFile; } internal static void SplitFontFaceIndex(Uri fontUri, out Uri fontSourceUri, out int faceIndex) { // extract face index string fragment = fontUri.GetComponents(UriComponents.Fragment, UriFormat.SafeUnescaped); if (!String.IsNullOrEmpty(fragment)) { if (!int.TryParse( fragment, NumberStyles.None, CultureInfo.InvariantCulture, out faceIndex )) { throw new ArgumentException(SR.Get(SRID.FaceIndexMustBePositiveOrZero), "fontUri"); } // face index was specified in a fragment, we need to strip off fragment from the source Uri fontSourceUri = new Uri(fontUri.GetComponents(Util.UriWithoutFragment, UriFormat.SafeUnescaped)); } else { // simple case, no face index specified faceIndex = 0; fontSourceUri = fontUri; } } internal static Uri CombineUriWithFaceIndex(string fontUri, int faceIndex) { if (faceIndex == 0) return new Uri(fontUri); // the Uri roundtrip is necessary for escaping possible '#' symbols in the folder path, // so that they don't conflict with the fragment part. string canonicalPathUri = new Uri(fontUri).GetComponents(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped); string faceIndexString = faceIndex.ToString(CultureInfo.InvariantCulture); return new Uri(canonicalPathUri + '#' + faceIndexString); } internal static bool IsSupportedFontExtension(string extension, out bool isComposite) { for (int i = 0; i < SupportedExtensions.Length; ++i) { string supportedExtension = SupportedExtensions[i]; if (String.Compare(extension, supportedExtension, StringComparison.OrdinalIgnoreCase) == 0) { isComposite = (i == 0); // First array entry is *.CompositeFont return true; } } isComposite = false; return false; } internal static bool IsEnumerableFontUriScheme(Uri fontLocation) { bool isEnumerable = false; // We only support file:// and pack:// application Uris to reference logical fonts. if (fontLocation.IsAbsoluteUri) { if (fontLocation.IsFile) { // file scheme is always enumerable isEnumerable = true; } else if (fontLocation.Scheme == PackUriHelper.UriSchemePack) { // This is just an arbitrary file name which we use to construct a file URI. const string fakeFileName = "X"; // The fontLocation could be a folder-like URI even though the pack scheme does not allow this. // We simulate the concept of subfolders for packaged fonts. Before calling any PackUriHelper // methods, create a Uri which we know to be a file-like (rather than folder-like) URI. Uri fileUri; if (Uri.TryCreate(fontLocation, fakeFileName, out fileUri)) { isEnumerable = BaseUriHelper.IsPackApplicationUri(fileUri); } } } return isEnumerable; } internal static bool IsAppSpecificUri(Uri fontLocation) { // Only file:// Uris that refer to local drives can be cached across applications. // This function filters out some easy options, such as app specific pack:// Uris and // UNC paths. // Note that we make an assumption here that local drive letters stay the same across user sessions. // Also, we rely on session 0 not having access to any of the mapped drives. return !fontLocation.IsAbsoluteUri || !fontLocation.IsFile || fontLocation.IsUnc; } internal static string GetUriExtension(Uri uri) { string unescapedPath = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped); return Path.GetExtension(unescapedPath); } /// /// Converts the specified portion of a friendly name to a normalized font family reference. /// ////// Friendly names use commas as delimeters so any literal commas must be escaped by /// doubling. If any doubled commas are present in the specified substring they are unescaped /// in the normalized font family reference. /// internal static string GetNormalizedFontFamilyReference(string friendlyName, int startIndex, int length) { if (friendlyName.IndexOf(',', startIndex, length) < 0) { // We don't need to unescape any commas. return NormalizeFontFamilyReference(friendlyName, startIndex, length); } else { // Unescape commas and then convert to normalized form. return NormalizeFontFamilyReference(friendlyName.Substring(startIndex, length).Replace(",,", ",")); } } ////// Converts a font family reference to a normalized form. /// private static string NormalizeFontFamilyReference(string fontFamilyReference) { return NormalizeFontFamilyReference(fontFamilyReference, 0, fontFamilyReference.Length); } ////// Converts a font family reference to normalized form. /// ////// In normalized form, the fragment separator ('#') is always present, and the family /// name part (i.e., everything after the fragment separator) has been converted to /// upper case. However, the case of the location part (if any) is preserved. /// ////// "Arial" --> "#ARIAL" /// "fonts/#My Font" --> "fonts/#MY FONT" /// "/fonts/#My Font" --> "/fonts/#MY FONT" /// private static string NormalizeFontFamilyReference(string fontFamilyReference, int startIndex, int length) { if (length == 0) return EmptyFontFamilyReference; int fragmentIndex = fontFamilyReference.IndexOf('#', startIndex, length); if (fragmentIndex < 0) { // No fragment separator. The entire string is a family name so convert to uppercase // and add a fragment separator at the beginning. return "#" + fontFamilyReference.Substring(startIndex, length).ToUpperInvariant(); } else if (fragmentIndex + 1 == startIndex + length) { // No family name part. return EmptyFontFamilyReference; } else if (fragmentIndex == startIndex) { // Empty location part; convert the whole string to uppercase. return fontFamilyReference.Substring(startIndex, length).ToUpperInvariant(); } else { // Convert the fragment to uppercase, but preserve the case of the location. string location = fontFamilyReference.Substring(startIndex, fragmentIndex - startIndex); string fragment = fontFamilyReference.Substring(fragmentIndex, (startIndex + length) - fragmentIndex); return location + fragment.ToUpperInvariant(); } } ////// Converts a font family name and location to a font family reference. /// /// A font family name with no characters escaped /// An optional location ///Returns a font family reference, which may be either a URI reference or just a fragment /// (with or without the '#' prefix) internal static string ConvertFamilyNameAndLocationToFontFamilyReference(string familyName, string location) { // Escape reserved characters in the family name. In the fragment, we need only // worry about literal percent signs ('%') to avoid confusion with the escape prefix // and literal pound signs ('#') to avoid confusion with the fragment separator. string fontFamilyReference = familyName.Replace("%", "%25").Replace("#", "%23"); // Is there a location part? if (!string.IsNullOrEmpty(location)) { // We just escaped the family name and the location part should already be a valid URI reference. fontFamilyReference = string.Concat( location, "#", fontFamilyReference ); } return fontFamilyReference; } ////// Converts a font family reference to a friendly name by escaping any literal commas. /// /// A font family reference ///Returns a friendly name. ///Single commas delimit multiple font family references in a friendly name so any /// commas in the specified string are replaced with double commas in the return value. /// internal static string ConvertFontFamilyReferenceToFriendlyName(string fontFamilyReference) { return fontFamilyReference.Replace(",", ",,"); } ////// Critical: This code resolves to unverifiable code and throws a demand /// [SecurityCritical] internal unsafe static void FillMemory(void* pv, int size, int valueToFill) { AssertAligned4(pv); AssertAligned4(size); byte* pb = (byte*)pv; for (byte* p = pb; p < pb + size; p += 4) { *(int*)p = valueToFill; } } internal static int Align8(int i) { return (i + 7) & ~7; } internal static int Align4(int i) { return (i + 3) & ~3; } ////// Critical: This code resolves to unverifiable code and throws a demand /// [SecurityCritical] [Conditional("DEBUG")] internal unsafe static void AssertAligned4(void* p) { Debug.Assert(p != null); long i = ((IntPtr)p).ToInt64(); Debug.Assert((i % 4L) == 0); } ////// Critical: This code resolves to unverifiable code and throws a demand /// TreatAsSafe: This code is safe to expose /// [SecurityCritical, SecurityTreatAsSafe] [Conditional("DEBUG")] internal static void AssertAligned4(int i) { Debug.Assert((i % 4) == 0); } ////// Critical: This code resolves to unverifiable code and throws a demand /// TreatAsSafe: This code is ok to expose /// [SecurityCritical, SecurityTreatAsSafe] [Conditional("DEBUG")] internal static void AssertAligned8(int i) { Debug.Assert((i % 8) == 0); } ////// Critical: This code resolves to unverifiable code and throws a demand /// [SecurityCritical] [Conditional("DEBUG")] internal unsafe static void AssertAligned8(void* p) { Debug.Assert(p != null); long i = ((IntPtr)p).ToInt64(); Debug.Assert((i % 8L) == 0); } ////// Copies values from an array of ushort to a CheckedPointer and returns the number of bytes /// copied. Other overloads can be added as needed; unfortunately we can't use generics for this. /// ////// Critical: This code does unsafe pointer manipulation /// TreatAsSafe: Probe checks for pointer validity and the creation of checked pointer /// is tracked /// [SecurityCritical,SecurityTreatAsSafe] internal static int ArrayCopyToCheckedPointer(CheckedPointer p, ushort[] array, int startIndex, int length) { if (startIndex < 0 || startIndex > array.Length) throw new ArgumentOutOfRangeException("startIndex"); if (length < 0 || length > array.Length - startIndex) throw new ArgumentOutOfRangeException("length"); int sizeInBytes = length * sizeof(ushort); unsafe { ushort* dst = (ushort*)p.Probe(0, sizeInBytes); for (int i = startIndex, end = startIndex + length; i < end; ++i) { *dst++ = array[i]; } Invariant.Assert(p.OffsetOf(dst) == sizeInBytes); } return sizeInBytes; } ////// Copies a string into a memory block /// /// Destination memory block /// Source string ////// Critical: This code does unsafe pointer manipulation and dereferences p /// [SecurityCritical] internal unsafe static void StringCopyToUncheckedPointer(void* p, string s) { char* d = (char*)p; for (int i = 0; i < s.Length; ++i) { d[i] = s[i]; } } ////// Critical: This code does unsafe pointer manipulation /// TreatAsSafe: Probe checks for pointer validity and the creation of checked pointer /// is tracked /// [SecurityCritical, SecurityTreatAsSafe] internal static void StringCopyToCheckedPointer(CheckedPointer p, string s) { unsafe { StringCopyToUncheckedPointer(p.Probe(0, s.Length * sizeof(char)), s); } } ////// Critical: This code does unsafe pointer manipulation /// [SecurityCritical] internal unsafe static string StringCopyFromUncheckedPointer(void* p, int sizeInBytes) { Invariant.Assert(sizeInBytes % sizeof(char) == 0); if (sizeInBytes == 0) return string.Empty; else return new string((char*)p, 0, sizeInBytes / sizeof(char)); } ////// Critical: This code does unsafe pointer manipulation /// TreatAsSafe: This uses checkedpointer which is carefully tracked and boundary checked /// [SecurityCritical, SecurityTreatAsSafe] internal static string StringCopyFromCheckedPointer(CheckedPointer p, int sizeInBytes) { unsafe { return StringCopyFromUncheckedPointer(p.Probe(0, sizeInBytes), sizeInBytes); } } ////// Copies a string and its length to a checked pointer. /// /// CheckedPointer to copy the string to. It will contain integer string length in the first four bytes. /// Input string. ///The number of bytes written to the CheckedPointer. ////// Critical: This code does unsafe pointer manipulation. /// TreatAsSafe: This uses checkedpointer which is carefully tracked and boundary checked /// [SecurityCritical, SecurityTreatAsSafe] internal static int StringAndLengthCopyToCheckedPointer(CheckedPointer p, string s) { int realSize = sizeof(int) + Util.StringSize(s); unsafe { int* l = (int*)p.Probe(0, realSize); *l = Util.StringSize(s); ++l; Util.StringCopyToUncheckedPointer(l, s); } return realSize; } /// CheckedPointer to copy the string from. It contains integer string length in the first four bytes. ///String to store the result in. ////// Critical: This code does unsafe pointer manipulation. /// TreatAsSafe: This uses checkedpointer which is carefully tracked and boundary checked /// [SecurityCritical, SecurityTreatAsSafe] internal static string StringAndLengthCopyFromCheckedPointer(CheckedPointer p) { unsafe { int* length = (int*)p.Probe(0, sizeof(int)); return StringCopyFromCheckedPointer(p + sizeof(int), *length); } } ////// Critical: This code does unsafe pointer manipulations. /// TreatAsSafe: The pointer manipulations are checked. /// [SecurityCritical, SecurityTreatAsSafe] internal static bool StringEqual(CheckedPointer p, string s) { // Don't throw if p points to a memory block smaller than s. // This simply means that the strings are not equal. if (p.Size < StringSize(s)) return false; unsafe { char* chars = (char*)p.Probe(0, StringSize(s)); for (int i = 0; i < s.Length; ++i) { if (s[i] != chars[i]) return false; } return true; } } ////// Critical: This code does unsafe pointer manipulations. /// TreatAsSafe: The pointer manipulations are checked. /// [SecurityCritical, SecurityTreatAsSafe] internal static bool StringEqualIgnoreCase(CheckedPointer p, string s) { // Don't throw if p points to a memory block smaller than s. // This simply means that the strings are not equal. if (p.Size < StringSize(s)) return false; unsafe { char* chars = (char*)p.Probe(0, StringSize(s)); for (int i = 0; i < s.Length; ++i) { char dst = Char.ToUpperInvariant(chars[i]); char src = Char.ToUpperInvariant(s[i]); if (dst != src) return false; } return true; } } ////// Critical: This code calls sizeof(char). /// TreatAsSafe: Calling sizeof(char) is obviously a safe operation. /// [SecurityCritical, SecurityTreatAsSafe] internal static int StringSize(string s) { unsafe { return s.Length * sizeof(char); } } internal static void AddMemoryPressure(long pressure) { MemoryPressure.Add(pressure); } internal static void RemoveMemoryPressure(long pressure) { MemoryPressure.Remove(pressure); } ////// Compares string using character ordinals. /// The comparison is case insensitive based on InvariantCulture. /// We have our own custom wrapper because we need to sort using the same algorithm /// we use in incremental charater search. /// There are subtle things (e.g. surrogates) that String.Compare() does and we don't. /// /// First input string. /// Second input string. ///Same semantics as for String.Compare internal static int CompareOrdinalIgnoreCase(string a, string b) { int aLength = a.Length; int bLength = b.Length; int minLength = Math.Min(aLength, bLength); for (int i = 0; i < minLength; ++i) { int result = CompareOrdinalIgnoreCase(a[i], b[i]); if (result != 0) return result; } return aLength - bLength; } private static int CompareOrdinalIgnoreCase(char a, char b) { char ca = Char.ToUpperInvariant(a); char cb = Char.ToUpperInvariant(b); return ca - cb; } // Makes sure the caller has path discovery permission for full fileName path. private static void ValidateFileNamePermissions(ref string fileName) { if (!SecurityHelper.CallerHasPathDiscoveryPermission(fileName)) { // If the caller didn't have path discovery permission for fileName, we can still give out relative file name. fileName = Path.GetFileName(fileName); } } ////// This function performs job similar to CLR's internal __Error.WinIOError function: /// it maps win32 errors from file I/O to CLR exceptions and includes string where possible. /// However, we're interested only in errors when opening a file for reading. /// /// Win32 error code. /// File name string. ////// Critical - As this function throws exception containing full file name, which can result in Information Disclosure. /// TreatAsSafe - As the function performs permission demand. /// [SecurityCritical, SecurityTreatAsSafe] internal static void ThrowWin32Exception(int errorCode, string fileName) { ValidateFileNamePermissions(ref fileName); switch (errorCode) { case NativeMethods.ERROR_FILE_NOT_FOUND: throw new FileNotFoundException(SR.Get(SRID.FileNotFoundExceptionWithFileName, fileName), fileName); case NativeMethods.ERROR_PATH_NOT_FOUND: throw new DirectoryNotFoundException(SR.Get(SRID.DirectoryNotFoundExceptionWithFileName, fileName)); case NativeMethods.ERROR_ACCESS_DENIED: throw new UnauthorizedAccessException(SR.Get(SRID.UnauthorizedAccessExceptionWithFileName, fileName)); case NativeMethods.ERROR_FILENAME_EXCED_RANGE: throw new PathTooLongException(SR.Get(SRID.PathTooLongExceptionWithFileName, fileName)); default: throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName), NativeMethods.MakeHRFromErrorCode(errorCode)); } } ////// Critical - As this function accesses font Uri that contains absolute font path. /// Safe - As we use ValidateFileNamePermissions to strip off the local path part for file Uris. /// [SecurityCritical, SecurityTreatAsSafe] internal static Exception ConvertInPageException(FontSource fontSource, SEHException e) { string fileName; if (fontSource.Uri.IsFile) { fileName = fontSource.Uri.LocalPath; ValidateFileNamePermissions(ref fileName); } else { fileName = fontSource.GetUriString(); } return new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName), e); } } ////// A class that wraps operations with Win32 memory sections and file mappings /// ////// This class could potentially contain critical information for the case /// where the data pointed to by the Mapping is obtained under an elevation. /// We consder FileMapping class itself to be security agnostic. When /// someone creates this instance from critical data, they'll need to mark /// the instance as SecurityCritical and track usage. For example if a call to /// OpenFile is made on an instance of FileMapping, that instance will be /// SecurityCritical. /// [FriendAccessAllowed] internal class FileMapping : UnmanagedMemoryStream { ~FileMapping() { Dispose(false); } ////// Critical: This method acceses critical elements _viewHandle and _mappingHandle /// TreatAsSafe: This data is not exposed and calling dispose is ok /// [SecurityCritical, SecurityTreatAsSafe] protected override void Dispose(bool disposing) { base.Dispose(disposing); if (!_disposed) { if (disposing) { if (_viewHandle != null) _viewHandle.Dispose(); if (_mappingHandle != null) _mappingHandle.Dispose(); } if (CanWrite) { long currentSize = AlignToPage(Length); if (currentSize != 0) Util.RemoveMemoryPressure(currentSize); } } _disposed = true; } ////// Creates a named memory section. May throw OutOfMemoryException. /// /// Memory section name /// The maximum reserve size of the section ////// Critical - as this functions calls MapViewOfFileEx, CreateFileMapping and Initialize /// which cause an elevation. /// TreatAsSafe - as this does a demand when name is not null. When name is null, /// it's just a chunk of memory it opens. /// [SecurityCritical, SecurityTreatAsSafe] internal void Create(string name, long maxSize) { maxSize = AlignToPage(maxSize); if (name != null) { // This code only gets called by font cache service which runs in full trust. // When name is not null, this function should be protected because we could // be exposing any named object on the system for read. SecurityHelper.DemandUnmanagedCode(); } NativeMethods.SECURITY_ATTRIBUTES sa = new NativeMethods.SECURITY_ATTRIBUTES(); using (sa) { if (name != null) { string securityDescriptor = "D:" + // DACL "(D;;GA;;;NU)" + // Deny Network All "(A;;GR;;;WD)"; // Allow World Read if (!UnsafeNativeMethods.ConvertStringSecurityDescriptorToSecurityDescriptor( securityDescriptor, UnsafeNativeMethods.SDDL_REVISION_1, ref sa.lpSecurityDescriptor, IntPtr.Zero)) { throw new System.ComponentModel.Win32Exception(); } } UnsafeNativeMethods.ULARGE_INTEGER nSize = new UnsafeNativeMethods.ULARGE_INTEGER(); nSize.QuadPart = (ulong)maxSize; unsafe { // Disable PREsharp warning about not calling Marshal.GetLastWin32Error, // because we already check the handle for invalid value and // we are not particularly interested in specific Win32 error. #pragma warning disable 6523 _mappingHandle = UnsafeNativeMethods.CreateFileMapping( new SafeFileHandle(UnsafeNativeMethods.INVALID_HANDLE_VALUE, false), sa, NativeMethods.PAGE_READWRITE | UnsafeNativeMethods.SEC_RESERVE, nSize.HighPart, nSize.LowPart, name); if (_mappingHandle.IsInvalid) throw new OutOfMemoryException(); _viewHandle = UnsafeNativeMethods.MapViewOfFileEx(_mappingHandle, UnsafeNativeMethods.FILE_MAP_ALL_ACCESS, 0, 0, IntPtr.Zero, IntPtr.Zero); if (_viewHandle.IsInvalid) throw new OutOfMemoryException(); #pragma warning disable 6523 // Initialize() method demands UnmanagedCode permission, and Create() is already marked as critical. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); //Blessed Assert try { Initialize((byte*)_viewHandle.Memory, 0, maxSize, FileAccess.ReadWrite); } finally { SecurityPermission.RevertAssert(); } } } } ////// Opens a named memory section. Caller should be prepared to catch IOException. /// /// The name of the section. ////// Critical - because the code does an elevation to open a named file mapping. /// [SecurityCritical] internal void OpenSection(string name) { // Disable PREsharp warning about not calling Marshal.GetLastWin32Error, // because we already check the handle for invalid value and // we are not particularly interested in specific Win32 error. #pragma warning disable 6523 _mappingHandle = UnsafeNativeMethods.OpenFileMapping(UnsafeNativeMethods.FILE_MAP_READ, false, name); if (_mappingHandle.IsInvalid) throw new IOException(); _viewHandle = UnsafeNativeMethods.MapViewOfFileEx(_mappingHandle, UnsafeNativeMethods.FILE_MAP_READ, 0, 0, IntPtr.Zero, IntPtr.Zero); if (_viewHandle.IsInvalid) throw new IOException(); #pragma warning restore 6523 // Initialize() method demands UnmanagedCode permission, and OpenSection() is already marked as critical. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); //Blessed Assert try { unsafe { Initialize((byte*)_viewHandle.Memory, 0, 0, FileAccess.Read); } } finally { SecurityPermission.RevertAssert(); } } ////// Critical - As this function calls functions CreateFileMapping, /// MapViewOfFileEx and Initialize under elevation. /// Any instance of FileMapping /// object on which this function is called becomes a critical /// instance and its usage will need to be tracked for audit. /// [SecurityCritical] internal void OpenFile(string fileName) { NativeMethods.SECURITY_ATTRIBUTES sa = new NativeMethods.SECURITY_ATTRIBUTES(); using (sa) { unsafe { // Disable PREsharp warning about not calling Marshal.GetLastWin32Error, // because we already check the handle for invalid value and // we are not particularly interested in specific Win32 error. #pragma warning disable 6523 long size; using (SafeFileHandle fileHandle = UnsafeNativeMethods.CreateFile( fileName, NativeMethods.GENERIC_READ, NativeMethods.FILE_SHARE_READ, null, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero )) { if (fileHandle.IsInvalid) { Util.ThrowWin32Exception(Marshal.GetLastWin32Error(), fileName); } UnsafeNativeMethods.LARGE_INTEGER fileSize = new UnsafeNativeMethods.LARGE_INTEGER(); if (!UnsafeNativeMethods.GetFileSizeEx(fileHandle, ref fileSize)) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); size = (long)fileSize.QuadPart; if (size == 0) throw new FileFormatException(new Uri(fileName)); _mappingHandle = UnsafeNativeMethods.CreateFileMapping( fileHandle, sa, UnsafeNativeMethods.PAGE_READONLY, 0, 0, null); } if (_mappingHandle.IsInvalid) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); _viewHandle = UnsafeNativeMethods.MapViewOfFileEx(_mappingHandle, UnsafeNativeMethods.FILE_MAP_READ, 0, 0, IntPtr.Zero, IntPtr.Zero); if (_viewHandle.IsInvalid) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); #pragma warning restore 6523 // Initialize() method demands UnmanagedCode permission, and OpenFile() is already marked as critical. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); //Blessed Assert try { Initialize((byte*)_viewHandle.Memory, size, size, FileAccess.Read); } finally { SecurityPermission.RevertAssert(); } } } } internal static long AlignToPage(long size) { return (size + (PageSize - 1)) & ~(PageSize - 1); } ////// Critical - As this function calls a native method VirtualAlloc under elevation. /// TreatAsSafe - as DOS for allocation of memory can happen in many different ways, e.g /// the app can commit lots of memory by just creating a huge number of objects. /// [SecurityCritical, SecurityTreatAsSafe] internal void Commit(long size) { Debug.Assert(size > Length); Debug.Assert(CanWrite); long wantedSize = AlignToPage(size); long currentSize = AlignToPage(Length); // No need to call VirtualAlloc if the page is already committed. if (wantedSize <= currentSize) { SetLength(size); return; } // this assert would fire in case of a bug in client's code Invariant.Assert(wantedSize <= Capacity); unsafe { // Disable PREsharp warning about not calling Marshal.GetLastWin32Error, // because we already check the base address for nullness and // we are not particularly interested in specific Win32 error. #pragma warning disable 6523 IntPtr baseAddress = UnsafeNativeMethods.VirtualAlloc((IntPtr)_viewHandle.Memory, (UIntPtr)wantedSize, UnsafeNativeMethods.MEM_COMMIT, UnsafeNativeMethods.PAGE_READWRITE); if (baseAddress == IntPtr.Zero) throw new OutOfMemoryException(); #pragma warning restore 6523 } SetLength(size); Util.AddMemoryPressure(wantedSize - currentSize); } ////// Critical: This element holds reference to an object retrieved under an elevation /// [SecurityCritical] private UnsafeNativeMethods.SafeViewOfFileHandle _viewHandle; ////// Critical: This element holds reference to an object retrieved under an elevation /// [SecurityCritical] private UnsafeNativeMethods.SafeFileMappingHandle _mappingHandle; private bool _disposed = false; ////// The number of bytes to preallocate on Commit(). /// It's equal to 2 x86 pages or 1 IA64 page. /// NOTE: AlignToPage relies on this being a power of 2. /// private const int PageSize = 4096 * 2; } internal class LocalizedName { internal LocalizedName(XmlLanguage language, string name) : this(language, name, language.GetEquivalentCulture().LCID) {} internal LocalizedName(XmlLanguage language, string name, int originalLCID) { _language = language; _name = name; _originalLCID = originalLCID; } internal XmlLanguage Language { get { return _language; } } internal string Name { get { return _name; } } internal int OriginalLCID { get { return _originalLCID; } } internal static IComparerNameComparer { get { return _nameComparer; } } internal static IComparer LanguageComparer { get { return _languageComparer; } } private class NameComparerClass : IComparer { #region IComparer Members int IComparer .Compare(LocalizedName x, LocalizedName y) { return Util.CompareOrdinalIgnoreCase(x._name, y._name); } #endregion } private class LanguageComparerClass : IComparer { #region IComparer Members int IComparer .Compare(LocalizedName x, LocalizedName y) { return String.Compare(x._language.IetfLanguageTag, y._language.IetfLanguageTag, StringComparison.OrdinalIgnoreCase); } #endregion } private XmlLanguage _language; // the language identifier private string _name; // name converted to Unicode private int _originalLCID; // original LCID, used in cases when we want to preserve it to avoid information loss private static NameComparerClass _nameComparer = new NameComparerClass(); private static LanguageComparerClass _languageComparer = new LanguageComparerClass(); } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // Description: Miscellaneous utility functions for font handling code. // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Packaging; using System.Reflection; using System.Resources; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using System.Windows; using System.Windows.Markup; // for XmlLanguage using System.Windows.Media; using System.Windows.Navigation; using System.Windows.Threading; using MS.Win32; using MS.Internal; using MS.Internal.FontFace; using MS.Internal.PresentationCore; using MS.Internal.Resources; using MS.Utility; using Microsoft.Win32.SafeHandles; // Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 namespace MS.Internal.FontCache { /// /// CheckedPointer is used to reference a fixed size memory block. /// The purpose of the class is to protect the memory block from overruns. /// ArgumentOutOfRangeException is thrown when an overrun is detected. /// ////// This class could potentially contain critical information for the case /// where the data pointed to by "pointer" parameter is obtained under an /// elevation. We consder CheckedPointer class itself to be security Agnostic. /// When someone creates this instance from critical data, they'll need to /// mark the instance as SecurityCritical and track usage. /// [FriendAccessAllowed] internal struct CheckedPointer { ////// Critical: This code yields unverifiable demand /// [SecurityCritical] internal unsafe CheckedPointer(void * pointer, int size) { _pointer = pointer; _size = size; } ////// Critical: This calls into PositionPointer which is link demand protected /// also it constructs data for a checked pointer. /// [SecurityCritical] internal CheckedPointer(UnmanagedMemoryStream stream) { Debug.Assert(stream.Position == 0); unsafe { _pointer = stream.PositionPointer; } long length = stream.Length; if (length < 0 || length > int.MaxValue) throw new ArgumentOutOfRangeException(); _size = (int)length; } ////// Critical: Call into unsafe code block /// TreatAsSafe: This is ok to call /// internal bool IsNull { [SecurityCritical,SecurityTreatAsSafe] get { unsafe { return (_pointer == null); } } } ////// Critical: _size is marked as critical. /// TreatAsSafe: _size is critical only for set. /// internal int Size { [SecurityCritical, SecurityTreatAsSafe] get { return _size; } } ////// Critical: This code yields unverifiable demand. This code can be used to expose contents of arbitrary pointers /// [SecurityCritical] internal byte[] ToArray() { byte[] b = new byte[_size]; unsafe { if (_pointer == null) throw new ArgumentOutOfRangeException(); Marshal.Copy((IntPtr)_pointer, b, 0, Size); } return b; } ////// Critical: This code exposes contents of arbitrary pointers. /// [SecurityCritical] internal void CopyTo(CheckedPointer dest) { unsafe { if (_pointer == null) throw new ArgumentOutOfRangeException(); byte* s = (byte*)_pointer; byte * d = (byte *)dest.Probe(0, _size); for (int i = 0; i < _size; ++i) { d[i] = s[i]; } } } // Returns the offset of the given pointer within this mapping, // with a bounds check. The returned offset may be equal to the size, // but not greater. Throws ArgumentOutOfRangeException if pointer // is not within the bounds of the mapping. ////// Critical: The method takes a pointer and performs pointer arithmetic. /// [SecurityCritical] internal unsafe int OffsetOf(void * pointer) { long offset = (byte*)pointer - (byte*)_pointer; if (offset < 0 || offset > _size || _pointer == null || pointer == null) throw new ArgumentOutOfRangeException(); return (int)offset;//no truncation possible since _size is an int } // Returns the offset of the given pointer within this mapping, // with a bounds check. The returned offset may be equal to the size, // but not greater. Throws ArgumentOutOfRangeException if pointer // is not within the bounds of the mapping. ////// Critical: The method contains unsafe code and calls the critical OffsetOf(void*) method. /// [SecurityCritical] internal int OffsetOf(CheckedPointer pointer) { unsafe { return OffsetOf(pointer._pointer); } } ////// Critical: This code yields unverifiable demand /// TreatAsSafe: This code simply overloads the + operator to increment pointer. /// It is safe because it is bounds checked to ensure it does not corrupt memory /// [SecurityCritical, SecurityTreatAsSafe] public static CheckedPointer operator+(CheckedPointer rhs, int offset) { // In future I'll just use checked context. That'll require modifying callers to expect integer overflow exceptions. unsafe { if (offset < 0 || offset > rhs._size || rhs._pointer == null) throw new ArgumentOutOfRangeException(); rhs._pointer = (byte*)rhs._pointer + offset; } rhs._size -= offset; return rhs; } ////// Critical: This code yields unverifiable demand and returns a pointer /// Although it is bounds checked the fact that it returns a pointer makes it risky. /// [SecurityCritical] internal unsafe void * Probe(int offset, int length) { if (_pointer == null || offset < 0 || offset > _size || offset + length > _size || offset + length < 0) throw new ArgumentOutOfRangeException(); return (byte *)_pointer + offset; } ////// Same as Probe, but returns a CheckedPointer instead of an unsafe pointer /// /// /// ////// /// Critical: This calls into unsafe code and also calls into checkedpointer which has a link demand /// TreatAsSafe: This code returns a checkedpointer and accessing the content is critical. Accessing is tracked in Probe /// [SecurityCritical,SecurityTreatAsSafe] internal CheckedPointer CheckedProbe(int offset, int length) { unsafe { if (_pointer == null || offset < 0 || offset > _size || offset + length > _size || offset + length < 0) throw new ArgumentOutOfRangeException(); return new CheckedPointer((byte*)_pointer + offset, length); } } ///Changes the buffer size of this CheckedPointer object. Used when memory block is resizable, ///like in a file mapping. ////// Critical: Passing an invalid size can calls future calls to Probe() to succeed even if the bounds of the buffer /// is being violated. This can cause the program to write to random memory. /// [SecurityCritical] internal void SetSize(int newSize) { _size = newSize; } ////// Critical: Contains unsafe code, which accesses _pointer field. /// TreatAsSafe: Only compares; does not expose pointer or do unsafe pointer operations. /// [SecurityCritical, SecurityTreatAsSafe] internal bool PointerEquals(CheckedPointer pointer) { unsafe { return _pointer == pointer._pointer; } } ////// Critical: Contains unsafe code. /// TreatAsSafe: Calls Probe and writes only the amount of data it probed for. /// [SecurityCritical, SecurityTreatAsSafe] internal void WriteBool(bool value) { unsafe { *(bool*)this.Probe(0, sizeof(bool)) = value; } } ////// Critical: Contains unsafe code. /// TreatAsSafe: Calls Probe and reads only the amount of data it probed for. /// [SecurityCritical, SecurityTreatAsSafe] internal bool ReadBool() { unsafe { return *(bool*)this.Probe(0, sizeof(bool)); } } ////// Critical: Holds reference to an unsafe pointer /// [SecurityCritical] private unsafe void * _pointer; ////// Critical: Having an invalid size can cause future calls to Probe() to succeed /// even if the bounds of the buffer is being violated. /// This can cause the program to write to random memory. /// This variable should really be SecurityCriticalDataForSet of int, /// but SecurityCriticalDataForSet is a generic defined in another assembly, /// and using it with a value type will cause JITting to happen. /// [SecurityCritical] private int _size; } ////// HashFn is a port of predefined hash functions from LKRHash /// [FriendAccessAllowed] internal static class HashFn { // Small prime number used as a multiplier in the supplied hash functions private const int HASH_MULTIPLIER = 101; internal static int HashMultiply(int hash) { return hash * HASH_MULTIPLIER; } ////// Distributes accumulated hash value across a range. /// Should be called just before returning hash code from a hash function. /// /// Hash value ///Scrambed hash value internal static int HashScramble(int hash) { // Here are 10 primes slightly greater than 10^9 // 1000000007, 1000000009, 1000000021, 1000000033, 1000000087, // 1000000093, 1000000097, 1000000103, 1000000123, 1000000181. // default value for "scrambling constant" const int RANDOM_CONSTANT = 314159269; // large prime number, also used for scrambling const uint RANDOM_PRIME = 1000000007; // we must cast to uint and back to int to correspond to current C++ behavior for operator% // since we have a matching hash function in native code uint a = (uint)(RANDOM_CONSTANT * hash); int b = (int)(a % RANDOM_PRIME); return b; } ////// Computes a hash code for a block of memory. /// One should not forget to call HashScramble before returning the final hash code value to the client. /// /// Pointer to a block of memory /// Size of the memory block in bytes /// Previous hash code to combine with ///Hash code ////// Critical - as this accesses unsafe code blocks and has the potential to /// corrupt memory. /// [SecurityCritical] internal unsafe static int HashMemory(void * pv, int numBytes, int hash) { byte * pb = (byte*)pv; while (numBytes-- > 0) { hash = HashMultiply(hash) + *pb; ++pb; } return hash; } internal static int HashString(string s, int hash) { foreach (char c in s) { hash = HashMultiply(hash) + (ushort)c; } return hash; } } internal unsafe struct HashTable { //Header for every font cache element in the hash table [StructLayout(LayoutKind.Explicit, Size = 8)] private struct ElementHeader { [FieldOffset(0)] internal int nextOffset;//offset of next element, relative to beginning of cache [FieldOffset(4)] internal int type; //type of current element //element data follows here }; //Probes a checked pointer for element header information and asserts //if the pointer is not aligned. ////// Critical - does unsafe pointer manipulation and returns a pointer /// [SecurityCritical] private static ElementHeader* ProbeElementHeader(CheckedPointer p,int offset) { void* res = p.Probe(offset, sizeof(ElementHeader)); Util.AssertAligned4(res); return (ElementHeader*)res; } ////// Retrieves information about the element stored at the given pointer. /// /// On input, *elementOffsetPtr contains the offset of an element in the cache /// or Util.nullOffset. /// If an element is referenced, elementOffsetPtr will be adjusted to point to the next element offset field of the element /// referenced. Otherwise, elementOffsetPtr will be unmodified. /// /// On output, pointer to the header of the referenced element, or null if none /// On output, CheckedPointer to data of referenced element, or null if none ///true if elementOffsetPtr references an element, false if not ////// Critical: takes and returns pointers. /// [SecurityCritical] private unsafe bool GetElementInfo(ref int* elementOffsetPtr, out ElementHeader* elementHeader, out CheckedPointer elementDataPtr) { int elementOffset = *elementOffsetPtr; if (elementOffset == Util.nullOffset) { //No element referenced elementHeader = null; elementDataPtr = new CheckedPointer(null,0); return false; } else { //Obtain the header CheckedPointer elementPtr = _cacher.Mapping + elementOffset; elementHeader = ProbeElementHeader(elementPtr,0); //Get the checked pointer for element data (implicit bounds check) elementDataPtr = elementPtr + sizeof(ElementHeader); //Update elementOffsetPtr elementOffsetPtr = &elementHeader->nextOffset; return true; } } ////// Adds a new element to the hash table. /// /// Pointer to a location that stores the offset of an element in the cache. /// This function modifies the contents of the pointer to add the element to the cache. /// /// Type of the new element to add /// Offset of the header of the new element, relative to the beginning of the cache ////// Critical: Takes a pointer as an argument. Will write to random memory if pointer is corrupt. /// [SecurityCritical] private unsafe void AddNewElement(int* elementOffsetPtr, int newElementType,int newElementOffset) { ElementHeader* newElementHeader = ProbeElementHeader(Mapping, newElementOffset); lock (_cacher.Lock) { //Update the new element first to keep the data in a consistent state //if another thread is iterating through it at the same time newElementHeader->nextOffset = *elementOffsetPtr; newElementHeader->type = newElementType; *elementOffsetPtr = newElementOffset; // flush the cache so that clients have a consistent view of it Thread.MemoryBarrier(); } } private const int sizeHashElement = sizeof(int); private int numberOfBuckets; private ElementCacher _cacher; private CheckedPointer Mapping { get { return _cacher.Mapping;}} private CheckedPointer Table { get { return _cacher.GetCheckedPointer(ElementCacher.offHashTable); } } ////// Returns a pointer to the offset of the first element in the cache of the given hash code. /// Modifying the contents of this pointer will alter the first element associated with the given /// hash code. If *GetHashEntry(hash) is Util.nullOffset then no element is associated with the given /// hash code. /// /// GetHashEntry() uses CheckedPointer.Probe() to ensure that *GetHashEntry(hash) is always /// a safe operation, although no guarentees are made about the contents of the data contained. /// ////// Critical: This code does unsafe pointer manipulation and returns a pointer /// [SecurityCritical] private int* GetHashEntry(int hash) { long offsetInTable = ((uint)hash % numberOfBuckets) * sizeHashElement; Invariant.Assert((offsetInTable >= 0) && (offsetInTable <= int.MaxValue)); int* offsetInCache = ((int*)(Table.Probe((int)offsetInTable,sizeof(int)))); return offsetInCache; } ////// Critical: This code resolves to unverifiable code and throws a demand. It takes an unverified pointer /// [SecurityCritical] internal HashTable(ElementCacher cacher, int numberOfBuckets) { _cacher = cacher; this.numberOfBuckets = numberOfBuckets; Invariant.Assert((numberOfBuckets != 0) && (!Mapping.IsNull)); } ////// Critical: This code resolves to unverifiable code and throws a demand /// TreatAsSafe: Initializes memory with null that is ok /// [SecurityCritical,SecurityTreatAsSafe] internal void Init() { int size = SizeOf; Util.FillMemory(Table.Probe(0,size), size, Util.nullOffset); } internal static int GetTableSize(int numberOfElements) { Debug.Assert(numberOfElements != 0); return numberOfElements * sizeHashElement; } internal int SizeOf { get { return GetTableSize(numberOfBuckets); } } ////// Critical: This code resolves to unverifiable code and throws a demand /// TreatAsSafe: This is safe to expose since it looks up an element in the hash table /// and in case where the boolean is true for add adds it to the cache. /// The risk is in the various function calls that deal with the pointers and /// the functions have been fortified to either be robust to pointers or there is /// checking in this code to ensure that invalid pointers do not go down the stack. /// [SecurityCritical,SecurityTreatAsSafe] internal bool Lookup(IFontCacheElement e, bool add) { int hash = e.GetHashCode(); int* elementOffsetPtr = GetHashEntry(hash); ElementHeader* header; CheckedPointer data; while(GetElementInfo(ref elementOffsetPtr, out header, out data)) { if (header->type == e.Type) { if (e.Match(data)) { e.GetData(data, _cacher); return true; } } } if (add) { // now, construct the new element int newOffset = _cacher.Alloc(e.Size + sizeof(ElementHeader)); CheckedPointer elem = _cacher.GetCheckedPointer(newOffset + sizeof(ElementHeader)); e.AddToCache(elem, _cacher); AddNewElement(elementOffsetPtr, e.Type, newOffset); } return false; } } ////// Utility functions for interaction with font cache service /// [FriendAccessAllowed] internal static class Util { internal const int nullOffset = -1; private static readonly string[] SupportedExtensions = new string[] { // .COMPOSITEFONT must remain the first entry in this array // because IsSupportedFontExtension relies on this. ".COMPOSITEFONT", ".OTF", ".TTC", ".TTF", ".TTE" }; private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars(); internal const UriComponents UriWithoutFragment = UriComponents.AbsoluteUri & ~UriComponents.Fragment; private const string WinDir = "windir"; private const string EmptyFontFamilyReference = "#"; private const string EmptyCanonicalName = ""; ////// Critical: This code elevates to get access to environment variable windir /// and exposes the local Windows fonts directory location. /// TreatAsSafe: This code uses critical fields to store critical data. /// [SecurityCritical, SecurityTreatAsSafe] static Util() { string s; //this code elevates to get access to the windir directory EnvironmentPermission environmentPermission = new EnvironmentPermission(EnvironmentPermissionAccess.Read, "Windir"); environmentPermission.Assert(); //Blessed Assert try { s = Environment.GetEnvironmentVariable(WinDir) + @"\Fonts\"; } finally { EnvironmentPermission.RevertAssert(); } _windowsFontsLocalPath = s.ToUpperInvariant(); _windowsFontsUriObject = new Uri(_windowsFontsLocalPath, UriKind.Absolute); // Characters that have reserved meanings (e.g., '%', '#', '/', etc.) remain escaped in the // string, but all others are not escaped. _windowsFontsUriString = _windowsFontsUriObject.GetComponents(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped); } ////// Windows fonts Uri string in an unescaped form optimized for Uri manipulations. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly string _windowsFontsLocalPath; ////// Critical: Exposes the local Windows fonts directory location. /// internal static string WindowsFontsLocalPath { [SecurityCritical] get { return _windowsFontsLocalPath; } } ////// Windows fonts Uri object. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly Uri _windowsFontsUriObject; ////// Critical: Exposes the local Windows fonts directory location. /// internal static Uri WindowsFontsUriObject { [SecurityCritical] get { return _windowsFontsUriObject; } } ////// Windows fonts Uri string in an unescaped form. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly string _windowsFontsUriString; ////// Critical: Exposes the local Windows fonts directory location. /// internal static string WindowsFontsUriString { [SecurityCritical] get { return _windowsFontsUriString; } } ////// Checks whether the specified location string or font family reference refers to the /// default Windows Fonts folder. /// ////// Returns true if the location part is empty or is a simple file name with no path /// characters (e.g., "#ARIAL" or "arial.ttf#ARIAL"). Returns false if the location /// is invalid or includes a path (e.g., "./#ARIAL", "..#ARIAL", "fonts/#ARIAL"). /// ////// This code is important for correcly interpreting font family references, but is NOT /// security critical. Even if the client somehow spoofs us, we never expose the resulting /// canonical family name and we do a security demand before accessing font data. /// internal static bool IsReferenceToWindowsFonts(string s) { // Empty location always refers to Windows Fonts. if (string.IsNullOrEmpty(s) || s[0] == '#') return true; // Get the length of the location part. int length = s.IndexOf('#'); if (length < 0) length = s.Length; // Check for invalid file name characters in the location. These include reserved path // characters ('/', '\\', ':'), wildcards ('?', '*'), control characters, etc. if (s.IndexOfAny(InvalidFileNameChars, 0, length) >= 0) return false; // A font family reference is a URI reference and may contain escaped characters. We need // to check their unescaped values to protect against cases like this: // 1. Create canonical name by concatenating "file:///c:/windows/Fonts/" + "foo%2fbar.ttf#Arial" // 2. Later we create a Uri from the canonical name and pass to FontSource class // 3. FontSource gets Uri.LocalPath which returns "c:\windows\Fonts\foo\bar.ttf" // 4. Doh! for (int i = s.IndexOf('%', 0, length); i >= 0; i = s.IndexOf('%', i, length - i)) { // Get the unescaped character; this always advances i by at least 1. char unescapedChar = Uri.HexUnescape(s, ref i); // Is it a reserved character? if (Array.IndexOf(InvalidFileNameChars, unescapedChar) >= 0) return false; } // Check for special names "." and ".." which represent directories. Also reject // variations like "...", ".. ", etc., as none of these are valid file names. if (s[0] == '.') { // Advance past one or more '.' int i = 1; while (i < length && s[i] == '.') ++i; // advance past trailing spaces, e.g., ".. #Arial" while (i < length && char.IsWhiteSpace(s[i])) ++i; // return false if the whole location is just repeated '. followed by spaces if (i == length) return false; } // If we fall through to here the location part has no reserved or illegal characters. // The "file name" could still be a reserved device name like CON, PRN, etc., but since // none of these will have a known font extension we won't try to open the device. return true; } internal static bool IsSupportedSchemeForAbsoluteFontFamilyUri(Uri absoluteUri) { // The only absolute URIs we allow in font family references are "file:" URIs. return absoluteUri.IsFile; } internal static void SplitFontFaceIndex(Uri fontUri, out Uri fontSourceUri, out int faceIndex) { // extract face index string fragment = fontUri.GetComponents(UriComponents.Fragment, UriFormat.SafeUnescaped); if (!String.IsNullOrEmpty(fragment)) { if (!int.TryParse( fragment, NumberStyles.None, CultureInfo.InvariantCulture, out faceIndex )) { throw new ArgumentException(SR.Get(SRID.FaceIndexMustBePositiveOrZero), "fontUri"); } // face index was specified in a fragment, we need to strip off fragment from the source Uri fontSourceUri = new Uri(fontUri.GetComponents(Util.UriWithoutFragment, UriFormat.SafeUnescaped)); } else { // simple case, no face index specified faceIndex = 0; fontSourceUri = fontUri; } } internal static Uri CombineUriWithFaceIndex(string fontUri, int faceIndex) { if (faceIndex == 0) return new Uri(fontUri); // the Uri roundtrip is necessary for escaping possible '#' symbols in the folder path, // so that they don't conflict with the fragment part. string canonicalPathUri = new Uri(fontUri).GetComponents(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped); string faceIndexString = faceIndex.ToString(CultureInfo.InvariantCulture); return new Uri(canonicalPathUri + '#' + faceIndexString); } internal static bool IsSupportedFontExtension(string extension, out bool isComposite) { for (int i = 0; i < SupportedExtensions.Length; ++i) { string supportedExtension = SupportedExtensions[i]; if (String.Compare(extension, supportedExtension, StringComparison.OrdinalIgnoreCase) == 0) { isComposite = (i == 0); // First array entry is *.CompositeFont return true; } } isComposite = false; return false; } internal static bool IsEnumerableFontUriScheme(Uri fontLocation) { bool isEnumerable = false; // We only support file:// and pack:// application Uris to reference logical fonts. if (fontLocation.IsAbsoluteUri) { if (fontLocation.IsFile) { // file scheme is always enumerable isEnumerable = true; } else if (fontLocation.Scheme == PackUriHelper.UriSchemePack) { // This is just an arbitrary file name which we use to construct a file URI. const string fakeFileName = "X"; // The fontLocation could be a folder-like URI even though the pack scheme does not allow this. // We simulate the concept of subfolders for packaged fonts. Before calling any PackUriHelper // methods, create a Uri which we know to be a file-like (rather than folder-like) URI. Uri fileUri; if (Uri.TryCreate(fontLocation, fakeFileName, out fileUri)) { isEnumerable = BaseUriHelper.IsPackApplicationUri(fileUri); } } } return isEnumerable; } internal static bool IsAppSpecificUri(Uri fontLocation) { // Only file:// Uris that refer to local drives can be cached across applications. // This function filters out some easy options, such as app specific pack:// Uris and // UNC paths. // Note that we make an assumption here that local drive letters stay the same across user sessions. // Also, we rely on session 0 not having access to any of the mapped drives. return !fontLocation.IsAbsoluteUri || !fontLocation.IsFile || fontLocation.IsUnc; } internal static string GetUriExtension(Uri uri) { string unescapedPath = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped); return Path.GetExtension(unescapedPath); } /// /// Converts the specified portion of a friendly name to a normalized font family reference. /// ////// Friendly names use commas as delimeters so any literal commas must be escaped by /// doubling. If any doubled commas are present in the specified substring they are unescaped /// in the normalized font family reference. /// internal static string GetNormalizedFontFamilyReference(string friendlyName, int startIndex, int length) { if (friendlyName.IndexOf(',', startIndex, length) < 0) { // We don't need to unescape any commas. return NormalizeFontFamilyReference(friendlyName, startIndex, length); } else { // Unescape commas and then convert to normalized form. return NormalizeFontFamilyReference(friendlyName.Substring(startIndex, length).Replace(",,", ",")); } } ////// Converts a font family reference to a normalized form. /// private static string NormalizeFontFamilyReference(string fontFamilyReference) { return NormalizeFontFamilyReference(fontFamilyReference, 0, fontFamilyReference.Length); } ////// Converts a font family reference to normalized form. /// ////// In normalized form, the fragment separator ('#') is always present, and the family /// name part (i.e., everything after the fragment separator) has been converted to /// upper case. However, the case of the location part (if any) is preserved. /// ////// "Arial" --> "#ARIAL" /// "fonts/#My Font" --> "fonts/#MY FONT" /// "/fonts/#My Font" --> "/fonts/#MY FONT" /// private static string NormalizeFontFamilyReference(string fontFamilyReference, int startIndex, int length) { if (length == 0) return EmptyFontFamilyReference; int fragmentIndex = fontFamilyReference.IndexOf('#', startIndex, length); if (fragmentIndex < 0) { // No fragment separator. The entire string is a family name so convert to uppercase // and add a fragment separator at the beginning. return "#" + fontFamilyReference.Substring(startIndex, length).ToUpperInvariant(); } else if (fragmentIndex + 1 == startIndex + length) { // No family name part. return EmptyFontFamilyReference; } else if (fragmentIndex == startIndex) { // Empty location part; convert the whole string to uppercase. return fontFamilyReference.Substring(startIndex, length).ToUpperInvariant(); } else { // Convert the fragment to uppercase, but preserve the case of the location. string location = fontFamilyReference.Substring(startIndex, fragmentIndex - startIndex); string fragment = fontFamilyReference.Substring(fragmentIndex, (startIndex + length) - fragmentIndex); return location + fragment.ToUpperInvariant(); } } ////// Converts a font family name and location to a font family reference. /// /// A font family name with no characters escaped /// An optional location ///Returns a font family reference, which may be either a URI reference or just a fragment /// (with or without the '#' prefix) internal static string ConvertFamilyNameAndLocationToFontFamilyReference(string familyName, string location) { // Escape reserved characters in the family name. In the fragment, we need only // worry about literal percent signs ('%') to avoid confusion with the escape prefix // and literal pound signs ('#') to avoid confusion with the fragment separator. string fontFamilyReference = familyName.Replace("%", "%25").Replace("#", "%23"); // Is there a location part? if (!string.IsNullOrEmpty(location)) { // We just escaped the family name and the location part should already be a valid URI reference. fontFamilyReference = string.Concat( location, "#", fontFamilyReference ); } return fontFamilyReference; } ////// Converts a font family reference to a friendly name by escaping any literal commas. /// /// A font family reference ///Returns a friendly name. ///Single commas delimit multiple font family references in a friendly name so any /// commas in the specified string are replaced with double commas in the return value. /// internal static string ConvertFontFamilyReferenceToFriendlyName(string fontFamilyReference) { return fontFamilyReference.Replace(",", ",,"); } ////// Critical: This code resolves to unverifiable code and throws a demand /// [SecurityCritical] internal unsafe static void FillMemory(void* pv, int size, int valueToFill) { AssertAligned4(pv); AssertAligned4(size); byte* pb = (byte*)pv; for (byte* p = pb; p < pb + size; p += 4) { *(int*)p = valueToFill; } } internal static int Align8(int i) { return (i + 7) & ~7; } internal static int Align4(int i) { return (i + 3) & ~3; } ////// Critical: This code resolves to unverifiable code and throws a demand /// [SecurityCritical] [Conditional("DEBUG")] internal unsafe static void AssertAligned4(void* p) { Debug.Assert(p != null); long i = ((IntPtr)p).ToInt64(); Debug.Assert((i % 4L) == 0); } ////// Critical: This code resolves to unverifiable code and throws a demand /// TreatAsSafe: This code is safe to expose /// [SecurityCritical, SecurityTreatAsSafe] [Conditional("DEBUG")] internal static void AssertAligned4(int i) { Debug.Assert((i % 4) == 0); } ////// Critical: This code resolves to unverifiable code and throws a demand /// TreatAsSafe: This code is ok to expose /// [SecurityCritical, SecurityTreatAsSafe] [Conditional("DEBUG")] internal static void AssertAligned8(int i) { Debug.Assert((i % 8) == 0); } ////// Critical: This code resolves to unverifiable code and throws a demand /// [SecurityCritical] [Conditional("DEBUG")] internal unsafe static void AssertAligned8(void* p) { Debug.Assert(p != null); long i = ((IntPtr)p).ToInt64(); Debug.Assert((i % 8L) == 0); } ////// Copies values from an array of ushort to a CheckedPointer and returns the number of bytes /// copied. Other overloads can be added as needed; unfortunately we can't use generics for this. /// ////// Critical: This code does unsafe pointer manipulation /// TreatAsSafe: Probe checks for pointer validity and the creation of checked pointer /// is tracked /// [SecurityCritical,SecurityTreatAsSafe] internal static int ArrayCopyToCheckedPointer(CheckedPointer p, ushort[] array, int startIndex, int length) { if (startIndex < 0 || startIndex > array.Length) throw new ArgumentOutOfRangeException("startIndex"); if (length < 0 || length > array.Length - startIndex) throw new ArgumentOutOfRangeException("length"); int sizeInBytes = length * sizeof(ushort); unsafe { ushort* dst = (ushort*)p.Probe(0, sizeInBytes); for (int i = startIndex, end = startIndex + length; i < end; ++i) { *dst++ = array[i]; } Invariant.Assert(p.OffsetOf(dst) == sizeInBytes); } return sizeInBytes; } ////// Copies a string into a memory block /// /// Destination memory block /// Source string ////// Critical: This code does unsafe pointer manipulation and dereferences p /// [SecurityCritical] internal unsafe static void StringCopyToUncheckedPointer(void* p, string s) { char* d = (char*)p; for (int i = 0; i < s.Length; ++i) { d[i] = s[i]; } } ////// Critical: This code does unsafe pointer manipulation /// TreatAsSafe: Probe checks for pointer validity and the creation of checked pointer /// is tracked /// [SecurityCritical, SecurityTreatAsSafe] internal static void StringCopyToCheckedPointer(CheckedPointer p, string s) { unsafe { StringCopyToUncheckedPointer(p.Probe(0, s.Length * sizeof(char)), s); } } ////// Critical: This code does unsafe pointer manipulation /// [SecurityCritical] internal unsafe static string StringCopyFromUncheckedPointer(void* p, int sizeInBytes) { Invariant.Assert(sizeInBytes % sizeof(char) == 0); if (sizeInBytes == 0) return string.Empty; else return new string((char*)p, 0, sizeInBytes / sizeof(char)); } ////// Critical: This code does unsafe pointer manipulation /// TreatAsSafe: This uses checkedpointer which is carefully tracked and boundary checked /// [SecurityCritical, SecurityTreatAsSafe] internal static string StringCopyFromCheckedPointer(CheckedPointer p, int sizeInBytes) { unsafe { return StringCopyFromUncheckedPointer(p.Probe(0, sizeInBytes), sizeInBytes); } } ////// Copies a string and its length to a checked pointer. /// /// CheckedPointer to copy the string to. It will contain integer string length in the first four bytes. /// Input string. ///The number of bytes written to the CheckedPointer. ////// Critical: This code does unsafe pointer manipulation. /// TreatAsSafe: This uses checkedpointer which is carefully tracked and boundary checked /// [SecurityCritical, SecurityTreatAsSafe] internal static int StringAndLengthCopyToCheckedPointer(CheckedPointer p, string s) { int realSize = sizeof(int) + Util.StringSize(s); unsafe { int* l = (int*)p.Probe(0, realSize); *l = Util.StringSize(s); ++l; Util.StringCopyToUncheckedPointer(l, s); } return realSize; } /// CheckedPointer to copy the string from. It contains integer string length in the first four bytes. ///String to store the result in. ////// Critical: This code does unsafe pointer manipulation. /// TreatAsSafe: This uses checkedpointer which is carefully tracked and boundary checked /// [SecurityCritical, SecurityTreatAsSafe] internal static string StringAndLengthCopyFromCheckedPointer(CheckedPointer p) { unsafe { int* length = (int*)p.Probe(0, sizeof(int)); return StringCopyFromCheckedPointer(p + sizeof(int), *length); } } ////// Critical: This code does unsafe pointer manipulations. /// TreatAsSafe: The pointer manipulations are checked. /// [SecurityCritical, SecurityTreatAsSafe] internal static bool StringEqual(CheckedPointer p, string s) { // Don't throw if p points to a memory block smaller than s. // This simply means that the strings are not equal. if (p.Size < StringSize(s)) return false; unsafe { char* chars = (char*)p.Probe(0, StringSize(s)); for (int i = 0; i < s.Length; ++i) { if (s[i] != chars[i]) return false; } return true; } } ////// Critical: This code does unsafe pointer manipulations. /// TreatAsSafe: The pointer manipulations are checked. /// [SecurityCritical, SecurityTreatAsSafe] internal static bool StringEqualIgnoreCase(CheckedPointer p, string s) { // Don't throw if p points to a memory block smaller than s. // This simply means that the strings are not equal. if (p.Size < StringSize(s)) return false; unsafe { char* chars = (char*)p.Probe(0, StringSize(s)); for (int i = 0; i < s.Length; ++i) { char dst = Char.ToUpperInvariant(chars[i]); char src = Char.ToUpperInvariant(s[i]); if (dst != src) return false; } return true; } } ////// Critical: This code calls sizeof(char). /// TreatAsSafe: Calling sizeof(char) is obviously a safe operation. /// [SecurityCritical, SecurityTreatAsSafe] internal static int StringSize(string s) { unsafe { return s.Length * sizeof(char); } } internal static void AddMemoryPressure(long pressure) { MemoryPressure.Add(pressure); } internal static void RemoveMemoryPressure(long pressure) { MemoryPressure.Remove(pressure); } ////// Compares string using character ordinals. /// The comparison is case insensitive based on InvariantCulture. /// We have our own custom wrapper because we need to sort using the same algorithm /// we use in incremental charater search. /// There are subtle things (e.g. surrogates) that String.Compare() does and we don't. /// /// First input string. /// Second input string. ///Same semantics as for String.Compare internal static int CompareOrdinalIgnoreCase(string a, string b) { int aLength = a.Length; int bLength = b.Length; int minLength = Math.Min(aLength, bLength); for (int i = 0; i < minLength; ++i) { int result = CompareOrdinalIgnoreCase(a[i], b[i]); if (result != 0) return result; } return aLength - bLength; } private static int CompareOrdinalIgnoreCase(char a, char b) { char ca = Char.ToUpperInvariant(a); char cb = Char.ToUpperInvariant(b); return ca - cb; } // Makes sure the caller has path discovery permission for full fileName path. private static void ValidateFileNamePermissions(ref string fileName) { if (!SecurityHelper.CallerHasPathDiscoveryPermission(fileName)) { // If the caller didn't have path discovery permission for fileName, we can still give out relative file name. fileName = Path.GetFileName(fileName); } } ////// This function performs job similar to CLR's internal __Error.WinIOError function: /// it maps win32 errors from file I/O to CLR exceptions and includes string where possible. /// However, we're interested only in errors when opening a file for reading. /// /// Win32 error code. /// File name string. ////// Critical - As this function throws exception containing full file name, which can result in Information Disclosure. /// TreatAsSafe - As the function performs permission demand. /// [SecurityCritical, SecurityTreatAsSafe] internal static void ThrowWin32Exception(int errorCode, string fileName) { ValidateFileNamePermissions(ref fileName); switch (errorCode) { case NativeMethods.ERROR_FILE_NOT_FOUND: throw new FileNotFoundException(SR.Get(SRID.FileNotFoundExceptionWithFileName, fileName), fileName); case NativeMethods.ERROR_PATH_NOT_FOUND: throw new DirectoryNotFoundException(SR.Get(SRID.DirectoryNotFoundExceptionWithFileName, fileName)); case NativeMethods.ERROR_ACCESS_DENIED: throw new UnauthorizedAccessException(SR.Get(SRID.UnauthorizedAccessExceptionWithFileName, fileName)); case NativeMethods.ERROR_FILENAME_EXCED_RANGE: throw new PathTooLongException(SR.Get(SRID.PathTooLongExceptionWithFileName, fileName)); default: throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName), NativeMethods.MakeHRFromErrorCode(errorCode)); } } ////// Critical - As this function accesses font Uri that contains absolute font path. /// Safe - As we use ValidateFileNamePermissions to strip off the local path part for file Uris. /// [SecurityCritical, SecurityTreatAsSafe] internal static Exception ConvertInPageException(FontSource fontSource, SEHException e) { string fileName; if (fontSource.Uri.IsFile) { fileName = fontSource.Uri.LocalPath; ValidateFileNamePermissions(ref fileName); } else { fileName = fontSource.GetUriString(); } return new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName), e); } } ////// A class that wraps operations with Win32 memory sections and file mappings /// ////// This class could potentially contain critical information for the case /// where the data pointed to by the Mapping is obtained under an elevation. /// We consder FileMapping class itself to be security agnostic. When /// someone creates this instance from critical data, they'll need to mark /// the instance as SecurityCritical and track usage. For example if a call to /// OpenFile is made on an instance of FileMapping, that instance will be /// SecurityCritical. /// [FriendAccessAllowed] internal class FileMapping : UnmanagedMemoryStream { ~FileMapping() { Dispose(false); } ////// Critical: This method acceses critical elements _viewHandle and _mappingHandle /// TreatAsSafe: This data is not exposed and calling dispose is ok /// [SecurityCritical, SecurityTreatAsSafe] protected override void Dispose(bool disposing) { base.Dispose(disposing); if (!_disposed) { if (disposing) { if (_viewHandle != null) _viewHandle.Dispose(); if (_mappingHandle != null) _mappingHandle.Dispose(); } if (CanWrite) { long currentSize = AlignToPage(Length); if (currentSize != 0) Util.RemoveMemoryPressure(currentSize); } } _disposed = true; } ////// Creates a named memory section. May throw OutOfMemoryException. /// /// Memory section name /// The maximum reserve size of the section ////// Critical - as this functions calls MapViewOfFileEx, CreateFileMapping and Initialize /// which cause an elevation. /// TreatAsSafe - as this does a demand when name is not null. When name is null, /// it's just a chunk of memory it opens. /// [SecurityCritical, SecurityTreatAsSafe] internal void Create(string name, long maxSize) { maxSize = AlignToPage(maxSize); if (name != null) { // This code only gets called by font cache service which runs in full trust. // When name is not null, this function should be protected because we could // be exposing any named object on the system for read. SecurityHelper.DemandUnmanagedCode(); } NativeMethods.SECURITY_ATTRIBUTES sa = new NativeMethods.SECURITY_ATTRIBUTES(); using (sa) { if (name != null) { string securityDescriptor = "D:" + // DACL "(D;;GA;;;NU)" + // Deny Network All "(A;;GR;;;WD)"; // Allow World Read if (!UnsafeNativeMethods.ConvertStringSecurityDescriptorToSecurityDescriptor( securityDescriptor, UnsafeNativeMethods.SDDL_REVISION_1, ref sa.lpSecurityDescriptor, IntPtr.Zero)) { throw new System.ComponentModel.Win32Exception(); } } UnsafeNativeMethods.ULARGE_INTEGER nSize = new UnsafeNativeMethods.ULARGE_INTEGER(); nSize.QuadPart = (ulong)maxSize; unsafe { // Disable PREsharp warning about not calling Marshal.GetLastWin32Error, // because we already check the handle for invalid value and // we are not particularly interested in specific Win32 error. #pragma warning disable 6523 _mappingHandle = UnsafeNativeMethods.CreateFileMapping( new SafeFileHandle(UnsafeNativeMethods.INVALID_HANDLE_VALUE, false), sa, NativeMethods.PAGE_READWRITE | UnsafeNativeMethods.SEC_RESERVE, nSize.HighPart, nSize.LowPart, name); if (_mappingHandle.IsInvalid) throw new OutOfMemoryException(); _viewHandle = UnsafeNativeMethods.MapViewOfFileEx(_mappingHandle, UnsafeNativeMethods.FILE_MAP_ALL_ACCESS, 0, 0, IntPtr.Zero, IntPtr.Zero); if (_viewHandle.IsInvalid) throw new OutOfMemoryException(); #pragma warning disable 6523 // Initialize() method demands UnmanagedCode permission, and Create() is already marked as critical. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); //Blessed Assert try { Initialize((byte*)_viewHandle.Memory, 0, maxSize, FileAccess.ReadWrite); } finally { SecurityPermission.RevertAssert(); } } } } ////// Opens a named memory section. Caller should be prepared to catch IOException. /// /// The name of the section. ////// Critical - because the code does an elevation to open a named file mapping. /// [SecurityCritical] internal void OpenSection(string name) { // Disable PREsharp warning about not calling Marshal.GetLastWin32Error, // because we already check the handle for invalid value and // we are not particularly interested in specific Win32 error. #pragma warning disable 6523 _mappingHandle = UnsafeNativeMethods.OpenFileMapping(UnsafeNativeMethods.FILE_MAP_READ, false, name); if (_mappingHandle.IsInvalid) throw new IOException(); _viewHandle = UnsafeNativeMethods.MapViewOfFileEx(_mappingHandle, UnsafeNativeMethods.FILE_MAP_READ, 0, 0, IntPtr.Zero, IntPtr.Zero); if (_viewHandle.IsInvalid) throw new IOException(); #pragma warning restore 6523 // Initialize() method demands UnmanagedCode permission, and OpenSection() is already marked as critical. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); //Blessed Assert try { unsafe { Initialize((byte*)_viewHandle.Memory, 0, 0, FileAccess.Read); } } finally { SecurityPermission.RevertAssert(); } } ////// Critical - As this function calls functions CreateFileMapping, /// MapViewOfFileEx and Initialize under elevation. /// Any instance of FileMapping /// object on which this function is called becomes a critical /// instance and its usage will need to be tracked for audit. /// [SecurityCritical] internal void OpenFile(string fileName) { NativeMethods.SECURITY_ATTRIBUTES sa = new NativeMethods.SECURITY_ATTRIBUTES(); using (sa) { unsafe { // Disable PREsharp warning about not calling Marshal.GetLastWin32Error, // because we already check the handle for invalid value and // we are not particularly interested in specific Win32 error. #pragma warning disable 6523 long size; using (SafeFileHandle fileHandle = UnsafeNativeMethods.CreateFile( fileName, NativeMethods.GENERIC_READ, NativeMethods.FILE_SHARE_READ, null, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero )) { if (fileHandle.IsInvalid) { Util.ThrowWin32Exception(Marshal.GetLastWin32Error(), fileName); } UnsafeNativeMethods.LARGE_INTEGER fileSize = new UnsafeNativeMethods.LARGE_INTEGER(); if (!UnsafeNativeMethods.GetFileSizeEx(fileHandle, ref fileSize)) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); size = (long)fileSize.QuadPart; if (size == 0) throw new FileFormatException(new Uri(fileName)); _mappingHandle = UnsafeNativeMethods.CreateFileMapping( fileHandle, sa, UnsafeNativeMethods.PAGE_READONLY, 0, 0, null); } if (_mappingHandle.IsInvalid) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); _viewHandle = UnsafeNativeMethods.MapViewOfFileEx(_mappingHandle, UnsafeNativeMethods.FILE_MAP_READ, 0, 0, IntPtr.Zero, IntPtr.Zero); if (_viewHandle.IsInvalid) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); #pragma warning restore 6523 // Initialize() method demands UnmanagedCode permission, and OpenFile() is already marked as critical. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); //Blessed Assert try { Initialize((byte*)_viewHandle.Memory, size, size, FileAccess.Read); } finally { SecurityPermission.RevertAssert(); } } } } internal static long AlignToPage(long size) { return (size + (PageSize - 1)) & ~(PageSize - 1); } ////// Critical - As this function calls a native method VirtualAlloc under elevation. /// TreatAsSafe - as DOS for allocation of memory can happen in many different ways, e.g /// the app can commit lots of memory by just creating a huge number of objects. /// [SecurityCritical, SecurityTreatAsSafe] internal void Commit(long size) { Debug.Assert(size > Length); Debug.Assert(CanWrite); long wantedSize = AlignToPage(size); long currentSize = AlignToPage(Length); // No need to call VirtualAlloc if the page is already committed. if (wantedSize <= currentSize) { SetLength(size); return; } // this assert would fire in case of a bug in client's code Invariant.Assert(wantedSize <= Capacity); unsafe { // Disable PREsharp warning about not calling Marshal.GetLastWin32Error, // because we already check the base address for nullness and // we are not particularly interested in specific Win32 error. #pragma warning disable 6523 IntPtr baseAddress = UnsafeNativeMethods.VirtualAlloc((IntPtr)_viewHandle.Memory, (UIntPtr)wantedSize, UnsafeNativeMethods.MEM_COMMIT, UnsafeNativeMethods.PAGE_READWRITE); if (baseAddress == IntPtr.Zero) throw new OutOfMemoryException(); #pragma warning restore 6523 } SetLength(size); Util.AddMemoryPressure(wantedSize - currentSize); } ////// Critical: This element holds reference to an object retrieved under an elevation /// [SecurityCritical] private UnsafeNativeMethods.SafeViewOfFileHandle _viewHandle; ////// Critical: This element holds reference to an object retrieved under an elevation /// [SecurityCritical] private UnsafeNativeMethods.SafeFileMappingHandle _mappingHandle; private bool _disposed = false; ////// The number of bytes to preallocate on Commit(). /// It's equal to 2 x86 pages or 1 IA64 page. /// NOTE: AlignToPage relies on this being a power of 2. /// private const int PageSize = 4096 * 2; } internal class LocalizedName { internal LocalizedName(XmlLanguage language, string name) : this(language, name, language.GetEquivalentCulture().LCID) {} internal LocalizedName(XmlLanguage language, string name, int originalLCID) { _language = language; _name = name; _originalLCID = originalLCID; } internal XmlLanguage Language { get { return _language; } } internal string Name { get { return _name; } } internal int OriginalLCID { get { return _originalLCID; } } internal static IComparerNameComparer { get { return _nameComparer; } } internal static IComparer LanguageComparer { get { return _languageComparer; } } private class NameComparerClass : IComparer { #region IComparer Members int IComparer .Compare(LocalizedName x, LocalizedName y) { return Util.CompareOrdinalIgnoreCase(x._name, y._name); } #endregion } private class LanguageComparerClass : IComparer { #region IComparer Members int IComparer .Compare(LocalizedName x, LocalizedName y) { return String.Compare(x._language.IetfLanguageTag, y._language.IetfLanguageTag, StringComparison.OrdinalIgnoreCase); } #endregion } private XmlLanguage _language; // the language identifier private string _name; // name converted to Unicode private int _originalLCID; // original LCID, used in cases when we want to preserve it to avoid information loss private static NameComparerClass _nameComparer = new NameComparerClass(); private static LanguageComparerClass _languageComparer = new LanguageComparerClass(); } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- PeerService.cs
- QueryTreeBuilder.cs
- UriTemplateDispatchFormatter.cs
- XmlAttributes.cs
- Single.cs
- AssertFilter.cs
- FigureParagraph.cs
- KeyboardDevice.cs
- ProxyWebPartConnectionCollection.cs
- StylusPointProperties.cs
- RenderData.cs
- Barrier.cs
- ScriptingWebServicesSectionGroup.cs
- ProfilePropertyNameValidator.cs
- TextTreeInsertUndoUnit.cs
- TypeRefElement.cs
- DocumentXmlWriter.cs
- ToggleButton.cs
- WebPartConnectionsEventArgs.cs
- DataGridDefaultColumnWidthTypeConverter.cs
- WebPartConnectionsCloseVerb.cs
- ListSortDescriptionCollection.cs
- ErrorTableItemStyle.cs
- SQLString.cs
- CheckedListBox.cs
- XmlWrappingReader.cs
- PropertyCondition.cs
- Configuration.cs
- IPAddressCollection.cs
- OdbcHandle.cs
- SqlException.cs
- UnsafeMethods.cs
- TdsParameterSetter.cs
- DrawListViewSubItemEventArgs.cs
- TemplateBindingExpressionConverter.cs
- XmlChildNodes.cs
- SqlTopReducer.cs
- DataGridTableCollection.cs
- BitmapCodecInfoInternal.cs
- RepeatInfo.cs
- PropertyItemInternal.cs
- Item.cs
- RequestBringIntoViewEventArgs.cs
- BasePattern.cs
- ContainerUIElement3D.cs
- TrackingWorkflowEventArgs.cs
- TypeResolver.cs
- PathFigure.cs
- IntSecurity.cs
- MimeReflector.cs
- StrokeNodeOperations.cs
- SplineKeyFrames.cs
- FloaterBaseParaClient.cs
- FileController.cs
- EventTrigger.cs
- PrivilegeNotHeldException.cs
- FormViewDeleteEventArgs.cs
- X509Certificate.cs
- WebPartDisplayModeCancelEventArgs.cs
- ProcessModule.cs
- HttpClientCertificate.cs
- GifBitmapEncoder.cs
- ProcessHostMapPath.cs
- TextBoxRenderer.cs
- InputScope.cs
- BmpBitmapDecoder.cs
- CommandArguments.cs
- ImageBrush.cs
- HtmlShimManager.cs
- ActiveXHost.cs
- UnknownExceptionActionHelper.cs
- RegionInfo.cs
- PreProcessInputEventArgs.cs
- BamlTreeNode.cs
- EncoderFallback.cs
- NumericExpr.cs
- CodeTypeConstructor.cs
- Int64KeyFrameCollection.cs
- RegexFCD.cs
- ByteStream.cs
- DelegateBodyWriter.cs
- MSG.cs
- GeometryHitTestParameters.cs
- XmlDataSourceDesigner.cs
- FragmentQuery.cs
- OlePropertyStructs.cs
- CategoryGridEntry.cs
- Pens.cs
- XomlCompilerError.cs
- XmlReader.cs
- CapabilitiesSection.cs
- UpdatePanel.cs
- Selection.cs
- SID.cs
- PreservationFileWriter.cs
- XamlWrappingReader.cs
- OdbcCommandBuilder.cs
- TransformCryptoHandle.cs
- StronglyTypedResourceBuilder.cs
- TableLayoutPanel.cs