Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / 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 IComparer NameComparer
{
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 IComparer NameComparer
{
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
- RC2CryptoServiceProvider.cs
- GridViewUpdatedEventArgs.cs
- MetadataWorkspace.cs
- SQLStringStorage.cs
- WSDualHttpBindingElement.cs
- LambdaCompiler.ControlFlow.cs
- DataControlFieldHeaderCell.cs
- ColorPalette.cs
- AvtEvent.cs
- CodeTypeDeclarationCollection.cs
- XPathNavigator.cs
- DataGridViewSelectedCellsAccessibleObject.cs
- PictureBoxDesigner.cs
- TransformerTypeCollection.cs
- MeasurementDCInfo.cs
- SizeAnimation.cs
- ToolConsole.cs
- XamlReaderHelper.cs
- BamlWriter.cs
- ObjectDataProvider.cs
- StreamWithDictionary.cs
- FileSystemEventArgs.cs
- HttpCapabilitiesEvaluator.cs
- EventMappingSettingsCollection.cs
- StaticSiteMapProvider.cs
- Geometry3D.cs
- DataObjectEventArgs.cs
- TreeNodeConverter.cs
- PlaceHolder.cs
- SafeLibraryHandle.cs
- RecipientInfo.cs
- SByteStorage.cs
- ExceptionNotification.cs
- DerivedKeySecurityTokenStub.cs
- Menu.cs
- RtfControlWordInfo.cs
- InputLangChangeRequestEvent.cs
- cookiecollection.cs
- CompilerParameters.cs
- GenericPrincipal.cs
- SerializationInfoEnumerator.cs
- Compiler.cs
- TouchEventArgs.cs
- HostedTransportConfigurationBase.cs
- InlineUIContainer.cs
- WebPartConnectionsCloseVerb.cs
- ParserHooks.cs
- LingerOption.cs
- Model3DGroup.cs
- PropertyEmitterBase.cs
- ToolboxComponentsCreatedEventArgs.cs
- SchemaSetCompiler.cs
- DataGridHeaderBorder.cs
- SetUserLanguageRequest.cs
- XamlReader.cs
- StringResourceManager.cs
- ReservationCollection.cs
- Component.cs
- NetworkInformationPermission.cs
- BitmapEffect.cs
- SmtpNegotiateAuthenticationModule.cs
- ToolStripProgressBar.cs
- GenericEnumerator.cs
- ReliabilityContractAttribute.cs
- PriorityItem.cs
- BitmapSource.cs
- externdll.cs
- HandleRef.cs
- DirectionalLight.cs
- ApplicationManager.cs
- PhysicalFontFamily.cs
- DbConnectionStringBuilder.cs
- EmptyEnumerator.cs
- AudioSignalProblemOccurredEventArgs.cs
- ToolboxItemSnapLineBehavior.cs
- EncryptedPackageFilter.cs
- SourceFilter.cs
- XmlBinaryReader.cs
- ProfileModule.cs
- GifBitmapDecoder.cs
- UpWmlPageAdapter.cs
- Sentence.cs
- DesignOnlyAttribute.cs
- FormsAuthenticationConfiguration.cs
- TransformProviderWrapper.cs
- DbModificationClause.cs
- ReflectionUtil.cs
- WorkflowRuntimeSection.cs
- DataServiceProviderMethods.cs
- IResourceProvider.cs
- DocumentApplication.cs
- Drawing.cs
- ValidationError.cs
- SystemIPAddressInformation.cs
- BooleanKeyFrameCollection.cs
- MultiTargetingUtil.cs
- Table.cs
- _ListenerRequestStream.cs
- DataTransferEventArgs.cs
- DataBoundControlHelper.cs