From f3978853102bd7db9294d51584d7cb85ac9a0090 Mon Sep 17 00:00:00 2001 From: Rahul Kumar Date: Sun, 13 Nov 2016 09:34:58 -0800 Subject: [PATCH] Add implementation for AssemblyName::EscapedCodeBase (dotnet/coreclr#8072) Commit migrated from https://github.com/dotnet/coreclr/commit/b28f557ee79d70a83e299d6c83a2c803139edba2 --- src/coreclr/src/mscorlib/model.xml | 4 + src/coreclr/src/mscorlib/ref/mscorlib.cs | 2 + .../mscorlib/src/System/Reflection/AssemblyName.cs | 226 ++++++++++++++++++++- 3 files changed, 230 insertions(+), 2 deletions(-) diff --git a/src/coreclr/src/mscorlib/model.xml b/src/coreclr/src/mscorlib/model.xml index 393a4bb..d43dd57 100644 --- a/src/coreclr/src/mscorlib/model.xml +++ b/src/coreclr/src/mscorlib/model.xml @@ -3958,6 +3958,7 @@ + @@ -4019,6 +4020,7 @@ + @@ -4158,6 +4160,7 @@ + @@ -4191,6 +4194,7 @@ + diff --git a/src/coreclr/src/mscorlib/ref/mscorlib.cs b/src/coreclr/src/mscorlib/ref/mscorlib.cs index 4c364ad..f63433e 100644 --- a/src/coreclr/src/mscorlib/ref/mscorlib.cs +++ b/src/coreclr/src/mscorlib/ref/mscorlib.cs @@ -7230,6 +7230,7 @@ namespace System.Reflection public virtual System.Collections.Generic.IEnumerable CustomAttributes { get { throw null; } } public virtual System.Collections.Generic.IEnumerable DefinedTypes { get { throw null; } } public virtual System.Reflection.MethodInfo EntryPoint { get { throw null; } } + public virtual string EscapedCodeBase { [System.Security.SecurityCriticalAttribute]get { throw null; } } public virtual System.Collections.Generic.IEnumerable ExportedTypes { get { throw null; } } public virtual string FullName { get { throw null; } } public virtual bool GlobalAssemblyCache { get { throw null; } } @@ -7436,6 +7437,7 @@ namespace System.Reflection public System.Globalization.CultureInfo CultureInfo { get { throw null; } set { } } public string CultureName { [System.Security.SecurityCriticalAttribute]get { throw null; } [System.Security.SecurityCriticalAttribute]set { } } public string CodeBase { get { throw null; } set { } } + public string EscapedCodeBase { get { throw null; } } public System.Reflection.AssemblyNameFlags Flags { get { throw null; } set { } } public string FullName { [System.Security.SecuritySafeCriticalAttribute]get { throw null; } } public System.Configuration.Assemblies.AssemblyHashAlgorithm HashAlgorithm { get { throw null; } set { } } diff --git a/src/coreclr/src/mscorlib/src/System/Reflection/AssemblyName.cs b/src/coreclr/src/mscorlib/src/System/Reflection/AssemblyName.cs index 4c3d5aa..c45a461 100644 --- a/src/coreclr/src/mscorlib/src/System/Reflection/AssemblyName.cs +++ b/src/coreclr/src/mscorlib/src/System/Reflection/AssemblyName.cs @@ -24,6 +24,7 @@ namespace System.Reflection { using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Diagnostics.Contracts; + using System.Text; [Serializable] [ClassInterface(ClassInterfaceType.None)] @@ -533,7 +534,228 @@ namespace System.Reflection { private extern byte[] nGetPublicKeyToken(); [System.Security.SecurityCritical] // auto-generated - [MethodImplAttribute(MethodImplOptions.InternalCall)] - static internal extern String EscapeCodeBase(String codeBase); + static internal String EscapeCodeBase(String codebase) + { + if (codebase == null) + return string.Empty; + + int position = 0; + char[] dest = EscapeString(codebase, 0, codebase.Length, null, ref position, true, c_DummyChar, c_DummyChar, c_DummyChar); + if (dest == null) + return codebase; + + return new string(dest, 0, position); + } + + // This implementation of EscapeString has been copied from System.Private.Uri from corefx repo + // - forceX characters are always escaped if found + // - rsvd character will remain unescaped + // + // start - starting offset from input + // end - the exclusive ending offset in input + // destPos - starting offset in dest for output, on return this will be an exclusive "end" in the output. + // + // In case "dest" has lack of space it will be reallocated by preserving the _whole_ content up to current destPos + // + // Returns null if nothing has to be escaped AND passed dest was null, otherwise the resulting array with the updated destPos + // + internal unsafe static char[] EscapeString(string input, int start, int end, char[] dest, ref int destPos, + bool isUriString, char force1, char force2, char rsvd) + { + int i = start; + int prevInputPos = start; + byte* bytes = stackalloc byte[c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar]; // 40*4=160 + + fixed (char* pStr = input) + { + for (; i < end; ++i) + { + char ch = pStr[i]; + + // a Unicode ? + if (ch > '\x7F') + { + short maxSize = (short)Math.Min(end - i, (int)c_MaxUnicodeCharsReallocate - 1); + + short count = 1; + for (; count < maxSize && pStr[i + count] > '\x7f'; ++count) + ; + + // Is the last a high surrogate? + if (pStr[i + count - 1] >= 0xD800 && pStr[i + count - 1] <= 0xDBFF) + { + // Should be a rare case where the app tries to feed an invalid Unicode surrogates pair + if (count == 1 || count == end - i) + throw new FormatException(Environment.GetResourceString("Arg_FormatException")); + // need to grab one more char as a Surrogate except when it's a bogus input + ++count; + } + + dest = EnsureDestinationSize(pStr, dest, i, + (short)(count * c_MaxUTF_8BytesPerUnicodeChar * c_EncodedCharsPerByte), + c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar * c_EncodedCharsPerByte, + ref destPos, prevInputPos); + + short numberOfBytes = (short)Encoding.UTF8.GetBytes(pStr + i, count, bytes, + c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar); + + // This is the only exception that built in UriParser can throw after a Uri ctor. + // Should not happen unless the app tries to feed an invalid Unicode String + if (numberOfBytes == 0) + throw new FormatException(Environment.GetResourceString("Arg_FormatException")); + + i += (count - 1); + + for (count = 0; count < numberOfBytes; ++count) + EscapeAsciiChar((char)bytes[count], dest, ref destPos); + + prevInputPos = i + 1; + } + else if (ch == '%' && rsvd == '%') + { + // Means we don't reEncode '%' but check for the possible escaped sequence + dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, + c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos); + if (i + 2 < end && EscapedAscii(pStr[i + 1], pStr[i + 2]) != c_DummyChar) + { + // leave it escaped + dest[destPos++] = '%'; + dest[destPos++] = pStr[i + 1]; + dest[destPos++] = pStr[i + 2]; + i += 2; + } + else + { + EscapeAsciiChar('%', dest, ref destPos); + } + prevInputPos = i + 1; + } + else if (ch == force1 || ch == force2) + { + dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, + c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos); + EscapeAsciiChar(ch, dest, ref destPos); + prevInputPos = i + 1; + } + else if (ch != rsvd && (isUriString ? !IsReservedUnreservedOrHash(ch) : !IsUnreserved(ch))) + { + dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, + c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos); + EscapeAsciiChar(ch, dest, ref destPos); + prevInputPos = i + 1; + } + } + + if (prevInputPos != i) + { + // need to fill up the dest array ? + if (prevInputPos != start || dest != null) + dest = EnsureDestinationSize(pStr, dest, i, 0, 0, ref destPos, prevInputPos); + } + } + + return dest; + } + + // + // ensure destination array has enough space and contains all the needed input stuff + // + private unsafe static char[] EnsureDestinationSize(char* pStr, char[] dest, int currentInputPos, + short charsToAdd, short minReallocateChars, ref int destPos, int prevInputPos) + { + if ((object)dest == null || dest.Length < destPos + (currentInputPos - prevInputPos) + charsToAdd) + { + // allocating or reallocating array by ensuring enough space based on maxCharsToAdd. + char[] newresult = new char[destPos + (currentInputPos - prevInputPos) + minReallocateChars]; + + if ((object)dest != null && destPos != 0) + Buffer.BlockCopy(dest, 0, newresult, 0, destPos << 1); + dest = newresult; + } + + // ensuring we copied everything form the input string left before last escaping + while (prevInputPos != currentInputPos) + dest[destPos++] = pStr[prevInputPos++]; + return dest; + } + + internal static void EscapeAsciiChar(char ch, char[] to, ref int pos) + { + to[pos++] = '%'; + to[pos++] = s_hexUpperChars[(ch & 0xf0) >> 4]; + to[pos++] = s_hexUpperChars[ch & 0xf]; + } + + internal static char EscapedAscii(char digit, char next) + { + if (!(((digit >= '0') && (digit <= '9')) + || ((digit >= 'A') && (digit <= 'F')) + || ((digit >= 'a') && (digit <= 'f')))) + { + return c_DummyChar; + } + + int res = (digit <= '9') + ? ((int)digit - (int)'0') + : (((digit <= 'F') + ? ((int)digit - (int)'A') + : ((int)digit - (int)'a')) + + 10); + + if (!(((next >= '0') && (next <= '9')) + || ((next >= 'A') && (next <= 'F')) + || ((next >= 'a') && (next <= 'f')))) + { + return c_DummyChar; + } + + return (char)((res << 4) + ((next <= '9') + ? ((int)next - (int)'0') + : (((next <= 'F') + ? ((int)next - (int)'A') + : ((int)next - (int)'a')) + + 10))); + } + + private static unsafe bool IsReservedUnreservedOrHash(char c) + { + if (IsUnreserved(c)) + { + return true; + } + return (RFC3986ReservedMarks.IndexOf(c) >= 0); + } + + internal static unsafe bool IsUnreserved(char c) + { + if (IsAsciiLetterOrDigit(c)) + { + return true; + } + return (RFC3986UnreservedMarks.IndexOf(c) >= 0); + } + + //Only consider ASCII characters + internal static bool IsAsciiLetter(char character) + { + return (character >= 'a' && character <= 'z') || + (character >= 'A' && character <= 'Z'); + } + + internal static bool IsAsciiLetterOrDigit(char character) + { + return IsAsciiLetter(character) || (character >= '0' && character <= '9'); + } + + private static readonly char[] s_hexUpperChars = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + internal const char c_DummyChar = (char)0xFFFF; //An Invalid Unicode character used as a dummy char passed into the parameter + private const short c_MaxAsciiCharsReallocate = 40; + private const short c_MaxUnicodeCharsReallocate = 40; + private const short c_MaxUTF_8BytesPerUnicodeChar = 4; + private const short c_EncodedCharsPerByte = 3; + private const string RFC3986ReservedMarks = @":/?#[]@!$&'()*+,;="; + private const string RFC3986UnreservedMarks = @"-._~"; } } -- 2.7.4