diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..87a3690c0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,67 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain + +# Force bash scripts to always use lf line endings so that if a repo is accessed +# in Unix via a file share from Windows, the scripts will work. +*.sh text eol=lf diff --git a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowchartConnectionPointsAdorner.cs b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowchartConnectionPointsAdorner.cs index d1b3abc1d..2debe1f35 100644 --- a/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowchartConnectionPointsAdorner.cs +++ b/System.Activities.Core.Presentation/System/Activities/Core/Presentation/FlowchartConnectionPointsAdorner.cs @@ -63,10 +63,11 @@ protected override void OnRender(DrawingContext drawingContext) trueLabelText = (string)virtualizingContainer.ModelItem.Properties["TrueLabel"].ComputedValue; } + double pixelsPerDip = VisualTreeHelper.GetDpi(trueConnectionPoint).PixelsPerDip; actualPoint = new Point(trueConnectionPoint.Location.X - origin.X, trueConnectionPoint.Location.Y - origin.Y); FormattedText trueMarkerFormattedText = new FormattedText(trueLabelText, new System.Globalization.CultureInfo(textCulture), this.FlowDirection, FlowchartDesigner.FlowElementCaptionTypeface, FlowchartDesigner.FlowNodeCaptionFontSize, - new SolidColorBrush(WorkflowDesignerColors.WorkflowViewElementCaptionColor)); + new SolidColorBrush(WorkflowDesignerColors.WorkflowViewElementCaptionColor), pixelsPerDip); actualPoint.Y += ConnectionPoint.DrawingLargeSide / 2; actualPoint.X -= trueMarkerFormattedText.WidthIncludingTrailingWhitespace; @@ -94,9 +95,10 @@ protected override void OnRender(DrawingContext drawingContext) actualPoint = new Point(falseConnectionPoint.Location.X - origin.X, falseConnectionPoint.Location.Y - origin.Y); actualPoint.Y += ConnectionPoint.DrawingLargeSide / 2; + double pixelsPerDip = VisualTreeHelper.GetDpi(falseConnectionPoint).PixelsPerDip; FormattedText falseMarkerFormattedText = new FormattedText(falseLabelText, new System.Globalization.CultureInfo(textCulture), this.FlowDirection, FlowchartDesigner.FlowElementCaptionTypeface, FlowchartDesigner.FlowNodeCaptionFontSize, - new SolidColorBrush(WorkflowDesignerColors.WorkflowViewElementCaptionColor)); + new SolidColorBrush(WorkflowDesignerColors.WorkflowViewElementCaptionColor), pixelsPerDip); DrawtWithTransform( drawingContext, diff --git a/System.Activities.Presentation/Microsoft.Tools.Common/Microsoft/Activities/Presentation/Xaml/ViewStateXamlHelper.cs b/System.Activities.Presentation/Microsoft.Tools.Common/Microsoft/Activities/Presentation/Xaml/ViewStateXamlHelper.cs index 08bf6c294..fb5266eb1 100644 --- a/System.Activities.Presentation/Microsoft.Tools.Common/Microsoft/Activities/Presentation/Xaml/ViewStateXamlHelper.cs +++ b/System.Activities.Presentation/Microsoft.Tools.Common/Microsoft/Activities/Presentation/Xaml/ViewStateXamlHelper.cs @@ -7,6 +7,7 @@ namespace Microsoft.Activities.Presentation.Xaml using System; using System.Activities; using System.Activities.Debugger; + using System.Activities.DynamicUpdate; using System.Activities.Presentation.View; using System.Activities.Presentation.ViewState; using System.Collections.Generic; @@ -30,6 +31,18 @@ internal static class ViewStateXamlHelper XamlDebuggerXmlReader.EndColumnName.MemberName }; + // These are used to discover that we have found a DynamicUpdateInfo.OriginalDefintion or OriginalActivityBuilder + // attached property member. We have "hardcoded" the *MemberName" here because DynamicUpdateInfo has the + // AttachableMemberIdentifier properties marked as private. But the DynamicUpdateInfo class itself is public, + // as are the Get and Set methods. + static readonly string DynamicUpdateOriginalDefinitionMemberName = "OriginalDefinition"; + static readonly MethodInfo GetOriginalDefinition = typeof(DynamicUpdateInfo).GetMethod("GetOriginalDefinition"); + static readonly MethodInfo SetOriginalDefinition = typeof(DynamicUpdateInfo).GetMethod("SetOriginalDefinition"); + + static readonly string DynamicUpdateOriginalActivityBuilderMemberName = "OriginalActivityBuilder"; + static readonly MethodInfo GetOriginalActivityBuilder = typeof(DynamicUpdateInfo).GetMethod("GetOriginalActivityBuilder"); + static readonly MethodInfo SetOriginalActivityBuilder = typeof(DynamicUpdateInfo).GetMethod("SetOriginalActivityBuilder"); + // This method collects view state attached properties and generates a Xaml node stream // with all view state information appearing within the ViewStateManager node. // It is called when workflow definition is being serialized to string. @@ -279,6 +292,22 @@ public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputRe // Xaml member definition for IdRef. Used to identify existing IdRef properties in the input nodestream. XamlMember idRefMember = new XamlMember(IdRef, GetIdRef, SetIdRef, inputReader.SchemaContext); + // These are used to ignore the IdRef members that are inside a DynamicUpdateInfo.OriginalDefinition/OriginalActivityBuilder attached property. + // We need to ignore these because if we don't, the IdRef values for the objects in the actual workflow defintion will be ignored because of the + // duplicate IdRef value. This causes problems with activity designers that depend on the ViewStateManager data to correctly display the workflow + // on the WorkflowDesigner canvas. + XamlMember originalDefinitionMember = new XamlMember(DynamicUpdateOriginalDefinitionMemberName, GetOriginalDefinition, SetOriginalDefinition, inputReader.SchemaContext); + XamlMember originalActivityBuilderMember = new XamlMember(DynamicUpdateOriginalActivityBuilderMemberName, GetOriginalActivityBuilder, SetOriginalActivityBuilder, inputReader.SchemaContext); + + // insideOriginalDefintion gets set to true when we find a "StartMember" node for either of the above two attached properties. + // originalDefintionMemberCount gets incremented if we find any "StartMember" and insideOriginalDefinition is true. + // originalDefintionMemberCount gets decremented if we find any "EndMember" and insideOriginalDefintion is true. + // insideOriginalDefintion gets set to false when we find an "EndMember" and originalDefinitionMemberCount gets decremented to 0. + // If insideOriginalDefintion is true when we find an "IdRef" member, we do NOT add that IdRef to the idRefsSeen HashSet to avoid + // duplicates being defined by the IdRefs inside of the OriginalDefinition attached properties. + bool insideOriginalDefinition = false; + int originalDefinitionMemberCount = 0; + // Dictionary containing Ids and corresponding viewstate related // attached property nodes. Populated by StripViewStateElement method. Dictionary viewStateInfo = null; @@ -323,9 +352,21 @@ public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputRe break; case XamlNodeType.StartMember: + // If we find a StartMember for DynamicUpdateInfo.OriginalDefinition or OriginalActivityBuilder, remember that we are + // inside one of those. We don't want to "remember" IdRef values in the idRefsSeen HashSet while inside these attached properties. + if (workflowDefinition.Member.Equals(originalDefinitionMember) || workflowDefinition.Member.Equals(originalActivityBuilderMember)) + { + insideOriginalDefinition = true; + } + + if (insideOriginalDefinition) + { + originalDefinitionMemberCount++; + } + // Track when the reader enters IdRef. Skip writing the start // node to the output nodelist until we check for duplicates. - if (workflowDefinition.Member.Equals(idRefMember)) + else if (workflowDefinition.Member.Equals(idRefMember)) { inIdRefMember = true; skipWritingWorkflowDefinition = true; @@ -341,38 +382,43 @@ public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputRe case XamlNodeType.Value: if (inIdRefMember) { - string idRef = workflowDefinition.Value as string; - if (!string.IsNullOrWhiteSpace(idRef)) + // We don't want to deal with the IdRef if we are inside a DynamicUpdateInfo.OriginalDefinition/OriginalActivityBuilder + // attached property. + if (!insideOriginalDefinition) { - // If IdRef value is a duplicate then do not associate it with - // the stack frame (top of stack == activity node with IdRef member on it). - if (idRefsSeen.Contains(idRef)) - { - stack.Peek().IdRef = null; - } - // If the IdRef value is unique then associate it with the - // stack frame and also write its value into the output nodestream. - else + string idRef = workflowDefinition.Value as string; + if (!string.IsNullOrWhiteSpace(idRef)) { - stack.Peek().IdRef = idRef; - idManager.UpdateMap(idRef); - idRefsSeen.Add(idRef); - - if (shouldPassLineInfo) + // If IdRef value is a duplicate then do not associate it with + // the stack frame (top of stack == activity node with IdRef member on it). + if (idRefsSeen.Contains(idRef)) { - lineInfoComsumer.SetLineInfo(idRefLineNumber, idRefLinePosition); + stack.Peek().IdRef = null; } + // If the IdRef value is unique then associate it with the + // stack frame and also write its value into the output nodestream. + else + { + stack.Peek().IdRef = idRef; + idManager.UpdateMap(idRef); + idRefsSeen.Add(idRef); - mergedNodeWriter.WriteStartMember(idRefMember); + if (shouldPassLineInfo) + { + lineInfoComsumer.SetLineInfo(idRefLineNumber, idRefLinePosition); + } - if (shouldPassLineInfo) - { - lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); - } + mergedNodeWriter.WriteStartMember(idRefMember); - mergedNodeWriter.WriteValue(idRef); + if (shouldPassLineInfo) + { + lineInfoComsumer.SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); + } - shouldWriteIdRefEndMember = true; + mergedNodeWriter.WriteValue(idRef); + + shouldWriteIdRefEndMember = true; + } } } // Don't need to write IdRef value into the output @@ -382,9 +428,21 @@ public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputRe break; case XamlNodeType.EndMember: + // If we are inside an OriginalDefinition/OriginalActivityBuilder attached property, + // decrement the count and if it goes to zero, set insideOriginalDefintion to false + // because we just encountered the EndMember for it. + if (insideOriginalDefinition) + { + originalDefinitionMemberCount--; + if (originalDefinitionMemberCount == 0) + { + insideOriginalDefinition = false; + } + } + // Exit IdRef node. Skip writing the EndMember node, we would have done // it as part of reading the IdRef value. - if (inIdRefMember) + if (inIdRefMember && !insideOriginalDefinition) { inIdRefMember = false; skipWritingWorkflowDefinition = true; @@ -401,6 +459,7 @@ public static XamlReader ConvertViewStateToAttachedProperties(XamlReader inputRe mergedNodeWriter.WriteEndMember(); } } + break; case XamlNodeType.EndObject: @@ -592,6 +651,7 @@ static XamlReader StripViewStateElement(XamlReader inputReader, out Dictionary viewStateInfo, out Dictionary viewStateSourceLocationMap) @@ -700,7 +760,11 @@ static void ReadViewState(XamlType viewStateType, XamlReader xamlReader, out str } } } - else if (globalMemberLevel == 1 && !IsAttachablePropertyForConvert(xamlReader)) + // The xamlReader.ReadSubtree and subsequent while loop to get the Id member + // has moved the xamlReader forward to the next member. We need to check to see + // if it is an Attached Property that we care about. If it isn't then we need to + // skip it and not put it in the resulting XamlNodeList. + if (globalMemberLevel == 1 && !IsAttachablePropertyForConvert(xamlReader)) { skippingUnexpectedAttachedProperty = true; } diff --git a/System.Activities/System/Activities/Hosting/WorkflowInstance.cs b/System.Activities/System/Activities/Hosting/WorkflowInstance.cs index 6b24f0ea5..c4cd899b8 100644 --- a/System.Activities/System/Activities/Hosting/WorkflowInstance.cs +++ b/System.Activities/System/Activities/Hosting/WorkflowInstance.cs @@ -48,6 +48,20 @@ public abstract class WorkflowInstance StackTrace abortStack; #endif + static WorkflowInstance() + { + try + { + using (TelemetryEventSource eventSource = new TelemetryEventSource()) + { + eventSource.V2Runtime(); + } + } + catch + { + } + } + protected WorkflowInstance(Activity workflowDefinition) : this(workflowDefinition, null) { diff --git a/System.Activities/System/Activities/WorkflowApplication.cs b/System.Activities/System/Activities/WorkflowApplication.cs index 209006325..ff9b74d72 100644 --- a/System.Activities/System/Activities/WorkflowApplication.cs +++ b/System.Activities/System/Activities/WorkflowApplication.cs @@ -104,7 +104,7 @@ public sealed class WorkflowApplication : WorkflowInstance IList rootExecutionProperties; IDictionary instanceMetadata; - + public WorkflowApplication(Activity workflowDefinition) : this(workflowDefinition, (WorkflowIdentity)null) { diff --git a/System.ComponentModel.DataAnnotations/DataAnnotations/LocalAppContextSwitches.cs b/System.ComponentModel.DataAnnotations/DataAnnotations/LocalAppContextSwitches.cs index 20b80cfde..799f49541 100644 --- a/System.ComponentModel.DataAnnotations/DataAnnotations/LocalAppContextSwitches.cs +++ b/System.ComponentModel.DataAnnotations/DataAnnotations/LocalAppContextSwitches.cs @@ -21,8 +21,10 @@ public static bool UseLegacyRegExTimeout { } public static void SetDefaultsLessOrEqual_46() { +#pragma warning disable BCL0012 //disable warning about AppContextDefaults not following the recommended pattern // Define the switches that should be true for 4.6 or less, false for 4.6.1+. LocalAppContext.DefineSwitchDefault(UseLegacyRegExTimeoutString, true); +#pragma warning restore BCL0012 } } } diff --git a/System.ComponentModel.DataAnnotations/DataAnnotations/RegularExpressionAttribute.cs b/System.ComponentModel.DataAnnotations/DataAnnotations/RegularExpressionAttribute.cs index fc5c061b7..f37b921e0 100644 --- a/System.ComponentModel.DataAnnotations/DataAnnotations/RegularExpressionAttribute.cs +++ b/System.ComponentModel.DataAnnotations/DataAnnotations/RegularExpressionAttribute.cs @@ -1,4 +1,4 @@ -using System.ComponentModel.DataAnnotations.Resources; +using System.ComponentModel.DataAnnotations.Resources; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text.RegularExpressions; @@ -19,7 +19,18 @@ public class RegularExpressionAttribute : ValidationAttribute { /// Gets or sets the timeout to use when matching the regular expression pattern (in milliseconds) /// (-1 means never timeout). /// - public int MatchTimeoutInMilliseconds { get; set; } = GetDefaultTimeout(); + public int MatchTimeoutInMilliseconds { + get { + return _matchTimeoutInMilliseconds; + } + set { + _matchTimeoutInMilliseconds = value; + _matchTimeoutSet = true; + } + } + + private int _matchTimeoutInMilliseconds; + private bool _matchTimeoutSet; private Regex Regex { get; set; } @@ -90,6 +101,11 @@ private void SetupRegex() { if (string.IsNullOrEmpty(this.Pattern)) { throw new InvalidOperationException(DataAnnotationsResources.RegularExpressionAttribute_Empty_Pattern); } + + if (!_matchTimeoutSet) { + MatchTimeoutInMilliseconds = GetDefaultTimeout(); + } + Regex = MatchTimeoutInMilliseconds == -1 ? new Regex(Pattern) : Regex = new Regex(Pattern, default(RegexOptions), TimeSpan.FromMilliseconds((double)MatchTimeoutInMilliseconds)); diff --git a/System.ComponentModel.DataAnnotations/InternalApis/Clr/inc/AppContextDefaultValues.cs b/System.ComponentModel.DataAnnotations/InternalApis/Clr/inc/AppContextDefaultValues.cs index 8a130a0f3..9224bf8b8 100644 --- a/System.ComponentModel.DataAnnotations/InternalApis/Clr/inc/AppContextDefaultValues.cs +++ b/System.ComponentModel.DataAnnotations/InternalApis/Clr/inc/AppContextDefaultValues.cs @@ -3,6 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== + +// There are cases where we have multiple assemblies that are going to import this file and +// if they are going to also have InternalsVisibleTo between them, there will be a compiler warning +// that the type is found both in the source and in a referenced assembly. The compiler will prefer +// the version of the type defined in the source +// +// In order to disable the warning for this type we are disabling this warning for this entire file. +#pragma warning disable 436 + using System; using System.Collections.Generic; @@ -167,3 +176,5 @@ private static bool TryParseFrameworkName(String frameworkName, out String ident static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version); } } + +#pragma warning restore 436 diff --git a/System.ComponentModel.DataAnnotations/InternalApis/Clr/inc/LocalAppContext.cs b/System.ComponentModel.DataAnnotations/InternalApis/Clr/inc/LocalAppContext.cs index f05b599ed..33662b542 100644 --- a/System.ComponentModel.DataAnnotations/InternalApis/Clr/inc/LocalAppContext.cs +++ b/System.ComponentModel.DataAnnotations/InternalApis/Clr/inc/LocalAppContext.cs @@ -4,6 +4,14 @@ // // ==--== +// There are cases where we have multiple assemblies that are going to import this file and +// if they are going to also have InternalsVisibleTo between them, there will be a compiler warning +// that the type is found both in the source and in a referenced assembly. The compiler will prefer +// the version of the type defined in the source +// +// In order to disable the warning for this type we are disabling this warning for this entire file. +#pragma warning disable 436 + // NOTE: This file should not be included in mscorlib. This should only be included in FX libraries that need to provide switches using System; using System.Collections.Generic; @@ -126,3 +134,5 @@ internal static void DefineSwitchDefault(string switchName, bool initialValue) } } } + +#pragma warning restore 436 diff --git a/System.Configuration/System/Configuration/ClientConfigPaths.cs b/System.Configuration/System/Configuration/ClientConfigPaths.cs index 4e46c6171..5ee50f1ad 100644 --- a/System.Configuration/System/Configuration/ClientConfigPaths.cs +++ b/System.Configuration/System/Configuration/ClientConfigPaths.cs @@ -26,6 +26,8 @@ class ClientConfigPaths { const string ClickOnceDataDirectory = "DataDirectory"; const string ConfigExtension = ".config"; const int MAX_PATH = 260; + const int MAX_UNICODESTRING_LEN = short.MaxValue; + const int ERROR_INSUFFICIENT_BUFFER = 122; //https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx const int MAX_LENGTH_TO_USE = 25; const string FILE_URI_LOCAL = "file:///"; const string FILE_URI_UNC = "file://"; @@ -108,7 +110,18 @@ private ClientConfigPaths(string exePath, bool includeUserConfig) { } else { StringBuilder sb = new StringBuilder(MAX_PATH); - UnsafeNativeMethods.GetModuleFileName(new HandleRef(null, IntPtr.Zero), sb, sb.Capacity); + int noOfTimes = 1; + int length = 0; + // Iterating by allocating chunk of memory each time we find the length is not sufficient. + // Performance should not be an issue for current MAX_PATH length due to this change. + while (((length = UnsafeNativeMethods.GetModuleFileName(new HandleRef(null, IntPtr.Zero), sb, sb.Capacity)) == sb.Capacity) + && Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER + && sb.Capacity < MAX_UNICODESTRING_LEN) { + noOfTimes += 2; // increasing buffer size by 520 in each iteration - perf. + int capacity = noOfTimes * MAX_PATH < MAX_UNICODESTRING_LEN ? noOfTimes * MAX_PATH : MAX_UNICODESTRING_LEN; + sb.EnsureCapacity(capacity); + } + sb.Length = length; applicationUri = Path.GetFullPath(sb.ToString()); applicationFilename = applicationUri; } diff --git a/System.Core/Microsoft/Win32/SafeHandles/CapiSafeHandles.cs b/System.Core/Microsoft/Win32/SafeHandles/CapiSafeHandles.cs index 16669900d..acda79628 100644 --- a/System.Core/Microsoft/Win32/SafeHandles/CapiSafeHandles.cs +++ b/System.Core/Microsoft/Win32/SafeHandles/CapiSafeHandles.cs @@ -186,6 +186,8 @@ protected override sealed bool ReleaseHandle() { #pragma warning restore 618 #endif internal sealed class SafeCapiHashHandle : SafeCapiHandleBase { + private static volatile SafeCapiHashHandle s_invalidHandle; + #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif @@ -197,9 +199,17 @@ private SafeCapiHashHandle() { /// public static SafeCapiHashHandle InvalidHandle { get { - SafeCapiHashHandle handle = new SafeCapiHashHandle(); - handle.SetHandle(IntPtr.Zero); - return handle; + if (s_invalidHandle == null) { + // More than one of these might get created in parallel, but that's okay. + // Saving one to the field saves on GC tracking, but by SuppressingFinalize on + // any instance returned there's already less finalization pressure. + SafeCapiHashHandle handle = new SafeCapiHashHandle(); + handle.SetHandle(IntPtr.Zero); + GC.SuppressFinalize(handle); + s_invalidHandle = handle; + } + + return s_invalidHandle; } } @@ -233,6 +243,8 @@ protected override bool ReleaseCapiChildHandle() { #pragma warning restore 618 #endif internal sealed class SafeCapiKeyHandle : SafeCapiHandleBase { + private static volatile SafeCapiKeyHandle s_invalidHandle; + #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif @@ -245,9 +257,17 @@ private SafeCapiKeyHandle() { internal static SafeCapiKeyHandle InvalidHandle { [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] get { - SafeCapiKeyHandle handle = new SafeCapiKeyHandle(); - handle.SetHandle(IntPtr.Zero); - return handle; + if (s_invalidHandle == null) { + // More than one of these might get created in parallel, but that's okay. + // Saving one to the field saves on GC tracking, but by SuppressingFinalize on + // any instance returned there's already less finalization pressure. + SafeCapiKeyHandle handle = new SafeCapiKeyHandle(); + handle.SetHandle(IntPtr.Zero); + GC.SuppressFinalize(handle); + s_invalidHandle = handle; + } + + return s_invalidHandle; } } diff --git a/System.Core/System/Security/Cryptography/BCryptNative.cs b/System.Core/System/Security/Cryptography/BCryptNative.cs index 48a846796..cfc18b21f 100644 --- a/System.Core/System/Security/Cryptography/BCryptNative.cs +++ b/System.Core/System/Security/Cryptography/BCryptNative.cs @@ -38,6 +38,35 @@ internal enum AsymmetricPaddingMode { /// Pss = 8 // BCRYPT_PAD_PSS } + + [StructLayout(LayoutKind.Sequential)] + internal struct BCRYPT_DSA_KEY_BLOB_V2 + { + public BCryptNative.KeyBlobMagicNumber dwMagic; // BCRYPT_DSA_PUBLIC_MAGIC_V2 or BCRYPT_DSA_PRIVATE_MAGIC_V2 + public int cbKey; // key lengths in BYTES (e.g. for a 3072-bit key, cbKey = 3072/8 = 384) + public HASHALGORITHM_ENUM hashAlgorithm; + public DSAFIPSVERSION_ENUM standardVersion; + public int cbSeedLength; // size (in bytes) of the seed value + public int cbGroupSize; // size (in bytes) of the Q value + public byte Count3; // # of iterations used to generate Q. In big-endian format. + public byte Count2; + public byte Count1; + public byte Count0; + } + + internal enum HASHALGORITHM_ENUM : int + { + DSA_HASH_ALGORITHM_SHA1 = 0, + DSA_HASH_ALGORITHM_SHA256 = 1, + DSA_HASH_ALGORITHM_SHA512 = 2, + } + + internal enum DSAFIPSVERSION_ENUM : int + { + DSA_FIPS186_2 = 0, + DSA_FIPS186_3 = 1, + } + /// /// Native interop with CNG's BCrypt layer. Native definitions can be found in bcrypt.h /// @@ -102,6 +131,10 @@ internal static class HashPropertyName { /// Magic numbers identifying blob types /// internal enum KeyBlobMagicNumber { + DsaPublic = 0x42505344, // BCRYPT_DSA_PUBLIC_MAGIC for key lengths <= 1024 bits + DsaPublicV2 = 0x32425044, // BCRYPT_DSA_PUBLIC_MAGIC_V2 for key lengths > 1024 bits + DsaPrivate = 0x56505344, // BCRYPT_DSA_PRIVATE_MAGIC for key lengths <= 1024 bits + DsaPrivateV2 = 0x32565044, // BCRYPT_DSA_PRIVATE_MAGIC_V2 for key lengths > 1024 bits ECDHPublicP256 = 0x314B4345, // BCRYPT_ECDH_PUBLIC_P256_MAGIC ECDHPublicP384 = 0x334B4345, // BCRYPT_ECDH_PUBLIC_P384_MAGIC ECDHPublicP521 = 0x354B4345, // BCRYPT_ECDH_PUBLIC_P521_MAGIC diff --git a/System.Core/System/Security/Cryptography/CapiNative.cs b/System.Core/System/Security/Cryptography/CapiNative.cs index b9f7080af..cbf197179 100644 --- a/System.Core/System/Security/Cryptography/CapiNative.cs +++ b/System.Core/System/Security/Cryptography/CapiNative.cs @@ -168,6 +168,14 @@ internal struct CRYPTOAPI_BLOB { public IntPtr pbData; } + [StructLayout(LayoutKind.Sequential)] + internal struct CERT_DSS_PARAMETERS + { + public CRYPTOAPI_BLOB p; + public CRYPTOAPI_BLOB q; + public CRYPTOAPI_BLOB g; + } + [StructLayout(LayoutKind.Sequential)] internal unsafe struct PROV_ENUMALGS { public AlgorithmId aiAlgId; @@ -187,6 +195,8 @@ internal unsafe struct PROV_ENUMALGS { internal const uint CALG_DSS_SIGN = (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_DSS_ANY); internal const uint CALG_RSA_KEYX = (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_RSA | ALG_SID_RSA_ANY); internal const uint CNG_RSA_PUBLIC_KEY_BLOB = 72; + internal const uint X509_DSS_PUBLICKEY = 38; + internal const uint X509_DSS_PARAMETERS = 39; internal const uint X509_ASN_ENCODING = 0x00000001; internal const uint PKCS_7_ASN_ENCODING = 0x00010000; diff --git a/System.Core/System/Security/Cryptography/CapiSymmetricAlgorithm.cs b/System.Core/System/Security/Cryptography/CapiSymmetricAlgorithm.cs index 490e61651..c1c5bdc3d 100644 --- a/System.Core/System/Security/Cryptography/CapiSymmetricAlgorithm.cs +++ b/System.Core/System/Security/Cryptography/CapiSymmetricAlgorithm.cs @@ -447,6 +447,9 @@ private void Reset() { buffer.Length); } else { + if (!LocalAppContextSwitches.AesCryptoServiceProviderDontCorrectlyResetDecryptor) { + resetSize = buffer.Length; + } CapiNative.UnsafeNativeMethods.CryptDecrypt(m_key, SafeCapiHashHandle.InvalidHandle, true, diff --git a/System.Core/System/Security/Cryptography/ECDiffieHellman.cs b/System.Core/System/Security/Cryptography/ECDiffieHellman.cs index 8108af745..7403ffbe8 100644 --- a/System.Core/System/Security/Cryptography/ECDiffieHellman.cs +++ b/System.Core/System/Security/Cryptography/ECDiffieHellman.cs @@ -42,6 +42,101 @@ public override string SignatureAlgorithm { // public abstract ECDiffieHellmanPublicKey PublicKey { get; } - public abstract byte[] DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPublicKey); + + // This method must be implemented by derived classes. In order to conform to the contract, it cannot be abstract. + public virtual byte[] DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPublicKey) + { + throw DerivedClassMustOverride(); + } + + /// + /// Derive key material using the formula HASH(x) where x is the computed result of the EC Diffie-Hellman algorithm. + /// + /// The public key of the party with which to derive a mutual secret. + /// The identifier for the hash algorithm to use. + /// A hashed output suitable for key material + /// is over a different curve than this key + public byte[] DeriveKeyFromHash(ECDiffieHellmanPublicKey otherPartyPublicKey, HashAlgorithmName hashAlgorithm) + { + return DeriveKeyFromHash(otherPartyPublicKey, hashAlgorithm, null, null); + } + + /// + /// Derive key material using the formula HASH(secretPrepend || x || secretAppend) where x is the computed + /// result of the EC Diffie-Hellman algorithm. + /// + /// The public key of the party with which to derive a mutual secret. + /// The identifier for the hash algorithm to use. + /// A value to prepend to the derived secret before hashing. A null value is treated as an empty array. + /// A value to append to the derived secret before hashing. A null value is treated as an empty array. + /// A hashed output suitable for key material + /// is over a different curve than this key + public virtual byte[] DeriveKeyFromHash( + ECDiffieHellmanPublicKey otherPartyPublicKey, + HashAlgorithmName hashAlgorithm, + byte[] secretPrepend, + byte[] secretAppend) + { + throw DerivedClassMustOverride(); + } + + /// + /// Derive key material using the formula HMAC(hmacKey, x) where x is the computed + /// result of the EC Diffie-Hellman algorithm. + /// + /// The public key of the party with which to derive a mutual secret. + /// The identifier for the hash algorithm to use. + /// The key to use in the HMAC. A null value indicates that the result of the EC Diffie-Hellman algorithm should be used as the HMAC key. + /// A hashed output suitable for key material + /// is over a different curve than this key + public byte[] DeriveKeyFromHmac( + ECDiffieHellmanPublicKey otherPartyPublicKey, + HashAlgorithmName hashAlgorithm, + byte[] hmacKey) + { + return DeriveKeyFromHmac(otherPartyPublicKey, hashAlgorithm, hmacKey, null, null); + } + + /// + /// Derive key material using the formula HMAC(hmacKey, secretPrepend || x || secretAppend) where x is the computed + /// result of the EC Diffie-Hellman algorithm. + /// + /// The public key of the party with which to derive a mutual secret. + /// The identifier for the hash algorithm to use. + /// The key to use in the HMAC. A null value indicates that the result of the EC Diffie-Hellman algorithm should be used as the HMAC key. + /// A value to prepend to the derived secret before hashing. A null value is treated as an empty array. + /// A value to append to the derived secret before hashing. A null value is treated as an empty array. + /// A hashed output suitable for key material + /// is over a different curve than this key + public virtual byte[] DeriveKeyFromHmac( + ECDiffieHellmanPublicKey otherPartyPublicKey, + HashAlgorithmName hashAlgorithm, + byte[] hmacKey, + byte[] secretPrepend, + byte[] secretAppend) + { + throw DerivedClassMustOverride(); + } + + /// + /// Derive key material using the TLS pseudo-random function (PRF) derivation algorithm. + /// + /// The public key of the party with which to derive a mutual secret. + /// The ASCII encoded PRF label. + /// The 64-byte PRF seed. + /// A 48-byte output of the TLS pseudo-random function. + /// is over a different curve than this key + /// is null + /// is null + /// is not exactly 64 bytes in length + public virtual byte[] DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey, byte[] prfLabel, byte[] prfSeed) + { + throw DerivedClassMustOverride(); + } + + private static Exception DerivedClassMustOverride() + { + return new NotImplementedException(SR.GetString(SR.NotSupported_SubclassOverride)); + } } } diff --git a/System.Core/System/Security/Cryptography/ECDiffieHellmanCng.cs b/System.Core/System/Security/Cryptography/ECDiffieHellmanCng.cs index ada0c1ac5..4a555dae5 100644 --- a/System.Core/System/Security/Cryptography/ECDiffieHellmanCng.cs +++ b/System.Core/System/Security/Cryptography/ECDiffieHellmanCng.cs @@ -93,7 +93,21 @@ public ECDiffieHellmanCng(CngKey key) { } CodeAccessPermission.RevertAssert(); - KeySize = m_key.KeySize; + // Our LegalKeySizes value stores the values that we encoded as being the correct + // legal key size limitations for this algorithm, as documented on MSDN. + // + // But on a new OS version we might not question if our limit is accurate, or MSDN + // could have been innacurate to start with. + // + // Since the key is already loaded, we know that Windows thought it to be valid; + // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance + // check. + // + // For RSA there are known cases where this change matters. RSACryptoServiceProvider can + // create a 384-bit RSA key, which we consider too small to be legal. It can also create + // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit + // alignment requirement. (In both cases Windows loads it just fine) + KeySizeValue = m_key.KeySize; } /// @@ -247,7 +261,22 @@ private set { // m_key = value; - KeySize = m_key.KeySize; + + // Our LegalKeySizes value stores the values that we encoded as being the correct + // legal key size limitations for this algorithm, as documented on MSDN. + // + // But on a new OS version we might not question if our limit is accurate, or MSDN + // could have been innacurate to start with. + // + // Since the key is already loaded, we know that Windows thought it to be valid; + // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance + // check. + // + // For RSA there are known cases where this change matters. RSACryptoServiceProvider can + // create a 384-bit RSA key, which we consider too small to be legal. It can also create + // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit + // alignment requirement. (In both cases Windows loads it just fine) + KeySizeValue = m_key.KeySize; } } @@ -369,6 +398,84 @@ public byte[] DeriveKeyMaterial(CngKey otherPartyPublicKey) { } } + [SecuritySafeCritical] + public override byte[] DeriveKeyFromHash( + ECDiffieHellmanPublicKey otherPartyPublicKey, + HashAlgorithmName hashAlgorithm, + byte[] secretPrepend, + byte[] secretAppend) + { + Contract.Ensures(Contract.Result() != null); + + if (otherPartyPublicKey == null) + throw new ArgumentNullException("otherPartyPublicKey"); + if (string.IsNullOrEmpty(hashAlgorithm.Name)) + throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm"); + + using (SafeNCryptSecretHandle secretAgreement = DeriveSecretAgreementHandle(otherPartyPublicKey)) + { + return NCryptNative.DeriveKeyMaterialHash( + secretAgreement, + hashAlgorithm.Name, + secretPrepend, + secretAppend, + NCryptNative.SecretAgreementFlags.None); + } + } + + [SecuritySafeCritical] + public override byte[] DeriveKeyFromHmac( + ECDiffieHellmanPublicKey otherPartyPublicKey, + HashAlgorithmName hashAlgorithm, + byte[] hmacKey, + byte[] secretPrepend, + byte[] secretAppend) + { + Contract.Ensures(Contract.Result() != null); + + if (otherPartyPublicKey == null) + throw new ArgumentNullException("otherPartyPublicKey"); + if (string.IsNullOrEmpty(hashAlgorithm.Name)) + throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm"); + + using (SafeNCryptSecretHandle secretAgreement = DeriveSecretAgreementHandle(otherPartyPublicKey)) + { + NCryptNative.SecretAgreementFlags flags = hmacKey == null ? + NCryptNative.SecretAgreementFlags.UseSecretAsHmacKey : + NCryptNative.SecretAgreementFlags.None; + + return NCryptNative.DeriveKeyMaterialHmac( + secretAgreement, + hashAlgorithm.Name, + hmacKey, + secretPrepend, + secretAppend, + flags); + } + } + + [SecuritySafeCritical] + public override byte[] DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey, byte[] prfLabel, byte[] prfSeed) + { + Contract.Ensures(Contract.Result() != null); + + if (otherPartyPublicKey == null) + throw new ArgumentNullException("otherPartyPublicKey"); + if (prfLabel == null) + throw new ArgumentNullException("prfLabel"); + if (prfSeed == null) + throw new ArgumentNullException("prfSeed"); + + using (SafeNCryptSecretHandle secretAgreement = DeriveSecretAgreementHandle(otherPartyPublicKey)) + { + return NCryptNative.DeriveKeyMaterialTls( + secretAgreement, + prfLabel, + prfSeed, + NCryptNative.SecretAgreementFlags.None); + } + } + /// /// Get a handle to the secret agreement generated between two parties /// diff --git a/System.Core/System/Security/Cryptography/ECDiffieHellmanPublicKey.cs b/System.Core/System/Security/Cryptography/ECDiffieHellmanPublicKey.cs index 3099a3a77..91e89d8be 100644 --- a/System.Core/System/Security/Cryptography/ECDiffieHellmanPublicKey.cs +++ b/System.Core/System/Security/Cryptography/ECDiffieHellmanPublicKey.cs @@ -40,6 +40,10 @@ public virtual byte[] ToByteArray() { return m_keyBlob.Clone() as byte[]; } - public abstract string ToXmlString(); + // This method must be implemented by derived classes. In order to conform to the contract, it cannot be abstract. + public virtual string ToXmlString() + { + throw new NotImplementedException(SR.GetString(SR.NotSupported_SubclassOverride)); + } } } diff --git a/System.Core/System/Security/Cryptography/ECDsaCng.cs b/System.Core/System/Security/Cryptography/ECDsaCng.cs index 40505e30d..096ce83a0 100644 --- a/System.Core/System/Security/Cryptography/ECDsaCng.cs +++ b/System.Core/System/Security/Cryptography/ECDsaCng.cs @@ -45,12 +45,12 @@ public ECDsaCng(int keySize) { [SecuritySafeCritical] public ECDsaCng(CngKey key) { Contract.Ensures(LegalKeySizesValue != null); - Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDsa); + Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup)); if (key == null) { throw new ArgumentNullException("key"); } - if (key.AlgorithmGroup != CngAlgorithmGroup.ECDsa) { + if (!IsEccAlgorithmGroup(key.AlgorithmGroup)) { throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey), "key"); } @@ -74,7 +74,21 @@ public ECDsaCng(CngKey key) { } CodeAccessPermission.RevertAssert(); - KeySize = m_key.KeySize; + // Our LegalKeySizes value stores the values that we encoded as being the correct + // legal key size limitations for this algorithm, as documented on MSDN. + // + // But on a new OS version we might not question if our limit is accurate, or MSDN + // could have been innacurate to start with. + // + // Since the key is already loaded, we know that Windows thought it to be valid; + // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance + // check. + // + // For RSA there are known cases where this change matters. RSACryptoServiceProvider can + // create a 384-bit RSA key, which we consider too small to be legal. It can also create + // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit + // alignment requirement. (In both cases Windows loads it just fine) + KeySizeValue = m_key.KeySize; } /// @@ -103,8 +117,8 @@ public CngAlgorithm HashAlgorithm { public CngKey Key { get { Contract.Ensures(Contract.Result() != null); - Contract.Ensures(Contract.Result().AlgorithmGroup == CngAlgorithmGroup.ECDsa); - Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDsa); + Contract.Ensures(IsEccAlgorithmGroup(Contract.Result().AlgorithmGroup)); + Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup)); // If the size of the key no longer matches our stored value, then we need to replace it with // a new key of the correct size. @@ -142,9 +156,9 @@ public CngKey Key { private set { Contract.Requires(value != null); - Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDsa); + Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup)); - if (value.AlgorithmGroup != CngAlgorithmGroup.ECDsa) { + if (!IsEccAlgorithmGroup(value.AlgorithmGroup)) { throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey)); } @@ -159,7 +173,22 @@ private set { // m_key = value; - KeySize = m_key.KeySize; + + // Our LegalKeySizes value stores the values that we encoded as being the correct + // legal key size limitations for this algorithm, as documented on MSDN. + // + // But on a new OS version we might not question if our limit is accurate, or MSDN + // could have been innacurate to start with. + // + // Since the key is already loaded, we know that Windows thought it to be valid; + // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance + // check. + // + // For RSA there are known cases where this change matters. RSACryptoServiceProvider can + // create a 384-bit RSA key, which we consider too small to be legal. It can also create + // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit + // alignment requirement. (In both cases Windows loads it just fine) + KeySizeValue = m_key.KeySize; } } @@ -407,5 +436,15 @@ protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) return hasher.HashFinal(); } } + + private static bool IsEccAlgorithmGroup(CngAlgorithmGroup algorithmGroup) + { + // Sometimes, when reading from certificates, ECDSA keys get identified as ECDH. + // Windows allows the ECDH keys to perform both key exchange (ECDH) and signing (ECDSA), + // so either value is acceptable for the ECDSA wrapper object. + // + // It is worth noting, however, that ECDSA-identified keys cannot be used for key exchange (ECDH) in CNG. + return algorithmGroup == CngAlgorithmGroup.ECDsa || algorithmGroup == CngAlgorithmGroup.ECDiffieHellman; + } } } diff --git a/System.Core/System/Security/Cryptography/NCryptNative.cs b/System.Core/System/Security/Cryptography/NCryptNative.cs index cf5620911..4b7adf451 100644 --- a/System.Core/System/Security/Cryptography/NCryptNative.cs +++ b/System.Core/System/Security/Cryptography/NCryptNative.cs @@ -794,10 +794,6 @@ private static bool VerifySignature(SafeNCryptKeyHandle key, signature, signature.Length, paddingMode); - if (error != ErrorCode.Success && error != ErrorCode.BadSignature) { - throw new CryptographicException((int)error); - } - return error == ErrorCode.Success; } @@ -1692,6 +1688,55 @@ internal static byte[] SignHash(SafeNCryptKeyHandle key, byte[] hash) { return signature; } + /// + /// Sign a hash using no padding + /// + [System.Security.SecurityCritical] + internal static byte[] SignHash(SafeNCryptKeyHandle key, byte[] hash, int expectedSize) + { + Contract.Requires(key != null); + Contract.Requires(hash != null); + Contract.Ensures(Contract.Result() != null); + +#if DEBUG + expectedSize = 1; +#endif + + // Figure out how big the signature is + byte[] signature = new byte[expectedSize]; + int signatureSize = 0; + ErrorCode error = UnsafeNativeMethods.NCryptSignHash(key, + IntPtr.Zero, + hash, + hash.Length, + signature, + signature.Length, + out signatureSize, + 0); + + if (error == ErrorCode.BufferTooSmall) + { + signature = new byte[signatureSize]; + + error = UnsafeNativeMethods.NCryptSignHash(key, + IntPtr.Zero, + hash, + hash.Length, + signature, + signature.Length, + out signatureSize, + 0); + } + + if (error != ErrorCode.Success) + { + throw new CryptographicException((int)error); + } + + Array.Resize(ref signature, signatureSize); + return signature; + } + /// /// Unpack a key blob in ECC public blob format into its X and Y parameters /// @@ -1734,10 +1779,6 @@ internal static bool VerifySignature(SafeNCryptKeyHandle key, byte[] hash, byte[ signature.Length, 0); - if (error != ErrorCode.Success && error != ErrorCode.BadSignature) { - throw new CryptographicException((int)error); - } - return error == ErrorCode.Success; } } diff --git a/System.Core/System/Security/Cryptography/RsaCng.cs b/System.Core/System/Security/Cryptography/RsaCng.cs index 3085b90e2..893a79c74 100644 --- a/System.Core/System/Security/Cryptography/RsaCng.cs +++ b/System.Core/System/Security/Cryptography/RsaCng.cs @@ -89,7 +89,11 @@ public CngKey Key // If we don't have a key yet, we need to generate a random one now if (_key == null) { - CngKeyCreationParameters creationParameters = new CngKeyCreationParameters(); + CngKeyCreationParameters creationParameters = new CngKeyCreationParameters() + { + ExportPolicy = CngExportPolicies.AllowPlaintextExport, + }; + CngProperty keySizeProperty = new CngProperty(NCryptNative.KeyPropertyName.Length, BitConverter.GetBytes(KeySize), CngPropertyOptions.None); @@ -114,7 +118,22 @@ private set } _key = value; - KeySize = _key.KeySize; + + // Our LegalKeySizes value stores the values that we encoded as being the correct + // legal key size limitations for this algorithm, as documented on MSDN. + // + // But on a new OS version we might not question if our limit is accurate, or MSDN + // could have been innacurate to start with. + // + // Since the key is already loaded, we know that Windows thought it to be valid; + // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance + // check. + // + // For RSA there are known cases where this change matters. RSACryptoServiceProvider can + // create a 384-bit RSA key, which we consider too small to be legal. It can also create + // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit + // alignment requirement. (In both cases Windows loads it just fine) + KeySizeValue = _key.KeySize; } } @@ -504,5 +523,24 @@ public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode)); } } + + /* + * The members + * DecryptValue + * EncryptValue + * get_KeyExchangeAlgorithm + * get_SignatureAlgorithm + * are all implemented on RSA as of net46. + * + * But in servicing situations, System.Core.dll can get patched onto a machine which has mscorlib < net46, meaning + * these abstract members have no implementation. + * + * To keep servicing simple, we'll redefine the overrides here. Since this type is sealed it only affects reflection, + * as there are no derived types to mis-target base.-invocations. + */ + public override byte[] DecryptValue(byte[] rgb) { throw new NotSupportedException(SR.NotSupported_Method); } + public override byte[] EncryptValue(byte[] rgb) { throw new NotSupportedException(SR.NotSupported_Method); } + public override string KeyExchangeAlgorithm { get { return "RSA"; } } + public override string SignatureAlgorithm { get { return "RSA"; } } } } diff --git a/System.Core/System/Security/Cryptography/X509Certificates/RSACertificateExtensions.cs b/System.Core/System/Security/Cryptography/X509Certificates/RSACertificateExtensions.cs index e4ef01d3e..a69b7ed8a 100644 --- a/System.Core/System/Security/Cryptography/X509Certificates/RSACertificateExtensions.cs +++ b/System.Core/System/Security/Cryptography/X509Certificates/RSACertificateExtensions.cs @@ -71,8 +71,14 @@ public static RSA GetRSAPrivateKey(this X509Certificate2 certificate) { if (privateKeyHandle == null) { + if (LocalAppContextSwitches.DontReliablyClonePrivateKey) + return (RSA)certificate.PrivateKey; + // fall back to CAPI if we cannot acquire the key using CNG. - return (RSA)certificate.PrivateKey; + RSACryptoServiceProvider rsaCsp = (RSACryptoServiceProvider)certificate.PrivateKey; + CspParameters cspParameters = DSACertificateExtensions.CopyCspParameters(rsaCsp); + RSACryptoServiceProvider clone = new RSACryptoServiceProvider(cspParameters); + return clone; } CngKey key = CngKey.Open(privateKeyHandle, CngKeyHandleOpenOptions.None); diff --git a/System.Data.Entity/System/Data/Common/Utils/MetadataHelper.cs b/System.Data.Entity/System/Data/Common/Utils/MetadataHelper.cs index 1ab0fb912..8d7a31911 100644 --- a/System.Data.Entity/System/Data/Common/Utils/MetadataHelper.cs +++ b/System.Data.Entity/System/Data/Common/Utils/MetadataHelper.cs @@ -833,6 +833,8 @@ internal static string GenerateHashForAllExtentViewsContent(double schemaVersion [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Cryptographic.Standard", "CA5350:Microsoft.Cryptographic.Standard", Justification = "MD5CryptoServiceProvider is not used for cryptography/security purposes and we do it only for v1 and v1.1 for compatibility reasons.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security.Cryptography", "CA5350:Microsoft.Cryptographic.Standard", + Justification = "MD5CryptoServiceProvider is not used for cryptography/security purposes and we do it only for v1 and v1.1 for compatibility reasons.")] internal static HashAlgorithm CreateMetadataHashAlgorithm(double schemaVersion) { HashAlgorithm hashAlgorithm; diff --git a/System.Data.Entity/System/Data/EntityModel/SchemaObjectModel/Schema.cs b/System.Data.Entity/System/Data/EntityModel/SchemaObjectModel/Schema.cs index a0388683a..adb7b34c7 100644 --- a/System.Data.Entity/System/Data/EntityModel/SchemaObjectModel/Schema.cs +++ b/System.Data.Entity/System/Data/EntityModel/SchemaObjectModel/Schema.cs @@ -1253,6 +1253,7 @@ private static XmlSchemaSet ComputeSchemaSet(SchemaDataModelOption dataModel) return schemaSet; } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security.Xml", "CA3060:UseXmlReaderForSchemaRead", Justification = "The schema files are embedded in the product assembly as resources")] private static void AddXmlSchemaToSet(XmlSchemaSet schemaSet, XmlSchemaResource schemaResource, HashSet schemasAlreadyAdded) { // loop through the children to do a depth first load diff --git a/System.Data.SqlXml/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs b/System.Data.SqlXml/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs index e1c869221..f671178fc 100644 --- a/System.Data.SqlXml/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs +++ b/System.Data.SqlXml/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs @@ -2289,8 +2289,8 @@ protected override QilNode VisitFilter(QilLoop local0) { if (local3.NodeType == QilNodeType.Loop) { QilNode local4 = local3[0]; QilNode local5 = local3[1]; - if ( NonPositional(local2, local1) ) { - // PATTERN: [CommuteFilterLoop] (Filter $iter:(For (Loop $iter2:* $ret2:*)) $cond:* ^ (NonPositional? $cond $iter)) => (Loop $iter2 (Filter $iter3:(For $ret2) (Subs $cond $iter $iter3))) + if (( NonPositional(local2, local1) ) && (!( IsDocOrderDistinct(local3) ))) { + // PATTERN: [CommuteFilterLoop] (Filter $iter:(For $loop:(Loop $iter2:* $ret2:*)) $cond:* ^ (NonPositional? $cond $iter) ^ ~((DocOrderDistinct? $loop))) => (Loop $iter2 (Filter $iter3:(For $ret2) (Subs $cond $iter $iter3))) if (AllowReplace(XmlILOptimization.CommuteFilterLoop, local0)) { QilNode local6 = VisitFor(f.For(local5)); return Replace(XmlILOptimization.CommuteFilterLoop, local0, VisitLoop(f.Loop(local4, VisitFilter(f.Filter(local6, Subs(local2, local1, local6) ))))); diff --git a/System.Data/System/Data/Common/DbConnectionStringCommon.cs b/System.Data/System/Data/Common/DbConnectionStringCommon.cs index e1981b6b9..24be05dfc 100644 --- a/System.Data/System/Data/Common/DbConnectionStringCommon.cs +++ b/System.Data/System/Data/Common/DbConnectionStringCommon.cs @@ -323,6 +323,143 @@ internal static string ConvertToString(object value) } } + #region <> + const string PoolBlockingPeriodAutoString = "Auto"; + const string PoolBlockingPeriodAlwaysBlockString = "AlwaysBlock"; + const string PoolBlockingPeriodNeverBlockString = "NeverBlock"; + + internal static bool TryConvertToPoolBlockingPeriod(string value, out PoolBlockingPeriod result) + { + Debug.Assert(Enum.GetNames(typeof(PoolBlockingPeriod)).Length == 3, "PoolBlockingPeriod enum has changed, update needed"); + Debug.Assert(null != value, "TryConvertToPoolBlockingPeriod(null,...)"); + + if (StringComparer.OrdinalIgnoreCase.Equals(value, PoolBlockingPeriodAutoString)) + { + result = PoolBlockingPeriod.Auto; + return true; + } + else if (StringComparer.OrdinalIgnoreCase.Equals(value, PoolBlockingPeriodAlwaysBlockString)) + { + result = PoolBlockingPeriod.AlwaysBlock; + return true; + } + else if (StringComparer.OrdinalIgnoreCase.Equals(value, PoolBlockingPeriodNeverBlockString)) + { + result = PoolBlockingPeriod.NeverBlock; + return true; + } + else + { + result = DbConnectionStringDefaults.PoolBlockingPeriod; + return false; + } + } + + internal static bool IsValidPoolBlockingPeriodValue(PoolBlockingPeriod value) + { + Debug.Assert(Enum.GetNames(typeof(PoolBlockingPeriod)).Length == 3, "PoolBlockingPeriod enum has changed, update needed"); + return value == PoolBlockingPeriod.Auto || value == PoolBlockingPeriod.AlwaysBlock || value == PoolBlockingPeriod.NeverBlock; + } + + internal static string PoolBlockingPeriodToString(PoolBlockingPeriod value) + { + Debug.Assert(IsValidPoolBlockingPeriodValue(value)); + + if (value == PoolBlockingPeriod.AlwaysBlock) + { + return PoolBlockingPeriodAlwaysBlockString; + } + if (value == PoolBlockingPeriod.NeverBlock) + { + return PoolBlockingPeriodNeverBlockString; + } + else + { + return PoolBlockingPeriodAutoString; + } + } + + /// + /// This method attempts to convert the given value to a PoolBlockingPeriod enum. The algorithm is: + /// * if the value is from type string, it will be matched against PoolBlockingPeriod enum names only, using ordinal, case-insensitive comparer + /// * if the value is from type PoolBlockingPeriod, it will be used as is + /// * if the value is from integral type (SByte, Int16, Int32, Int64, Byte, UInt16, UInt32, or UInt64), it will be converted to enum + /// * if the value is another enum or any other type, it will be blocked with an appropriate ArgumentException + /// + /// in any case above, if the conerted value is out of valid range, the method raises ArgumentOutOfRangeException. + /// + /// PoolBlockingPeriod value in the valid range + internal static PoolBlockingPeriod ConvertToPoolBlockingPeriod(string keyword, object value) + { + Debug.Assert(null != value, "ConvertToPoolBlockingPeriod(null)"); + string sValue = (value as string); + PoolBlockingPeriod result; + if (null != sValue) + { + // We could use Enum.TryParse here, but it accepts value combinations like + // "ReadOnly, ReadWrite" which are unwelcome here + // Also, Enum.TryParse is 100x slower than plain StringComparer.OrdinalIgnoreCase.Equals method. + + if (TryConvertToPoolBlockingPeriod(sValue, out result)) + { + return result; + } + + // try again after remove leading & trailing whitespaces. + sValue = sValue.Trim(); + if (TryConvertToPoolBlockingPeriod(sValue, out result)) + { + return result; + } + + // string values must be valid + throw ADP.InvalidConnectionOptionValue(keyword); + } + else + { + // the value is not string, try other options + PoolBlockingPeriod eValue; + + if (value is PoolBlockingPeriod) + { + // quick path for the most common case + eValue = (PoolBlockingPeriod)value; + } + else if (value.GetType().IsEnum) + { + // explicitly block scenarios in which user tries to use wrong enum types, like: + // builder["PoolBlockingPeriod"] = EnvironmentVariableTarget.Process; + // workaround: explicitly cast non-PoolBlockingPeriod enums to int + throw ADP.ConvertFailed(value.GetType(), typeof(PoolBlockingPeriod), null); + } + else + { + try + { + // Enum.ToObject allows only integral and enum values (enums are blocked above), rasing ArgumentException for the rest + eValue = (PoolBlockingPeriod)Enum.ToObject(typeof(PoolBlockingPeriod), value); + } + catch (ArgumentException e) + { + // to be consistent with the messages we send in case of wrong type usage, replace + // the error with our exception, and keep the original one as inner one for troubleshooting + throw ADP.ConvertFailed(value.GetType(), typeof(PoolBlockingPeriod), e); + } + } + + // ensure value is in valid range + if (IsValidPoolBlockingPeriodValue(eValue)) + { + return eValue; + } + else + { + throw ADP.InvalidEnumerationValue(typeof(ApplicationIntent), (int)eValue); + } + } + } + #endregion + const string ApplicationIntentReadWriteString = "ReadWrite"; const string ApplicationIntentReadOnlyString = "ReadOnly"; @@ -752,6 +889,7 @@ internal static class DbConnectionStringDefaults { internal const int ConnectRetryInterval = 10; internal static readonly SqlAuthenticationMethod Authentication = SqlAuthenticationMethod.NotSpecified; internal static readonly SqlConnectionColumnEncryptionSetting ColumnEncryptionSetting = SqlConnectionColumnEncryptionSetting.Disabled; + internal const PoolBlockingPeriod PoolBlockingPeriod = SqlClient.PoolBlockingPeriod.Auto; } internal static class DbConnectionOptionKeywords { @@ -819,6 +957,7 @@ internal static class DbConnectionStringKeywords { internal const string Authentication = "Authentication"; internal const string Certificate = "Certificate"; internal const string ColumnEncryptionSetting = "Column Encryption Setting"; + internal const string PoolBlockingPeriod = "PoolBlockingPeriod"; // common keywords (OleDb, OracleClient, SqlClient) internal const string DataSource = "Data Source"; diff --git a/System.Data/System/Data/DataSet.cs b/System.Data/System/Data/DataSet.cs index 504cdd268..b9f0af4ca 100644 --- a/System.Data/System/Data/DataSet.cs +++ b/System.Data/System/Data/DataSet.cs @@ -2120,7 +2120,12 @@ public XmlReadMode ReadXml(Stream stream) if (stream == null) return XmlReadMode.Auto; - return ReadXml(new XmlTextReader(stream), false); + XmlTextReader xr = new XmlTextReader(stream); + + // Prevent Dtd entity in dataset + xr.XmlResolver = null; + + return ReadXml(xr, false); } /// @@ -2130,7 +2135,12 @@ public XmlReadMode ReadXml(TextReader reader) if (reader == null) return XmlReadMode.Auto; - return ReadXml(new XmlTextReader(reader), false); + XmlTextReader xr = new XmlTextReader(reader); + + // Prevent Dtd entity in dataset + xr.XmlResolver = null; + + return ReadXml(xr, false); } /// @@ -2140,7 +2150,12 @@ public XmlReadMode ReadXml(TextReader reader) public XmlReadMode ReadXml(string fileName) { XmlTextReader xr = new XmlTextReader(fileName); - try { + + // Prevent Dtd entity in dataset + xr.XmlResolver = null; + + try + { return ReadXml(xr, false); } finally { @@ -2518,6 +2533,8 @@ public XmlReadMode ReadXml(Stream stream, XmlReadMode mode) return XmlReadMode.Auto; XmlTextReader reader = (mode == XmlReadMode.Fragment) ? new XmlTextReader(stream, XmlNodeType.Element, null) : new XmlTextReader(stream); + // Prevent Dtd entity in dataset + reader.XmlResolver = null; return ReadXml(reader, mode, false); } @@ -2529,6 +2546,8 @@ public XmlReadMode ReadXml(TextReader reader, XmlReadMode mode) return XmlReadMode.Auto; XmlTextReader xmlreader = (mode == XmlReadMode.Fragment) ? new XmlTextReader(reader.ReadToEnd(), XmlNodeType.Element, null) : new XmlTextReader(reader); + // Prevent Dtd entity in dataset + xmlreader.XmlResolver = null; return ReadXml(xmlreader, mode, false); } @@ -2545,7 +2564,12 @@ public XmlReadMode ReadXml(string fileName, XmlReadMode mode) } else xr = new XmlTextReader(fileName); - try { + + // Prevent Dtd entity in dataset + xr.XmlResolver = null; + + try + { return ReadXml(xr, mode, false); } finally { diff --git a/System.Data/System/Data/DataTable.cs b/System.Data/System/Data/DataTable.cs index 637816ef0..24673fa5c 100644 --- a/System.Data/System/Data/DataTable.cs +++ b/System.Data/System/Data/DataTable.cs @@ -5044,7 +5044,12 @@ public XmlReadMode ReadXml(Stream stream) if (stream == null) return XmlReadMode.Auto; - return ReadXml( new XmlTextReader(stream), false); + XmlTextReader xr = new XmlTextReader(stream); + + // Prevent Dtd entity in DataTable + xr.XmlResolver = null; + + return ReadXml(xr, false); } public XmlReadMode ReadXml(TextReader reader) @@ -5052,7 +5057,12 @@ public XmlReadMode ReadXml(TextReader reader) if (reader == null) return XmlReadMode.Auto; - return ReadXml( new XmlTextReader(reader), false); + XmlTextReader xr = new XmlTextReader(reader); + + // Prevent Dtd entity in DataTable + xr.XmlResolver = null; + + return ReadXml(xr, false); } [ResourceExposure(ResourceScope.Machine)] @@ -5060,7 +5070,12 @@ public XmlReadMode ReadXml(TextReader reader) public XmlReadMode ReadXml(string fileName) { XmlTextReader xr = new XmlTextReader(fileName); - try { + + // Prevent Dtd entity in DataTable + xr.XmlResolver = null; + + try + { return ReadXml( xr , false); } finally { diff --git a/System.Data/System/Data/ProviderBase/DbConnectionPool.cs b/System.Data/System/Data/ProviderBase/DbConnectionPool.cs index 6c87c0229..f9fcefa74 100644 --- a/System.Data/System/Data/ProviderBase/DbConnectionPool.cs +++ b/System.Data/System/Data/ProviderBase/DbConnectionPool.cs @@ -12,6 +12,7 @@ namespace System.Data.ProviderBase { using System.Collections; using System.Collections.Generic; using System.Data.Common; + using System.Data.SqlClient; using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; @@ -747,7 +748,81 @@ internal void Clear() { private Timer CreateCleanupTimer() { return (new Timer(new TimerCallback(this.CleanupCallback), null, _cleanupWait, _cleanupWait)); } - + + private static readonly string[] AzureSqlServerEndpoints = {Res.GetString(Res.AZURESQL_GenericEndpoint), + Res.GetString(Res.AZURESQL_GermanEndpoint), + Res.GetString(Res.AZURESQL_UsGovEndpoint), + Res.GetString(Res.AZURESQL_ChinaEndpoint) }; + private static bool IsAzureSqlServerEndpoint(string dataSource) + { + // remove server port + var i = dataSource.LastIndexOf(','); + if (i >= 0) + { + dataSource = dataSource.Substring(0, i); + } + + // check for the instance name + i = dataSource.LastIndexOf('\\'); + if (i >= 0) + { + dataSource = dataSource.Substring(0, i); + } + + // trim redundant whitespaces + dataSource = dataSource.Trim(); + + // check if servername end with any azure endpoints + for (i = 0; i < AzureSqlServerEndpoints.Length; i++) + { + if (dataSource.EndsWith(AzureSqlServerEndpoints[i], StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + return false; + } + + private bool IsBlockingPeriodEnabled() + { + var poolGroupConnectionOptions = _connectionPoolGroup.ConnectionOptions as SqlConnectionString; + if (poolGroupConnectionOptions == null) + { + return true; + } + + var policy = poolGroupConnectionOptions.PoolBlockingPeriod; + + switch (policy) + { + case PoolBlockingPeriod.Auto: + { + if (IsAzureSqlServerEndpoint(poolGroupConnectionOptions.DataSource)) + { + return false; // in Azure it will be Disabled + } + else + { + return true; // in Non Azure, it will be Enabled + } + } + case PoolBlockingPeriod.AlwaysBlock: + { + return true; //Enabled + } + case PoolBlockingPeriod.NeverBlock: + { + return false; //Disabled + } + default: + { + //we should never get into this path. + Debug.Fail("Unknown PoolBlockingPeriod. Please specify explicit results in above switch case statement."); + return true; + } + } + } + private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) { DbConnectionInternal newObj = null; @@ -786,7 +861,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio // Reset the error wait: _errorWait = ERROR_WAIT_DEFAULT; } - catch(Exception e) { + catch(Exception e) { // if (!ADP.IsCatchableExceptionType(e)) { throw; @@ -794,6 +869,11 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio ADP.TraceExceptionForCapture(e); + if (!IsBlockingPeriodEnabled()) + { + throw; + } + newObj = null; // set to null, so we do not return bad new object // Failed to create instance _resError = e; diff --git a/System.Data/System/Data/SqlClient/SqlClientSymmetricKey.cs b/System.Data/System/Data/SqlClient/SqlClientSymmetricKey.cs index 112108ae1..501ed7c91 100644 --- a/System.Data/System/Data/SqlClient/SqlClientSymmetricKey.cs +++ b/System.Data/System/Data/SqlClient/SqlClientSymmetricKey.cs @@ -15,10 +15,9 @@ namespace System.Data.SqlClient /// Base class containing raw key bytes for symmetric key algorithms. Some encryption algorithms can use the key directly while others derive sub keys from this. /// If an algorithm needs to derive more keys, have a derived class from this and use it in the corresponding encryption algorithm. /// - internal class SqlClientSymmetricKey - { + internal class SqlClientSymmetricKey { /// - /// DPAPI protected key + /// The underlying key material /// protected readonly byte[] _rootKey; @@ -26,8 +25,7 @@ internal class SqlClientSymmetricKey /// Constructor that initializes the root key. /// /// root key - internal SqlClientSymmetricKey(byte[] rootKey) - { + internal SqlClientSymmetricKey(byte[] rootKey) { // Key validation if (rootKey == null || rootKey.Length == 0) { throw SQL.NullColumnEncryptionKeySysErr(); @@ -36,14 +34,24 @@ internal SqlClientSymmetricKey(byte[] rootKey) _rootKey = rootKey; } + /// + /// Destructor that cleans up the key material. + /// This is a best effort approach since there are no guarantees around GC. + /// + ~SqlClientSymmetricKey() { + if (_rootKey != null) { + for (int i = 0; i < _rootKey.Length; i++) { + _rootKey[i] = 0; + } + } + } + /// /// Returns a copy of the plain text key /// This is needed for actual encryption/decryption. /// - internal virtual byte[] RootKey - { - get - { + internal virtual byte[] RootKey { + get { return _rootKey; } } @@ -52,8 +60,7 @@ internal virtual byte[] RootKey /// Computes SHA256 value of the plain text key bytes /// /// A string containing SHA256 hash of the root key - internal virtual string GetKeyHash() - { + internal virtual string GetKeyHash() { return SqlSecurityUtility.GetSHA256Hash(RootKey); } @@ -63,10 +70,7 @@ internal virtual string GetKeyHash() /// /// Returns the length of the root key /// - internal virtual int Length() - { - // Note: DPAPI preserves the original byte length - // so for now, this is as same as returning the length of the raw key. + internal virtual int Length() { return _rootKey.Length; } } diff --git a/System.Data/System/Data/SqlClient/SqlCommand.cs b/System.Data/System/Data/SqlClient/SqlCommand.cs index 2e560dbf3..46a535370 100644 --- a/System.Data/System/Data/SqlClient/SqlCommand.cs +++ b/System.Data/System/Data/SqlClient/SqlCommand.cs @@ -81,7 +81,12 @@ public sealed class SqlCommand : DbCommand, ICloneable { /// Force the client to sleep during sp_describe_parameter_encryption after ReadDescribeEncryptionParameterResults. /// private static bool _sleepAfterReadDescribeEncryptionParameterResults = false; -#endif + + /// + /// Internal flag for testing purposes that forces all queries to internally end async calls. + /// + private static bool _forceInternalEndQuery = false; +#endif // devnote: Prepare // Against 7.0 Server (Sphinx) a prepare/unprepare requires an extra roundtrip to the server. @@ -287,6 +292,16 @@ internal bool IsDescribeParameterEncryptionRPCCurrentlyInProgress { } } + /// + /// A flag to indicate if EndExecute was already initiated by the Begin call. + /// + private volatile bool _internalEndExecuteInitiated; + + /// + /// A flag to indicate whether we postponed caching the query metadata for this command. + /// + internal bool CachingQueryMetadataPostponed { get; set; } + // // Smi execution-specific stuff // @@ -1170,7 +1185,8 @@ override public int ExecuteNonQuery() { try { statistics = SqlStatistics.StartTimer(Statistics); WriteBeginExecuteEvent(); - InternalExecuteNonQuery(null, ADP.ExecuteNonQuery, false, CommandTimeout); + bool usedCache; + InternalExecuteNonQuery(null, ADP.ExecuteNonQuery, false, CommandTimeout, out usedCache); success = true; return _rowsAffected; } @@ -1199,7 +1215,8 @@ internal void ExecuteToPipe( SmiContext pipeContext ) { Bid.ScopeEnter(out hscp, " %d#", ObjectID); try { statistics = SqlStatistics.StartTimer(Statistics); - InternalExecuteNonQuery(null, ADP.ExecuteNonQuery, true, CommandTimeout); + bool usedCache; + InternalExecuteNonQuery(null, ADP.ExecuteNonQuery, true, CommandTimeout, out usedCache); } finally { SqlStatistics.StopTimer(statistics); @@ -1217,34 +1234,41 @@ public IAsyncResult BeginExecuteNonQuery() { public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObject) { Bid.CorrelationTrace(" ObjectID%d#, ActivityID %ls\n", ObjectID); SqlConnection.ExecutePermission.Demand(); - return BeginExecuteNonQueryInternal(callback, stateObject, 0); + return BeginExecuteNonQueryInternal(0, callback, stateObject, 0, inRetry: false); } private IAsyncResult BeginExecuteNonQueryAsync(AsyncCallback callback, object stateObject) { - return BeginExecuteNonQueryInternal(callback, stateObject, CommandTimeout, asyncWrite:true); + return BeginExecuteNonQueryInternal(0, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite:true); } - private IAsyncResult BeginExecuteNonQueryInternal(AsyncCallback callback, object stateObject, int timeout, bool asyncWrite = false) { - // Reset _pendingCancel upon entry into any Execute - used to synchronize state - // between entry into Execute* API and the thread obtaining the stateObject. - _pendingCancel = false; + private IAsyncResult BeginExecuteNonQueryInternal(CommandBehavior behavior, AsyncCallback callback, object stateObject, int timeout, bool inRetry, bool asyncWrite = false) { + TaskCompletionSource globalCompletion = new TaskCompletionSource(stateObject); + TaskCompletionSource localCompletion = new TaskCompletionSource(stateObject); + + if (!inRetry) { + // Reset _pendingCancel upon entry into any Execute - used to synchronize state + // between entry into Execute* API and the thread obtaining the stateObject. + _pendingCancel = false; + + ValidateAsyncCommand(); // Special case - done outside of try/catches to prevent putting a stateObj + // back into pool when we should not. + } - ValidateAsyncCommand(); // Special case - done outside of try/catches to prevent putting a stateObj - // back into pool when we should not. - SqlStatistics statistics = null; try { - statistics = SqlStatistics.StartTimer(Statistics); - WriteBeginExecuteEvent(); - TaskCompletionSource completion = new TaskCompletionSource(stateObject); + if (!inRetry) { + statistics = SqlStatistics.StartTimer(Statistics); + WriteBeginExecuteEvent(); + } + bool usedCache; try { // InternalExecuteNonQuery already has reliability block, but if failure will not put stateObj back into pool. - Task execNQ = InternalExecuteNonQuery(completion, ADP.BeginExecuteNonQuery, false, timeout, asyncWrite); + Task execNQ = InternalExecuteNonQuery(localCompletion, ADP.BeginExecuteNonQuery, false, timeout, out usedCache, asyncWrite, inRetry: inRetry); if (execNQ != null) { - AsyncHelper.ContinueTask(execNQ, completion, () => BeginExecuteNonQueryInternalReadStage(completion)); + AsyncHelper.ContinueTask(execNQ, localCompletion, () => BeginExecuteNonQueryInternalReadStage(localCompletion)); } else { - BeginExecuteNonQueryInternalReadStage(completion); + BeginExecuteNonQueryInternalReadStage(localCompletion); } } catch (Exception e) { @@ -1258,12 +1282,18 @@ private IAsyncResult BeginExecuteNonQueryInternal(AsyncCallback callback, object throw; } + // When we use query caching for parameter encryption we need to retry on specific errors. + // In these cases finalize the call internally and trigger a retry when needed. + if (!TriggerInternalEndAndRetryIfNecessary(behavior, stateObject, timeout, ADP.EndExecuteNonQuery, usedCache, inRetry, asyncWrite, globalCompletion, localCompletion, InternalEndExecuteNonQuery, BeginExecuteNonQueryInternal)) { + globalCompletion = localCompletion; + } + // Add callback after work is done to avoid overlapping Begin\End methods if (callback != null) { - completion.Task.ContinueWith((t) => callback(t), TaskScheduler.Default); + globalCompletion.Task.ContinueWith((t) => callback(t), TaskScheduler.Default); } - return completion.Task; + return globalCompletion.Task; } finally { SqlStatistics.StopTimer(statistics); @@ -1319,7 +1349,7 @@ private void BeginExecuteNonQueryInternalReadStage(TaskCompletionSource } } - private void VerifyEndExecuteState(Task completionTask, String endMethod) { + private void VerifyEndExecuteState(Task completionTask, String endMethod, bool fullCheckForColumnEncryption = false) { if (null == completionTask) { throw ADP.ArgumentNull("asyncResult"); } @@ -1340,7 +1370,7 @@ private void VerifyEndExecuteState(Task completionTask, String endMethod) { // If transparent parameter encryption was attempted, then we need to skip other checks like those on EndMethodName // since we want to wait for async results before checking those fields. - if (IsColumnEncryptionEnabled) { + if (IsColumnEncryptionEnabled && !fullCheckForColumnEncryption) { if (_activeConnection.State != ConnectionState.Open) { // If the connection is not 'valid' then it was closed while we were executing throw ADP.ClosedConnectionError(); @@ -1361,13 +1391,24 @@ private void VerifyEndExecuteState(Task completionTask, String endMethod) { } } - private void WaitForAsyncResults(IAsyncResult asyncResult) { + private void WaitForAsyncResults(IAsyncResult asyncResult, bool isInternal) { Task completionTask = (Task) asyncResult; if (!asyncResult.IsCompleted) { asyncResult.AsyncWaitHandle.WaitOne(); } - _stateObj._networkPacketTaskSource = null; - _activeConnection.GetOpenTdsConnection().DecrementAsyncCount(); + + if (_stateObj != null) { + _stateObj._networkPacketTaskSource = null; + } + + // If this is an internal command we will decrement the count when the End method is actually called by the user. + // If we are using Column Encryption and the previous task failed, the async count should have already been fixed up. + // There is a generic issue in how we handle the async count because: + // a) BeginExecute might or might not clean it up on failure. + // b) In EndExecute, we check the task state before waiting and throw if it's failed, whereas if we wait we will always adjust the count. + if (!isInternal && (!IsColumnEncryptionEnabled || !completionTask.IsFaulted)) { + _activeConnection.GetOpenTdsConnection().DecrementAsyncCount(); + } } public int EndExecuteNonQuery(IAsyncResult asyncResult) { @@ -1390,6 +1431,7 @@ private void ThrowIfReconnectionHasBeenCanceled() { private int EndExecuteNonQueryAsync(IAsyncResult asyncResult) { Bid.CorrelationTrace(" ObjectID%d#, ActivityID %ls\n", ObjectID); + Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; if (asyncException != null) { @@ -1400,7 +1442,13 @@ private int EndExecuteNonQueryAsync(IAsyncResult asyncResult) { else { ThrowIfReconnectionHasBeenCanceled(); // lock on _stateObj prevents ----s with close/cancel. - lock (_stateObj) { + // If we have already initiate the End call internally, we have already done that, so no point doing it again. + if (!_internalEndExecuteInitiated) { + lock (_stateObj) { + return EndExecuteNonQueryInternal(asyncResult); + } + } + else { return EndExecuteNonQueryInternal(asyncResult); } } @@ -1408,11 +1456,43 @@ private int EndExecuteNonQueryAsync(IAsyncResult asyncResult) { private int EndExecuteNonQueryInternal(IAsyncResult asyncResult) { SqlStatistics statistics = null; + bool success = false; + int? sqlExceptionNumber = null; + try { + statistics = SqlStatistics.StartTimer(Statistics); + int result = (int)InternalEndExecuteNonQuery(asyncResult, ADP.EndExecuteNonQuery, isInternal: false); + success = true; + return result; + } + catch (SqlException e) { + sqlExceptionNumber = e.Number; + if (cachedAsyncState != null) { + cachedAsyncState.ResetAsyncState(); + }; + // SqlException is always catchable + ReliablePutStateObject(); + throw; + } + catch (Exception e) { + if (cachedAsyncState != null) { + cachedAsyncState.ResetAsyncState(); + }; + if (ADP.IsCatchableExceptionType(e)) { + ReliablePutStateObject(); + }; + throw; + } + finally { + SqlStatistics.StopTimer(statistics); + WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: false); + } + } + + private object InternalEndExecuteNonQuery(IAsyncResult asyncResult, string endMethod, bool isInternal) { TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); - bool success = false; - int? sqlExceptionNumber = null; + try { #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); @@ -1424,30 +1504,32 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult) { { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); - statistics = SqlStatistics.StartTimer(Statistics); - VerifyEndExecuteState((Task)asyncResult, ADP.EndExecuteNonQuery); - WaitForAsyncResults(asyncResult); + VerifyEndExecuteState((Task)asyncResult, endMethod); + WaitForAsyncResults(asyncResult, isInternal); - // If Transparent parameter encryption was attempted, then we would have skipped the below - // checks in VerifyEndExecuteState since we wanted to wait for WaitForAsyncResults to complete. + // If column encryption is enabled, also check the state after waiting for the task. + // It would be better to do this for all cases, but avoiding for compatibility reasons. if (IsColumnEncryptionEnabled) { - if (cachedAsyncState.EndMethodName == null) { - throw ADP.MethodCalledTwice(ADP.EndExecuteNonQuery); - } - - if (ADP.EndExecuteNonQuery != cachedAsyncState.EndMethodName) { - throw ADP.MismatchedAsyncResult(cachedAsyncState.EndMethodName, ADP.EndExecuteNonQuery); - } - - if (!cachedAsyncState.IsActiveConnectionValid(_activeConnection)) { - // If the connection is not 'valid' then it was closed while we were executing - throw ADP.ClosedConnectionError(); - } + VerifyEndExecuteState((Task)asyncResult, endMethod, fullCheckForColumnEncryption: true); } bool processFinallyBlock = true; try { - NotifyDependency(); + // If this is not for internal usage, notify the dependency. + // If we have already initiated the end internally, the reader should be ready, so just return the rows affected. + if (!isInternal) { + NotifyDependency(); + + if (_internalEndExecuteInitiated) { + Debug.Assert(_stateObj == null); + + // Reset the state since we exit early. + cachedAsyncState.ResetAsyncState(); + + return _rowsAffected; + } + } + CheckThrowSNIException(); // only send over SQL Batch command if we are not a stored proc and have no parameters @@ -1459,20 +1541,19 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult) { if (!result) { throw SQL.SynchronousCallMayNotPend(); } } finally { - cachedAsyncState.ResetAsyncState(); + // Don't reset the state for internal End. The user End will do that eventually. + if (!isInternal) { + cachedAsyncState.ResetAsyncState(); + } } } else { // otherwise, use a full-fledged execute that can handle params and stored procs - SqlDataReader reader = CompleteAsyncExecuteReader(); + SqlDataReader reader = CompleteAsyncExecuteReader(isInternal); if (null != reader) { reader.Close(); } } } - catch (SqlException e) { - sqlExceptionNumber = e.Number; - throw; - } catch (Exception e) { processFinallyBlock = ADP.IsCatchableExceptionType(e); throw; @@ -1484,7 +1565,6 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult) { } Debug.Assert(null == _stateObj, "non-null state object in EndExecuteNonQuery"); - success = true; return _rowsAffected; } #if DEBUG @@ -1506,23 +1586,11 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult) { SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); throw; } - catch (Exception e) { - if (cachedAsyncState != null) { - cachedAsyncState.ResetAsyncState(); - }; - if (ADP.IsCatchableExceptionType(e)) { - ReliablePutStateObject(); - }; - throw; - } - finally { - SqlStatistics.StopTimer(statistics); - WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: false); - } } - private Task InternalExecuteNonQuery(TaskCompletionSource completion, string methodName, bool sendToPipe, int timeout, bool asyncWrite = false) { + private Task InternalExecuteNonQuery(TaskCompletionSource completion, string methodName, bool sendToPipe, int timeout, out bool usedCache, bool asyncWrite = false, bool inRetry = false) { bool async = (null != completion); + usedCache = false; SqlStatistics statistics = Statistics; _rowsAffected = -1; @@ -1542,7 +1610,9 @@ private Task InternalExecuteNonQuery(TaskCompletionSource completion, st bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); // @devnote: this function may throw for an invalid connection // @devnote: returns false for empty command text - ValidateCommand(methodName, async); + if (!inRetry) { + ValidateCommand(methodName, async); + } CheckNotificationStateAndAutoEnlist(); // Only call after validate - requires non null connection! Task task = null; @@ -1566,12 +1636,15 @@ private Task InternalExecuteNonQuery(TaskCompletionSource completion, st } } + // We should never get here for a retry since we only have retries for parameters. + Debug.Assert(!inRetry); + task = RunExecuteNonQueryTds(methodName, async, timeout, asyncWrite); } else { // otherwise, use a full-fledged execute that can handle params and stored procs Debug.Assert( !sendToPipe, "trying to send non-context command to pipe" ); Bid.Trace(" %d#, Command executed as RPC.\n", ObjectID); - SqlDataReader reader = RunExecuteReader(0, RunBehavior.UntilDone, false, methodName, completion, timeout, out task, asyncWrite); + SqlDataReader reader = RunExecuteReader(0, RunBehavior.UntilDone, false, methodName, completion, timeout, out task, out usedCache, asyncWrite, inRetry); if (null!=reader) { if (task != null) { task = AsyncHelper.CreateContinuationTask(task, () => reader.Close()); @@ -1649,31 +1722,38 @@ public IAsyncResult BeginExecuteXmlReader() { [System.Security.Permissions.HostProtectionAttribute(ExternalThreading=true)] public IAsyncResult BeginExecuteXmlReader(AsyncCallback callback, object stateObject) { Bid.CorrelationTrace(" ObjectID%d#, ActivityID %ls\n", ObjectID); - SqlConnection.ExecutePermission.Demand(); - return BeginExecuteXmlReaderInternal(callback, stateObject, 0); + SqlConnection.ExecutePermission.Demand(); + return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, 0, inRetry: false); } private IAsyncResult BeginExecuteXmlReaderAsync(AsyncCallback callback, object stateObject) { - return BeginExecuteXmlReaderInternal(callback, stateObject, CommandTimeout, asyncWrite:true); + return BeginExecuteXmlReaderInternal(CommandBehavior.SequentialAccess, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite: true); } - private IAsyncResult BeginExecuteXmlReaderInternal(AsyncCallback callback, object stateObject, int timeout, bool asyncWrite = false) { - // Reset _pendingCancel upon entry into any Execute - used to synchronize state - // between entry into Execute* API and the thread obtaining the stateObject. - _pendingCancel = false; + private IAsyncResult BeginExecuteXmlReaderInternal(CommandBehavior behavior, AsyncCallback callback, object stateObject, int timeout, bool inRetry, bool asyncWrite = false) { + TaskCompletionSource globalCompletion = new TaskCompletionSource(stateObject); + TaskCompletionSource localCompletion = new TaskCompletionSource(stateObject); - ValidateAsyncCommand(); // Special case - done outside of try/catches to prevent putting a stateObj - // back into pool when we should not. + if (!inRetry) { + // Reset _pendingCancel upon entry into any Execute - used to synchronize state + // between entry into Execute* API and the thread obtaining the stateObject. + _pendingCancel = false; + + ValidateAsyncCommand(); // Special case - done outside of try/catches to prevent putting a stateObj + // back into pool when we should not. + } SqlStatistics statistics = null; try { - statistics = SqlStatistics.StartTimer(Statistics); - WriteBeginExecuteEvent(); - TaskCompletionSource completion = new TaskCompletionSource(stateObject); + if (!inRetry) { + statistics = SqlStatistics.StartTimer(Statistics); + WriteBeginExecuteEvent(); + } + bool usedCache; Task writeTask; try { // InternalExecuteNonQuery already has reliability block, but if failure will not put stateObj back into pool. - RunExecuteReader(CommandBehavior.SequentialAccess, RunBehavior.ReturnImmediately, true, ADP.BeginExecuteXmlReader, completion, timeout, out writeTask, asyncWrite); + RunExecuteReader(behavior, RunBehavior.ReturnImmediately, true, ADP.BeginExecuteXmlReader, localCompletion, timeout, out writeTask, out usedCache, asyncWrite, inRetry); } catch (Exception e) { if (!ADP.IsCatchableOrSecurityExceptionType(e)) { @@ -1687,17 +1767,23 @@ private IAsyncResult BeginExecuteXmlReaderInternal(AsyncCallback callback, objec } if (writeTask != null) { - AsyncHelper.ContinueTask(writeTask, completion, () => BeginExecuteXmlReaderInternalReadStage(completion)); + AsyncHelper.ContinueTask(writeTask, localCompletion, () => BeginExecuteXmlReaderInternalReadStage(localCompletion)); } else { - BeginExecuteXmlReaderInternalReadStage(completion); + BeginExecuteXmlReaderInternalReadStage(localCompletion); + } + + // When we use query caching for parameter encryption we need to retry on specific errors. + // In these cases finalize the call internally and trigger a retry when needed. + if (!TriggerInternalEndAndRetryIfNecessary(behavior, stateObject, timeout, ADP.EndExecuteXmlReader, usedCache, inRetry, asyncWrite, globalCompletion, localCompletion, InternalEndExecuteReader, BeginExecuteXmlReaderInternal)) { + globalCompletion = localCompletion; } // Add callback after work is done to avoid overlapping Begin\End methods if (callback != null) { - completion.Task.ContinueWith((t) => callback(t), TaskScheduler.Default); + globalCompletion.Task.ContinueWith((t) => callback(t), TaskScheduler.Default); } - return completion.Task; + return globalCompletion.Task; } finally { SqlStatistics.StopTimer(statistics); @@ -1768,6 +1854,7 @@ public XmlReader EndExecuteXmlReader(IAsyncResult asyncResult) { private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult) { Bid.CorrelationTrace(" ObjectID%d#, ActivityID %ls\n", ObjectID); + Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; if (asyncException != null) { @@ -1778,7 +1865,13 @@ private XmlReader EndExecuteXmlReaderAsync(IAsyncResult asyncResult) { else { ThrowIfReconnectionHasBeenCanceled(); // lock on _stateObj prevents ----s with close/cancel. - lock (_stateObj) { + // If we have already initiate the End call internally, we have already done that, so no point doing it again. + if (!_internalEndExecuteInitiated) { + lock (_stateObj) { + return EndExecuteXmlReaderInternal(asyncResult); + } + } + else { return EndExecuteXmlReaderInternal(asyncResult); } } @@ -1788,7 +1881,7 @@ private XmlReader EndExecuteXmlReaderInternal(IAsyncResult asyncResult) { bool success = false; int? sqlExceptionNumber = null; try { - XmlReader result = CompleteXmlReader(InternalEndExecuteReader(asyncResult, ADP.EndExecuteXmlReader)); + XmlReader result = CompleteXmlReader(InternalEndExecuteReader(asyncResult, ADP.EndExecuteXmlReader, isInternal: false)); success = true; return result; } @@ -1894,7 +1987,7 @@ public IAsyncResult BeginExecuteReader(CommandBehavior behavior) { public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject, CommandBehavior behavior) { Bid.CorrelationTrace(" ObjectID%d#, behavior=%d{ds.CommandBehavior}, ActivityID %ls\n", ObjectID, (int)behavior); SqlConnection.ExecutePermission.Demand(); - return BeginExecuteReaderInternal(behavior, callback, stateObject, 0); + return BeginExecuteReaderInternal(behavior, callback, stateObject, 0, inRetry: false); } internal SqlDataReader ExecuteReader(CommandBehavior behavior, string method) { @@ -1967,6 +2060,7 @@ public SqlDataReader EndExecuteReader(IAsyncResult asyncResult) { private SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult) { Bid.CorrelationTrace(" ObjectID%d#, ActivityID %ls\n", ObjectID); + Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null); Exception asyncException = ((Task)asyncResult).Exception; if (asyncException != null) { @@ -1977,8 +2071,14 @@ private SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult) { else { ThrowIfReconnectionHasBeenCanceled(); // lock on _stateObj prevents ----s with close/cancel. - lock (_stateObj) { + // If we have already initiate the End call internally, we have already done that, so no point doing it again. + if (!_internalEndExecuteInitiated) { + lock (_stateObj) { return EndExecuteReaderInternal(asyncResult); + } + } + else { + return EndExecuteReaderInternal(asyncResult); } } } @@ -1989,7 +2089,7 @@ private SqlDataReader EndExecuteReaderInternal(IAsyncResult asyncResult) { int? sqlExceptionNumber = null; try { statistics = SqlStatistics.StartTimer(Statistics); - SqlDataReader result = InternalEndExecuteReader(asyncResult, ADP.EndExecuteReader); + SqlDataReader result = InternalEndExecuteReader(asyncResult, ADP.EndExecuteReader, isInternal: false); success = true; return result; } @@ -2020,26 +2120,33 @@ private SqlDataReader EndExecuteReaderInternal(IAsyncResult asyncResult) { } private IAsyncResult BeginExecuteReaderAsync(CommandBehavior behavior, AsyncCallback callback, object stateObject) { - return BeginExecuteReaderInternal(behavior, callback, stateObject, CommandTimeout, asyncWrite:true); + return BeginExecuteReaderInternal(behavior, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite:true); } - private IAsyncResult BeginExecuteReaderInternal(CommandBehavior behavior, AsyncCallback callback, object stateObject, int timeout, bool asyncWrite = false) { - // Reset _pendingCancel upon entry into any Execute - used to synchronize state - // between entry into Execute* API and the thread obtaining the stateObject. - _pendingCancel = false; + private IAsyncResult BeginExecuteReaderInternal(CommandBehavior behavior, AsyncCallback callback, object stateObject, int timeout, bool inRetry, bool asyncWrite = false) { + TaskCompletionSource globalCompletion = new TaskCompletionSource(stateObject); + TaskCompletionSource localCompletion = new TaskCompletionSource(stateObject); + + if (!inRetry) { + // Reset _pendingCancel upon entry into any Execute - used to synchronize state + // between entry into Execute* API and the thread obtaining the stateObject. + _pendingCancel = false; + } SqlStatistics statistics = null; try { - statistics = SqlStatistics.StartTimer(Statistics); - WriteBeginExecuteEvent(); - TaskCompletionSource completion = new TaskCompletionSource(stateObject); - - ValidateAsyncCommand(); // Special case - done outside of try/catches to prevent putting a stateObj - // back into pool when we should not. + if (!inRetry) { + statistics = SqlStatistics.StartTimer(Statistics); + WriteBeginExecuteEvent(); + ValidateAsyncCommand(); // Special case - done outside of try/catches to prevent putting a stateObj + // back into pool when we should not. + } + + bool usedCache; Task writeTask = null; try { // InternalExecuteNonQuery already has reliability block, but if failure will not put stateObj back into pool. - RunExecuteReader(behavior, RunBehavior.ReturnImmediately, true, ADP.BeginExecuteReader, completion, timeout, out writeTask, asyncWrite); + RunExecuteReader(behavior, RunBehavior.ReturnImmediately, true, ADP.BeginExecuteReader, localCompletion, timeout, out writeTask, out usedCache, asyncWrite, inRetry); } catch (Exception e) { if (!ADP.IsCatchableOrSecurityExceptionType(e)) { @@ -2053,23 +2160,128 @@ private IAsyncResult BeginExecuteReaderInternal(CommandBehavior behavior, AsyncC } if (writeTask != null ) { - AsyncHelper.ContinueTask(writeTask,completion,()=> BeginExecuteReaderInternalReadStage(completion)); + AsyncHelper.ContinueTask(writeTask, localCompletion, () => BeginExecuteReaderInternalReadStage(localCompletion)); } else { - BeginExecuteReaderInternalReadStage(completion); + BeginExecuteReaderInternalReadStage(localCompletion); + } + + // When we use query caching for parameter encryption we need to retry on specific errors. + // In these cases finalize the call internally and trigger a retry when needed. + if (!TriggerInternalEndAndRetryIfNecessary(behavior, stateObject, timeout, ADP.EndExecuteReader, usedCache, inRetry, asyncWrite, globalCompletion, localCompletion, InternalEndExecuteReader, BeginExecuteReaderInternal)) { + globalCompletion = localCompletion; } // Add callback after work is done to avoid overlapping Begin\End methods if (callback != null) { - completion.Task.ContinueWith((t) => callback(t), TaskScheduler.Default); + globalCompletion.Task.ContinueWith((t) => callback(t), TaskScheduler.Default); } - return completion.Task; + + return globalCompletion.Task; } finally { SqlStatistics.StopTimer(statistics); } } + private bool TriggerInternalEndAndRetryIfNecessary(CommandBehavior behavior, object stateObject, int timeout, string endMethod, bool usedCache, bool inRetry, bool asyncWrite, TaskCompletionSource globalCompletion, TaskCompletionSource localCompletion, Func endFunc, Func retryFunc) { + // We shouldn't be using the cache if we are in retry. + Debug.Assert(!usedCache || !inRetry); + + // If column ecnryption is enabled and we used the cache, we want to catch any potential exceptions that were caused by the query cache and retry if the error indicates that we should. + // So, try to read the result of the query before completing the overall task and trigger a retry if appropriate. + if ((IsColumnEncryptionEnabled && !inRetry && usedCache) +#if DEBUG + || _forceInternalEndQuery +#endif + ) { + long firstAttemptStart = ADP.TimerCurrent(); + + localCompletion.Task.ContinueWith(tsk => { + if (tsk.IsFaulted) { + globalCompletion.TrySetException(tsk.Exception.InnerException); + } + else if (tsk.IsCanceled) { + globalCompletion.TrySetCanceled(); + } + else { + try { + // Mark that we initiated the internal EndExecute. This should always be false until we set it here. + Debug.Assert(!_internalEndExecuteInitiated); + _internalEndExecuteInitiated = true; + + // lock on _stateObj prevents ----s with close/cancel. + lock (_stateObj) { + endFunc(tsk, endMethod, true/*inInternal*/); + } + globalCompletion.TrySetResult(tsk.Result); + } + catch (Exception e) { + // Put the state object back to the cache. + // Do not reset the async state, since this is managed by the user Begin/End and not internally. + if (ADP.IsCatchableExceptionType(e)) { + ReliablePutStateObject(); + } + + bool shouldRetry = false; + + // Check if we have an error indicating that we can retry. + if (e is SqlException) { + SqlException sqlEx = e as SqlException; + + for (int i = 0; i < sqlEx.Errors.Count; i++) { + if (sqlEx.Errors[i].Number == TdsEnums.TCE_CONVERSION_ERROR_CLIENT_RETRY) { + shouldRetry = true; + break; + } + } + } + + if (!shouldRetry) { + // If we cannot retry, Reset the async state to make sure we leave a clean state. + if (null != _cachedAsyncState) { + _cachedAsyncState.ResetAsyncState(); + } + _activeConnection.GetOpenTdsConnection().DecrementAsyncCount(); + + globalCompletion.TrySetException(e); + } + else { + // Remove the enrty from the cache since it was inconsistent. + SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this); + + try { + // Kick off the retry. + _internalEndExecuteInitiated = false; + Task retryTask = (Task)retryFunc(behavior, null, stateObject, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), true/*inRetry*/, asyncWrite); + + retryTask.ContinueWith(retryTsk => { + if (retryTsk.IsFaulted) { + globalCompletion.TrySetException(retryTsk.Exception.InnerException); + } + else if (retryTsk.IsCanceled) { + globalCompletion.TrySetCanceled(); + } + else { + globalCompletion.TrySetResult(retryTsk.Result); + } + }, TaskScheduler.Default); + } + catch (Exception e2) { + globalCompletion.TrySetException(e2); + } + } + } + } + }, TaskScheduler.Default); + + return true; + } + else { + return false; + } + } + private void BeginExecuteReaderInternalReadStage(TaskCompletionSource completion) { Debug.Assert(completion != null,"CompletionSource should not be null"); // Read SNI does not have catches for async exceptions, handle here. @@ -2123,26 +2335,15 @@ private void BeginExecuteReaderInternalReadStage(TaskCompletionSource co } } - private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, string endMethod) { - - VerifyEndExecuteState((Task) asyncResult, endMethod); - WaitForAsyncResults(asyncResult); + private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, string endMethod, bool isInternal) { + + VerifyEndExecuteState((Task)asyncResult, endMethod); + WaitForAsyncResults(asyncResult, isInternal); - // If Transparent parameter encryption was attempted, then we would have skipped the below - // checks in VerifyEndExecuteState since we wanted to wait for WaitForAsyncResults to complete. + // If column encryption is enabled, also check the state after waiting for the task. + // It would be better to do this for all cases, but avoiding for compatibility reasons. if (IsColumnEncryptionEnabled) { - if (cachedAsyncState.EndMethodName == null) { - throw ADP.MethodCalledTwice(endMethod); - } - - if (endMethod != cachedAsyncState.EndMethodName) { - throw ADP.MismatchedAsyncResult(cachedAsyncState.EndMethodName, endMethod); - } - - if (!cachedAsyncState.IsActiveConnectionValid(_activeConnection)) { - // If the connection is not 'valid' then it was closed while we were executing - throw ADP.ClosedConnectionError(); - } + VerifyEndExecuteState((Task)asyncResult, endMethod, fullCheckForColumnEncryption: true); } CheckThrowSNIException(); @@ -2160,7 +2361,7 @@ private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, string { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); - SqlDataReader reader = CompleteAsyncExecuteReader(); + SqlDataReader reader = CompleteAsyncExecuteReader(isInternal); Debug.Assert(null == _stateObj, "non-null state object in InternalEndExecuteReader"); return reader; } @@ -2188,7 +2389,7 @@ private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, string public override Task ExecuteNonQueryAsync(CancellationToken cancellationToken) { Bid.CorrelationTrace(" ObjectID%d#, ActivityID %ls\n", ObjectID); - SqlConnection.ExecutePermission.Demand(); + SqlConnection.ExecutePermission.Demand(); TaskCompletionSource source = new TaskCompletionSource(); @@ -2220,7 +2421,7 @@ public override Task ExecuteNonQueryAsync(CancellationToken cancellationTok } } }, TaskScheduler.Default); - } + } catch (Exception e) { source.SetException(e); } @@ -2252,7 +2453,7 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b new public Task ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { Bid.CorrelationTrace(" ObjectID%d#, behavior=%d{ds.CommandBehavior}, ActivityID %ls\n", ObjectID, (int)behavior); - SqlConnection.ExecutePermission.Demand(); + SqlConnection.ExecutePermission.Demand(); TaskCompletionSource source = new TaskCompletionSource(); @@ -2264,12 +2465,12 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b } registration = cancellationToken.Register(CancelIgnoreFailure); } - + Task returnedTask = source.Task; try { RegisterForConnectionCloseNotification(ref returnedTask); - Task.Factory.FromAsync(BeginExecuteReaderAsync, EndExecuteReaderAsync, behavior, null).ContinueWith((t) => { + Task.Factory.FromAsync(BeginExecuteReaderAsync, EndExecuteReaderAsync, behavior, null).ContinueWith((t) => { registration.Dispose(); if (t.IsFaulted) { Exception e = t.Exception.InnerException; @@ -2284,7 +2485,7 @@ protected override Task ExecuteDbDataReaderAsync(CommandBehavior b } } }, TaskScheduler.Default); - } + } catch (Exception e) { source.SetException(e); } @@ -2356,8 +2557,8 @@ public Task ExecuteXmlReaderAsync() { public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken) { Bid.CorrelationTrace(" ObjectID%d#, ActivityID %ls\n", ObjectID); - SqlConnection.ExecutePermission.Demand(); - + SqlConnection.ExecutePermission.Demand(); + TaskCompletionSource source = new TaskCompletionSource(); CancellationTokenRegistration registration = new CancellationTokenRegistration(); @@ -2368,7 +2569,7 @@ public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken } registration = cancellationToken.Register(CancelIgnoreFailure); } - + Task returnedTask = source.Task; try { RegisterForConnectionCloseNotification(ref returnedTask); @@ -2388,7 +2589,7 @@ public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken } } }, TaskScheduler.Default); - } + } catch (Exception e) { source.SetException(e); } @@ -2882,6 +3083,9 @@ private Task RunExecuteNonQueryTds(string methodName, bool async, int timeout, b GetStateObject(); + // Reset the encryption state in case it has been set by a previous command. + ResetEncryptionState(); + // we just send over the raw text with no annotation // no parameters are sent over // no data reader is returned @@ -2993,6 +3197,12 @@ private void ResetEncryptionState() { // First reset the command level state. ClearDescribeParameterEncryptionRequests(); + // Reset the state for internal End execution. + _internalEndExecuteInitiated = false; + + // Reset the state for the cache. + CachingQueryMetadataPostponed = false; + // Reset the state of each of the parameters. if (_parameters != null) { for (int i = 0; i < _parameters.Count; i++) { @@ -3047,13 +3257,13 @@ private void PrepareTransparentEncryptionFinallyBlock( bool closeDataReader, /// /// /// - private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool returnStream, bool async, int timeout, TaskCompletionSource completion, out Task returnTask, bool asyncWrite) - { + private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool returnStream, bool async, int timeout, TaskCompletionSource completion, out Task returnTask, bool asyncWrite, out bool usedCache, bool inRetry) { // Fetch reader with input params Task fetchInputParameterEncryptionInfoTask = null; bool describeParameterEncryptionNeeded = false; SqlDataReader describeParameterEncryptionDataReader = null; returnTask = null; + usedCache = false; Debug.Assert(_activeConnection != null, "_activeConnection should not be null in PrepareForTransparentEncryption."); Debug.Assert(_activeConnection.Parser != null, "_activeConnection.Parser should not be null in PrepareForTransparentEncryption."); @@ -3064,11 +3274,18 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r "ColumnEncryption setting should be enabled for input parameter encryption."); Debug.Assert(async == (completion != null), "completion should can be null if and only if mode is async."); + // If we are not in Batch RPC and not already retrying, attempt to fetch the cipher MD for each parameter from the cache. + // If this succeeds then return immediately, otherwise just fall back to the full crypto MD discovery. + if (!BatchRPCMode && !inRetry && SqlQueryMetadataCache.GetInstance().GetQueryMetadataIfExists(this)) { + usedCache = true; + return; + } + // A flag to indicate if finallyblock needs to execute. bool processFinallyBlock = true; // A flag to indicate if we need to decrement async count on the connection in finally block. - bool decrementAsyncCountInFinallyBlock = async; + bool decrementAsyncCountInFinallyBlock = false; // Flag to indicate if exception is caught during the execution, to govern clean up. bool exceptionCaught = false; @@ -3118,6 +3335,9 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r return; } + // If we are in async execution, we need to decrement our async count on exception. + decrementAsyncCountInFinallyBlock = async; + Debug.Assert(describeParameterEncryptionDataReader != null, "describeParameterEncryptionDataReader should not be null, as it is required to get results of describe parameter encryption."); @@ -3128,6 +3348,7 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r processFinallyBlock = false; returnTask = AsyncHelper.CreateContinuationTask(fetchInputParameterEncryptionInfoTask, () => { bool processFinallyBlockAsync = true; + bool decrementAsyncCountInFinallyBlockAsync = true; RuntimeHelpers.PrepareConstrainedRegions(); try { @@ -3143,14 +3364,13 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r // If it is async, then TryFetchInputParameterEncryptionInfo-> RunExecuteReaderTds would have incremented the async count. // Decrement it when we are about to complete async execute reader. SqlInternalConnectionTds internalConnectionTds = _activeConnection.GetOpenTdsConnection(); - if (internalConnectionTds != null) - { + if (internalConnectionTds != null) { internalConnectionTds.DecrementAsyncCount(); - decrementAsyncCountInFinallyBlock = false; + decrementAsyncCountInFinallyBlockAsync = false; } // Complete executereader. - describeParameterEncryptionDataReader = CompleteAsyncExecuteReader(); + describeParameterEncryptionDataReader = CompleteAsyncExecuteReader(forDescribeParameterEncryption: true); Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption."); // Read the results of describe parameter encryption. @@ -3173,7 +3393,7 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r } finally { PrepareTransparentEncryptionFinallyBlock( closeDataReader: processFinallyBlockAsync, - decrementAsyncCount: decrementAsyncCountInFinallyBlock, + decrementAsyncCount: decrementAsyncCountInFinallyBlockAsync, clearDataStructures: processFinallyBlockAsync, wasDescribeParameterEncryptionNeeded: describeParameterEncryptionNeeded, describeParameterEncryptionRpcOriginalRpcMap: describeParameterEncryptionRpcOriginalRpcMap, @@ -3187,6 +3407,8 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r if (exception != null) { throw exception; }})); + + decrementAsyncCountInFinallyBlock = false; } else { // If it was async, ending the reader is still pending. @@ -3196,6 +3418,7 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r processFinallyBlock = false; returnTask = Task.Run(() => { bool processFinallyBlockAsync = true; + bool decrementAsyncCountInFinallyBlockAsync = true; RuntimeHelpers.PrepareConstrainedRegions(); try { @@ -3214,11 +3437,11 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r SqlInternalConnectionTds internalConnectionTds = _activeConnection.GetOpenTdsConnection(); if (internalConnectionTds != null) { internalConnectionTds.DecrementAsyncCount(); - decrementAsyncCountInFinallyBlock = false; + decrementAsyncCountInFinallyBlockAsync = false; } // Complete executereader. - describeParameterEncryptionDataReader = CompleteAsyncExecuteReader(); + describeParameterEncryptionDataReader = CompleteAsyncExecuteReader(forDescribeParameterEncryption: true); Debug.Assert(null == _stateObj, "non-null state object in PrepareForTransparentEncryption."); // Read the results of describe parameter encryption. @@ -3242,13 +3465,15 @@ private void PrepareForTransparentEncryption(CommandBehavior cmdBehavior, bool r } finally { PrepareTransparentEncryptionFinallyBlock( closeDataReader: processFinallyBlockAsync, - decrementAsyncCount: decrementAsyncCountInFinallyBlock, + decrementAsyncCount: decrementAsyncCountInFinallyBlockAsync, clearDataStructures: processFinallyBlockAsync, wasDescribeParameterEncryptionNeeded: describeParameterEncryptionNeeded, describeParameterEncryptionRpcOriginalRpcMap: describeParameterEncryptionRpcOriginalRpcMap, describeParameterEncryptionDataReader: describeParameterEncryptionDataReader); } }); + + decrementAsyncCountInFinallyBlock = false; } else { // For synchronous execution, read the results of describe parameter encryption here. @@ -3413,6 +3638,7 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, timeout: timeout, task: out task, asyncWrite: asyncWrite, + inRetry: false, ds: null, describeParameterEncryptionRequest: true); } @@ -3494,6 +3720,13 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques SqlParameter param = originalRpcRequest.parameters[i]; paramCopy = new SqlParameter(param.ParameterName, param.SqlDbType, param.Size, param.Direction, param.Precision, param.Scale, param.SourceColumn, param.SourceVersion, param.SourceColumnNullMapping, param.Value, param.XmlSchemaCollectionDatabase, param.XmlSchemaCollectionOwningSchema, param.XmlSchemaCollectionName); + paramCopy.CompareInfo = param.CompareInfo; + paramCopy.TypeName = param.TypeName; + paramCopy.UdtTypeName = param.UdtTypeName; + paramCopy.IsNullable = param.IsNullable; + paramCopy.LocaleId = param.LocaleId; + paramCopy.Offset = param.Offset; + tempCollection.Add(paramCopy); } @@ -3715,18 +3948,25 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi } } } + + // If we are not in Batch RPC mode, update the query cache with the encryption MD. + if (!BatchRPCMode) { + SqlQueryMetadataCache.GetInstance().AddQueryMetadata(this, ignoreQueriesWithReturnValueParams: true); + } } internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, string method) { Task unused; // sync execution - SqlDataReader reader = RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, completion:null, timeout:CommandTimeout, task:out unused); + bool usedCache; + SqlDataReader reader = RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, completion: null, timeout: CommandTimeout, task: out unused, usedCache: out usedCache); Debug.Assert(unused == null, "returned task during synchronous execution"); return reader; } // task is created in case of pending asynchronous write, returned SqlDataReader should not be utilized until that task is complete - internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, string method, TaskCompletionSource completion, int timeout, out Task task, bool asyncWrite = false) { + internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, string method, TaskCompletionSource completion, int timeout, out Task task, out bool usedCache, bool asyncWrite = false, bool inRetry = false) { bool async = (null != completion); + usedCache = false; task = null; @@ -3740,7 +3980,10 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior // @devnote: this function may throw for an invalid connection // @devnote: returns false for empty command text - ValidateCommand(method, async); + if (!inRetry) { + ValidateCommand(method, async); + } + CheckNotificationStateAndAutoEnlist(); // Only call after validate - requires non null connection! TdsParser bestEffortCleanupTarget = null; @@ -3777,14 +4020,46 @@ internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior } else if (IsColumnEncryptionEnabled) { Task returnTask = null; - PrepareForTransparentEncryption(cmdBehavior, returnStream, async, timeout, completion, out returnTask, asyncWrite && async); - Debug.Assert(async == (returnTask != null), @"returnTask should be null if and only if async is false."); + PrepareForTransparentEncryption(cmdBehavior, returnStream, async, timeout, completion, out returnTask, asyncWrite && async, out usedCache, inRetry); + Debug.Assert(usedCache || (async == (returnTask != null)), @"if we didn't use the cache, returnTask should be null if and only if async is false."); + + long firstAttemptStart = ADP.TimerCurrent(); - return RunExecuteReaderTdsWithTransparentParameterEncryption( cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite && async, ds: null, - describeParameterEncryptionRequest: false, describeParameterEncryptionTask: returnTask); + try { + return RunExecuteReaderTdsWithTransparentParameterEncryption(cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite && async, inRetry: inRetry, ds: null, + describeParameterEncryptionRequest: false, describeParameterEncryptionTask: returnTask); + } + catch (SqlException ex) { + // We only want to retry once, so don't retry if we are already in retry. + // If we didn't use the cache, we don't want to retry. + // The async retried are handled separately, handle only [....] calls here. + if (inRetry || async || !usedCache) { + throw; + } + + bool shouldRetry = false; + + // Check if we have an error indicating that we can retry. + for (int i = 0; i < ex.Errors.Count; i++) { + if (ex.Errors[i].Number == TdsEnums.TCE_CONVERSION_ERROR_CLIENT_RETRY) { + shouldRetry = true; + break; + } + } + + if (!shouldRetry) { + throw; + } + else { + // Retry if the command failed with appropriate error. + // First invalidate the entry from the cache, so that we refresh our encryption MD. + SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this); + return RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, async, inRetry: true); + } + } } else { - return RunExecuteReaderTds( cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite && async); + return RunExecuteReaderTds( cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite && async, inRetry: inRetry); } } @@ -3830,11 +4105,11 @@ private SqlDataReader RunExecuteReaderTdsWithTransparentParameterEncryption(Comm int timeout, out Task task, bool asyncWrite, + bool inRetry, SqlDataReader ds=null, bool describeParameterEncryptionRequest = false, Task describeParameterEncryptionTask = null) { Debug.Assert(!asyncWrite || async, "AsyncWrite should be always accompanied by Async"); - Debug.Assert((describeParameterEncryptionTask != null) == async, @"async should be true if and only if describeParameterEncryptionTask is not null."); if (ds == null && returnStream) { ds = new SqlDataReader(this, cmdBehavior); @@ -3842,40 +4117,42 @@ private SqlDataReader RunExecuteReaderTdsWithTransparentParameterEncryption(Comm if (describeParameterEncryptionTask != null) { long parameterEncryptionStart = ADP.TimerCurrent(); - TaskCompletionSource completion = new TaskCompletionSource(); - AsyncHelper.ContinueTask(describeParameterEncryptionTask, completion, - () => { - Task subTask = null; - RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, TdsParserStaticMethods.GetRemainingTimeout(timeout, parameterEncryptionStart), out subTask, asyncWrite, ds); - if (subTask == null) { - completion.SetResult(null); - } - else { - AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null)); - } - }, connectionToDoom: null, - onFailure: ((exception) => { - if (_cachedAsyncState != null) { - _cachedAsyncState.ResetAsyncState(); - } - if (exception != null) { - throw exception; - }}), - onCancellation: (() => { - if (_cachedAsyncState != null) { - _cachedAsyncState.ResetAsyncState(); - }}), - connectionToAbort: _activeConnection); - task = completion.Task; - return ds; + TaskCompletionSource completion = new TaskCompletionSource(); + AsyncHelper.ContinueTask(describeParameterEncryptionTask, completion, + () => { + Task subTask = null; + RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, TdsParserStaticMethods.GetRemainingTimeout(timeout, parameterEncryptionStart), out subTask, asyncWrite, inRetry, ds); + if (subTask == null) { + completion.SetResult(null); + } + else { + AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null)); + } + }, connectionToDoom: null, + onFailure: ((exception) => { + if (_cachedAsyncState != null) { + _cachedAsyncState.ResetAsyncState(); + } + if (exception != null) { + throw exception; + } + }), + onCancellation: (() => { + if (_cachedAsyncState != null) { + _cachedAsyncState.ResetAsyncState(); + } + }), + connectionToAbort: _activeConnection); + task = completion.Task; + return ds; } else { // Synchronous execution. - return RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite, ds); + return RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite, inRetry, ds); } } - private SqlDataReader RunExecuteReaderTds( CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, bool async, int timeout, out Task task, bool asyncWrite, SqlDataReader ds=null, bool describeParameterEncryptionRequest = false) { + private SqlDataReader RunExecuteReaderTds( CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, bool async, int timeout, out Task task, bool asyncWrite, bool inRetry, SqlDataReader ds=null, bool describeParameterEncryptionRequest = false) { Debug.Assert(!asyncWrite || async, "AsyncWrite should be always accompanied by Async"); if (ds == null && returnStream) { @@ -3900,7 +4177,7 @@ private SqlDataReader RunExecuteReaderTds( CommandBehavior cmdBehavior, RunBehav Interlocked.CompareExchange(ref _reconnectionCompletionSource, null, completion); timeoutCTS.Cancel(); Task subTask; - RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart), out subTask, asyncWrite, ds); + RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart), out subTask, asyncWrite, inRetry, ds); if (subTask == null) { completion.SetResult(null); } @@ -3933,7 +4210,8 @@ private SqlDataReader RunExecuteReaderTds( CommandBehavior cmdBehavior, RunBehav bool processFinallyBlock = true; bool decrementAsyncCountOnFailure = false; - if (async) { + // If we are in retry, don't increment the Async count. This should have already been set. + if (async && !inRetry) { _activeConnection.GetOpenTdsConnection().IncrementAsyncCount(); decrementAsyncCountOnFailure = true; } @@ -4077,7 +4355,7 @@ private SqlDataReader RunExecuteReaderTds( CommandBehavior cmdBehavior, RunBehav } else { // Always execute - even if no reader! - FinishExecuteReader(ds, runBehavior, optionSettings); + FinishExecuteReader(ds, runBehavior, optionSettings, isInternal: false, forDescribeParameterEncryption: false); } } catch (Exception e) { @@ -4177,11 +4455,11 @@ private SqlDataReader RunExecuteReaderSmi( CommandBehavior cmdBehavior, RunBehav return ds; } - private SqlDataReader CompleteAsyncExecuteReader() { + private SqlDataReader CompleteAsyncExecuteReader(bool isInternal = false, bool forDescribeParameterEncryption = false) { SqlDataReader ds = cachedAsyncState.CachedAsyncReader; // should not be null bool processFinallyBlock = true; try { - FinishExecuteReader(ds, cachedAsyncState.CachedRunBehavior, cachedAsyncState.CachedSetOptions); + FinishExecuteReader(ds, cachedAsyncState.CachedRunBehavior, cachedAsyncState.CachedSetOptions, isInternal, forDescribeParameterEncryption); } catch (Exception e) { processFinallyBlock = ADP.IsCatchableExceptionType(e); @@ -4190,7 +4468,11 @@ private SqlDataReader CompleteAsyncExecuteReader() { finally { TdsParser.ReliabilitySection.Assert("unreliable call to CompleteAsyncExecuteReader"); // you need to setup for a thread abort somewhere before you call this method if (processFinallyBlock) { - cachedAsyncState.ResetAsyncState(); + // Don't reset the state for internal End. The user End will do that eventually. + if (!isInternal) { + cachedAsyncState.ResetAsyncState(); + } + PutStateObject(); } } @@ -4198,10 +4480,19 @@ private SqlDataReader CompleteAsyncExecuteReader() { return ds; } - private void FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, string resetOptionsString) { + private void FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, string resetOptionsString, bool isInternal, bool forDescribeParameterEncryption) { // always wrap with a try { FinishExecuteReader(...) } finally { PutStateObject(); } - NotifyDependency(); + // If this is not for internal usage, notify the dependency. If we have already initiated the end internally, the reader should be ready, so just return. + if (!isInternal && !forDescribeParameterEncryption) { + NotifyDependency(); + + if (_internalEndExecuteInitiated) { + Debug.Assert(_stateObj == null); + return; + } + } + if (runBehavior == RunBehavior.UntilDone) { try { bool dataReady; @@ -4618,6 +4909,14 @@ internal void OnReturnStatus(int status) { parameter.Value = status; } + + // If we are not in Batch RPC mode, update the query cache with the encryption MD. + // We can do this now that we have distinguished between ReturnValue and ReturnStatus. + // Read comment in AddQueryMetadata() for more details. + if (!BatchRPCMode && CachingQueryMetadataPostponed) { + SqlQueryMetadataCache.GetInstance().AddQueryMetadata(this, ignoreQueriesWithReturnValueParams: false); + } + break; } } diff --git a/System.Data/System/Data/SqlClient/SqlConnection.cs b/System.Data/System/Data/SqlClient/SqlConnection.cs index 9e637751e..af278effe 100644 --- a/System.Data/System/Data/SqlClient/SqlConnection.cs +++ b/System.Data/System/Data/SqlClient/SqlConnection.cs @@ -88,7 +88,51 @@ static public IDictionary> ColumnEncryptionTrustedMasterKe return _ColumnEncryptionTrustedMasterKeyPaths; } } - + + /// + /// Defines whether query metadata caching is enabled. + /// + static private bool _ColumnEncryptionQueryMetadataCacheEnabled = true; + + [ + DefaultValue(null), + ResCategoryAttribute(Res.DataCategory_Data), + ResDescriptionAttribute(Res.TCE_SqlConnection_ColumnEncryptionQueryMetadataCacheEnabled), + ] + static public bool ColumnEncryptionQueryMetadataCacheEnabled + { + get + { + return _ColumnEncryptionQueryMetadataCacheEnabled; + } + set + { + _ColumnEncryptionQueryMetadataCacheEnabled = value; + } + } + + /// + /// Defines whether query metadata caching is enabled. + /// + static private TimeSpan _ColumnEncryptionKeyCacheTtl = TimeSpan.FromHours(2); + + [ + DefaultValue(null), + ResCategoryAttribute(Res.DataCategory_Data), + ResDescriptionAttribute(Res.TCE_SqlConnection_ColumnEncryptionKeyCacheTtl), + ] + static public TimeSpan ColumnEncryptionKeyCacheTtl + { + get + { + return _ColumnEncryptionKeyCacheTtl; + } + set + { + _ColumnEncryptionKeyCacheTtl = value; + } + } + /// /// This function should only be called once in an app. This does shallow copying of the dictionary so that /// the app cannot alter the custom provider list once it has been set. @@ -437,7 +481,15 @@ internal Version TypeSystemAssemblyVersion { get { return ((SqlConnectionString)ConnectionOptions).TypeSystemAssemblyVersion; } - } + } + + internal PoolBlockingPeriod PoolBlockingPeriod + { + get + { + return ((SqlConnectionString)ConnectionOptions).PoolBlockingPeriod; + } + } internal int ConnectRetryInterval { get { diff --git a/System.Data/System/Data/SqlClient/SqlConnectionString.cs b/System.Data/System/Data/SqlClient/SqlConnectionString.cs index 85ed1b7bb..1dd74aec9 100644 --- a/System.Data/System/Data/SqlClient/SqlConnectionString.cs +++ b/System.Data/System/Data/SqlClient/SqlConnectionString.cs @@ -30,6 +30,7 @@ internal static class DEFAULT { internal const string Application_Name = TdsEnums.SQL_PROVIDER_NAME; internal const bool Asynchronous = false; internal const string AttachDBFilename = ""; + internal const PoolBlockingPeriod PoolBlockingPeriod = DbConnectionStringDefaults.PoolBlockingPeriod; internal const int Connect_Timeout = ADP.DefaultConnectionTimeout; internal const bool Connection_Reset = true; internal const bool Context_Connection = false; @@ -69,6 +70,7 @@ internal static class KEY { internal const string Application_Name = "application name"; internal const string AsynchronousProcessing = "asynchronous processing"; internal const string AttachDBFilename = "attachdbfilename"; + internal const string PoolBlockingPeriod = "poolblockingperiod"; internal const string ColumnEncryptionSetting = "column encryption setting"; internal const string Connect_Timeout = "connect timeout"; internal const string Connection_Reset = "connection reset"; @@ -190,6 +192,7 @@ internal static class TRANSACIONBINDING { private readonly bool _integratedSecurity; + private readonly PoolBlockingPeriod _poolBlockingPeriod; private readonly bool _connectionReset; private readonly bool _contextConnection; private readonly bool _encrypt; @@ -247,6 +250,7 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G ConvertValueToBoolean(KEY.AsynchronousProcessing, DEFAULT.Asynchronous); // while we don't use it anymore, we still need to verify it is true/false // SQLPT 41700: Ignore ResetConnection=False (still validate the keyword/value) + _poolBlockingPeriod = ConvertValueToPoolBlockingPeriod(); _connectionReset = ConvertValueToBoolean(KEY.Connection_Reset, DEFAULT.Connection_Reset); _contextConnection = ConvertValueToBoolean(KEY.Context_Connection, DEFAULT.Context_Connection); _encrypt = ConvertValueToEncrypt(); @@ -490,6 +494,7 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS _userInstance = userInstance; _connectTimeout = connectionOptions._connectTimeout; _loadBalanceTimeout = connectionOptions._loadBalanceTimeout; + _poolBlockingPeriod = connectionOptions._poolBlockingPeriod; _maxPoolSize = connectionOptions._maxPoolSize; _minPoolSize = connectionOptions._minPoolSize; _multiSubnetFailover = connectionOptions._multiSubnetFailover; @@ -525,6 +530,8 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS // will work. In the future we can deprecate the keyword entirely. internal bool Asynchronous { get { return true; } } + internal PoolBlockingPeriod PoolBlockingPeriod { get { return _poolBlockingPeriod; } } + // SQLPT 41700: Ignore ResetConnection=False, always reset the connection for security internal bool ConnectionReset { get { return true; } } internal bool ContextConnection { get { return _contextConnection; } } @@ -620,6 +627,7 @@ internal static Hashtable GetParseSynonyms() { hash.Add(KEY.Application_Name, KEY.Application_Name); hash.Add(KEY.AsynchronousProcessing, KEY.AsynchronousProcessing); hash.Add(KEY.AttachDBFilename, KEY.AttachDBFilename); + hash.Add(KEY.PoolBlockingPeriod, KEY.PoolBlockingPeriod); hash.Add(KEY.Connect_Timeout, KEY.Connect_Timeout); hash.Add(KEY.Connection_Reset, KEY.Connection_Reset); hash.Add(KEY.Context_Connection, KEY.Context_Connection); @@ -779,6 +787,28 @@ internal System.Data.SqlClient.ApplicationIntent ConvertValueToApplicationIntent // ArgumentException and other types are raised as is (no wrapping) } + internal System.Data.SqlClient.PoolBlockingPeriod ConvertValueToPoolBlockingPeriod() + { + object value = base.Parsetable[KEY.PoolBlockingPeriod]; + if (value == null) + { + return DEFAULT.PoolBlockingPeriod; + } + + try + { + return DbConnectionStringBuilderUtil.ConvertToPoolBlockingPeriod(KEY.PoolBlockingPeriod, value); + } + catch (FormatException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.PoolBlockingPeriod, e); + } + catch (OverflowException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.PoolBlockingPeriod, e); + } + } + internal SqlAuthenticationMethod ConvertValueToAuthenticationType() { object value = base.Parsetable[KEY.Authentication]; diff --git a/System.Data/System/Data/SqlClient/SqlConnectionStringBuilder.cs b/System.Data/System/Data/SqlClient/SqlConnectionStringBuilder.cs index 8e7714341..ae3c2b1e6 100644 --- a/System.Data/System/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/System.Data/System/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -41,6 +41,7 @@ public sealed class SqlConnectionStringBuilder : DbConnectionStringBuilder { Pooling, MinPoolSize, MaxPoolSize, + PoolBlockingPeriod, AsynchronousProcessing, ConnectionReset, @@ -127,6 +128,7 @@ public sealed class SqlConnectionStringBuilder : DbConnectionStringBuilder { private bool _userInstance = DbConnectionStringDefaults.UserInstance; private SqlAuthenticationMethod _authentication = DbConnectionStringDefaults.Authentication; private SqlConnectionColumnEncryptionSetting _columnEncryptionSetting = DbConnectionStringDefaults.ColumnEncryptionSetting; + private PoolBlockingPeriod _poolBlockingPeriod = DbConnectionStringDefaults.PoolBlockingPeriod; static SqlConnectionStringBuilder() { string[] validKeywords = new string[KeywordsCount]; @@ -134,6 +136,7 @@ static SqlConnectionStringBuilder() { validKeywords[(int)Keywords.ApplicationName] = DbConnectionStringKeywords.ApplicationName; validKeywords[(int)Keywords.AsynchronousProcessing] = DbConnectionStringKeywords.AsynchronousProcessing; validKeywords[(int)Keywords.AttachDBFilename] = DbConnectionStringKeywords.AttachDBFilename; + validKeywords[(int)Keywords.PoolBlockingPeriod] = DbConnectionStringKeywords.PoolBlockingPeriod; validKeywords[(int)Keywords.ConnectionReset] = DbConnectionStringKeywords.ConnectionReset; validKeywords[(int)Keywords.ContextConnection] = DbConnectionStringKeywords.ContextConnection; validKeywords[(int)Keywords.ConnectTimeout] = DbConnectionStringKeywords.ConnectTimeout; @@ -174,6 +177,7 @@ static SqlConnectionStringBuilder() { hash.Add(DbConnectionStringKeywords.ApplicationName, Keywords.ApplicationName); hash.Add(DbConnectionStringKeywords.AsynchronousProcessing, Keywords.AsynchronousProcessing); hash.Add(DbConnectionStringKeywords.AttachDBFilename, Keywords.AttachDBFilename); + hash.Add(DbConnectionStringKeywords.PoolBlockingPeriod, Keywords.PoolBlockingPeriod); hash.Add(DbConnectionStringKeywords.ConnectTimeout, Keywords.ConnectTimeout); hash.Add(DbConnectionStringKeywords.ConnectionReset, Keywords.ConnectionReset); hash.Add(DbConnectionStringKeywords.ContextConnection, Keywords.ContextConnection); @@ -278,6 +282,7 @@ public override object this[string keyword] { case Keywords.Authentication: Authentication = ConvertToAuthenticationType(keyword, value); break; case Keywords.ColumnEncryptionSetting: ColumnEncryptionSetting = ConvertToColumnEncryptionSetting(keyword, value); break; case Keywords.AsynchronousProcessing: AsynchronousProcessing = ConvertToBoolean(value); break; + case Keywords.PoolBlockingPeriod: PoolBlockingPeriod = ConvertToPoolBlockingPeriod(keyword, value); break; #pragma warning disable 618 // Obsolete ConnectionReset case Keywords.ConnectionReset: ConnectionReset = ConvertToBoolean(value); break; #pragma warning restore 618 @@ -360,6 +365,25 @@ public string AttachDBFilename { } } + [DisplayName(DbConnectionStringKeywords.PoolBlockingPeriod)] + [ResCategoryAttribute(Res.DataCategory_Pooling)] + [ResDescriptionAttribute(Res.DbConnectionString_PoolBlockingPeriod)] + [RefreshPropertiesAttribute(RefreshProperties.All)] + public PoolBlockingPeriod PoolBlockingPeriod + { + get { return _poolBlockingPeriod; } + set + { + if (!DbConnectionStringBuilderUtil.IsValidPoolBlockingPeriodValue(value)) + { + throw ADP.InvalidEnumerationValue(typeof(PoolBlockingPeriod), (int)value); + } + + SetPoolBlockingPeriodValue(value); + _poolBlockingPeriod = value; + } + } + [Browsable(false)] [DisplayName(DbConnectionStringKeywords.ConnectionReset)] [Obsolete("ConnectionReset has been deprecated. SqlConnection will ignore the 'connection reset' keyword and always reset the connection")] // SQLPT 41700 @@ -881,6 +905,10 @@ private static ApplicationIntent ConvertToApplicationIntent(string keyword, obje private static SqlAuthenticationMethod ConvertToAuthenticationType(string keyword, object value) { return DbConnectionStringBuilderUtil.ConvertToAuthenticationType(keyword, value); } + private static PoolBlockingPeriod ConvertToPoolBlockingPeriod(string keyword, object value) + { + return DbConnectionStringBuilderUtil.ConvertToPoolBlockingPeriod(keyword, value); + } /// /// Convert to SqlConnectionColumnEncryptionSetting. @@ -906,6 +934,7 @@ private object GetAt(Keywords index) { case Keywords.ApplicationName: return ApplicationName; case Keywords.AsynchronousProcessing: return AsynchronousProcessing; case Keywords.AttachDBFilename: return AttachDBFilename; + case Keywords.PoolBlockingPeriod: return PoolBlockingPeriod; case Keywords.ConnectTimeout: return ConnectTimeout; #pragma warning disable 618 // Obsolete ConnectionReset case Keywords.ConnectionReset: return ConnectionReset; @@ -1012,6 +1041,10 @@ private void Reset(Keywords index) { case Keywords.Authentication: _authentication = DbConnectionStringDefaults.Authentication; break; + case Keywords.PoolBlockingPeriod: + _poolBlockingPeriod = DbConnectionStringDefaults.PoolBlockingPeriod; + break; + case Keywords.ConnectTimeout: _connectTimeout = DbConnectionStringDefaults.ConnectTimeout; break; @@ -1128,6 +1161,11 @@ private void SetApplicationIntentValue(ApplicationIntent value) { Debug.Assert(DbConnectionStringBuilderUtil.IsValidApplicationIntentValue(value), "Invalid value for ApplicationIntent"); base[DbConnectionStringKeywords.ApplicationIntent] = DbConnectionStringBuilderUtil.ApplicationIntentToString(value); } + private void SetPoolBlockingPeriodValue(PoolBlockingPeriod value) + { + Debug.Assert(DbConnectionStringBuilderUtil.IsValidPoolBlockingPeriodValue(value), "Invalid value for PoolBlockingPeriod"); + base[DbConnectionStringKeywords.PoolBlockingPeriod] = DbConnectionStringBuilderUtil.PoolBlockingPeriodToString(value); + } private void SetAuthenticationValue(SqlAuthenticationMethod value) { Debug.Assert(DbConnectionStringBuilderUtil.IsValidAuthenticationTypeValue(value), "Invalid value for AuthenticationType"); base[DbConnectionStringKeywords.Authentication] = DbConnectionStringBuilderUtil.AuthenticationTypeToString(value); diff --git a/System.Data/System/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs b/System.Data/System/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs index 879a86d61..0e799476b 100644 --- a/System.Data/System/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs +++ b/System.Data/System/Data/SqlClient/SqlConnectionTimeoutErrorInternal.cs @@ -207,10 +207,10 @@ internal string GetErrorMessage() durationString = null; break; } - + // This message is to be added only when within the various stages of a connection. // In all other cases, it will default to the original error message. - if ((currentPhase != SqlConnectionTimeoutErrorPhase.Undefined) || (currentPhase != SqlConnectionTimeoutErrorPhase.Complete)) + if ((currentPhase != SqlConnectionTimeoutErrorPhase.Undefined) && (currentPhase != SqlConnectionTimeoutErrorPhase.Complete)) { // NOTE: In case of a failover scenario, add a string that this failure occured as part of the primary or secondary server if (isFailoverScenario) @@ -229,13 +229,13 @@ internal string GetErrorMessage() originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth].GetMilliSecondDuration(), originalPhaseDurations[(int)SqlConnectionTimeoutErrorPhase.PostLogin].GetMilliSecondDuration()); } + } - // NOTE: To display duration in each phase. - if (durationString != null) - { - errorBuilder.Append(" "); - errorBuilder.Append(durationString); - } + // NOTE: To display duration in each phase. + if (durationString != null) + { + errorBuilder.Append(" "); + errorBuilder.Append(durationString); } return errorBuilder.ToString(); diff --git a/System.Data/System/Data/SqlClient/SqlInternalConnectionTds.cs b/System.Data/System/Data/SqlClient/SqlInternalConnectionTds.cs index c07b195ac..376909b9a 100644 --- a/System.Data/System/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/System.Data/System/Data/SqlClient/SqlInternalConnectionTds.cs @@ -889,6 +889,7 @@ private void ResetConnection() { } internal void DecrementAsyncCount() { + Debug.Assert(_asyncCommandCount > 0); Interlocked.Decrement(ref _asyncCommandCount); } diff --git a/System.Data/System/Data/SqlClient/SqlParameter.cs b/System.Data/System/Data/SqlClient/SqlParameter.cs index 9b238a470..81274a6b8 100644 --- a/System.Data/System/Data/SqlClient/SqlParameter.cs +++ b/System.Data/System/Data/SqlClient/SqlParameter.cs @@ -99,9 +99,6 @@ internal SqlCipherMetadata CipherMetadata { } set { - Debug.Assert(_columnEncryptionCipherMetadata == null || value == null, - "_columnEncryptionCipherMetadata should be set to a non-null value only once."); - _columnEncryptionCipherMetadata = value; } } diff --git a/System.Data/System/Data/SqlClient/SqlSymmetricKeyCache.cs b/System.Data/System/Data/SqlClient/SqlSymmetricKeyCache.cs index fa371fb72..8df061742 100644 --- a/System.Data/System/Data/SqlClient/SqlSymmetricKeyCache.cs +++ b/System.Data/System/Data/SqlClient/SqlSymmetricKeyCache.cs @@ -13,18 +13,19 @@ namespace System.Data.SqlClient { using System.Diagnostics; using System.Globalization; using System.Linq; + using System.Runtime.Caching; using System.Text; /// /// Implements a cache of Symmetric Keys (once they are decrypted).Useful for rapidly decrypting multiple data values. /// sealed internal class SqlSymmetricKeyCache { - private readonly ConcurrentDictionary _cache; + private readonly MemoryCache _cache; private static readonly SqlSymmetricKeyCache _singletonInstance = new SqlSymmetricKeyCache(); private SqlSymmetricKeyCache () { - _cache = new ConcurrentDictionary(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, capacity: 2); + _cache = new MemoryCache("ColumnEncryptionKeyCache"); } internal static SqlSymmetricKeyCache GetInstance () { @@ -54,10 +55,10 @@ internal bool GetKey (SqlEncryptionKeyInfo keyInfo, string serverName, out SqlCl Debug.Assert(cacheLookupKey.Length <= capacity, "We needed to allocate a larger array"); #endif //DEBUG - encryptionKey = null; - // Lookup the key in cache - if (!_cache.TryGetValue(cacheLookupKey, out encryptionKey)) { + encryptionKey = _cache.Get(cacheLookupKey) as SqlClientSymmetricKey; + + if (encryptionKey == null) { Debug.Assert(SqlConnection.ColumnEncryptionTrustedMasterKeyPaths != null, @"SqlConnection.ColumnEncryptionTrustedMasterKeyPaths should not be null"); // Check against the trusted key paths @@ -96,9 +97,13 @@ internal bool GetKey (SqlEncryptionKeyInfo keyInfo, string serverName, out SqlCl encryptionKey = new SqlClientSymmetricKey (plaintextKey); - // In case multiple threads reach here at the same time, the first one wins. - // The allocated memory will be reclaimed by Garbage Collector. - _cache.TryAdd(cacheLookupKey, encryptionKey); + // If the cache TTL is zero, don't even bother inserting to the cache. + if (SqlConnection.ColumnEncryptionKeyCacheTtl != TimeSpan.Zero) { + // In case multiple threads reach here at the same time, the first one wins. + // The allocated memory will be reclaimed by Garbage Collector. + DateTimeOffset expirationTime = DateTimeOffset.UtcNow.Add(SqlConnection.ColumnEncryptionKeyCacheTtl); + _cache.Add(cacheLookupKey, encryptionKey, expirationTime); + } } return true; diff --git a/System.Data/System/Data/SqlClient/SqlUtil.cs b/System.Data/System/Data/SqlClient/SqlUtil.cs index d12fb655c..815a25efd 100644 --- a/System.Data/System/Data/SqlClient/SqlUtil.cs +++ b/System.Data/System/Data/SqlClient/SqlUtil.cs @@ -1536,7 +1536,7 @@ static internal string SSPIGenerateError() { return Res.GetString(Res.SQL_SSPIGenerateError); } static internal string Timeout() { - return Res.GetString(Res.SQL_Timeout); + return Res.GetString(Res.SQL_Timeout_Execution); } static internal string Timeout_PreLogin_Begin() { return Res.GetString(Res.SQL_Timeout_PreLogin_Begin); diff --git a/System.Data/System/Data/SqlClient/TdsEnums.cs b/System.Data/System/Data/SqlClient/TdsEnums.cs index 319e252b2..11e3cdd98 100644 --- a/System.Data/System/Data/SqlClient/TdsEnums.cs +++ b/System.Data/System/Data/SqlClient/TdsEnums.cs @@ -544,6 +544,9 @@ public enum ActiveDirectoryWorkflow : byte { public const int IMPERSONATION_FAILED = 1346; public const int P_TOKENTOOLONG = 103; + // SQL error that indicates retry for Always Encrypted + public const int TCE_CONVERSION_ERROR_CLIENT_RETRY = 33514; + // SNI\Win32 error values // NOTE: these are simply windows system error codes, not SNI specific public const uint SNI_UNINITIALIZED = unchecked((uint)-1); diff --git a/System.Data/System/NewXml/XPathNodePointer.cs b/System.Data/System/NewXml/XPathNodePointer.cs index f8f3d1d95..1af6e149a 100644 --- a/System.Data/System/NewXml/XPathNodePointer.cs +++ b/System.Data/System/NewXml/XPathNodePointer.cs @@ -883,7 +883,6 @@ internal bool MoveToParent() { RealFoliate(); AssertValid(); if ( NodeType == XPathNodeType.Namespace ) { - Debug.Assert( _parentOfNS != null ); MoveTo( _parentOfNS ); return true; } diff --git a/System.IdentityModel/InternalApis/Clr/inc/AppContextDefaultValues.cs b/System.IdentityModel/InternalApis/Clr/inc/AppContextDefaultValues.cs index 8a130a0f3..9224bf8b8 100644 --- a/System.IdentityModel/InternalApis/Clr/inc/AppContextDefaultValues.cs +++ b/System.IdentityModel/InternalApis/Clr/inc/AppContextDefaultValues.cs @@ -3,6 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== + +// There are cases where we have multiple assemblies that are going to import this file and +// if they are going to also have InternalsVisibleTo between them, there will be a compiler warning +// that the type is found both in the source and in a referenced assembly. The compiler will prefer +// the version of the type defined in the source +// +// In order to disable the warning for this type we are disabling this warning for this entire file. +#pragma warning disable 436 + using System; using System.Collections.Generic; @@ -167,3 +176,5 @@ private static bool TryParseFrameworkName(String frameworkName, out String ident static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version); } } + +#pragma warning restore 436 diff --git a/System.IdentityModel/InternalApis/Clr/inc/LocalAppContext.cs b/System.IdentityModel/InternalApis/Clr/inc/LocalAppContext.cs index f05b599ed..33662b542 100644 --- a/System.IdentityModel/InternalApis/Clr/inc/LocalAppContext.cs +++ b/System.IdentityModel/InternalApis/Clr/inc/LocalAppContext.cs @@ -4,6 +4,14 @@ // // ==--== +// There are cases where we have multiple assemblies that are going to import this file and +// if they are going to also have InternalsVisibleTo between them, there will be a compiler warning +// that the type is found both in the source and in a referenced assembly. The compiler will prefer +// the version of the type defined in the source +// +// In order to disable the warning for this type we are disabling this warning for this entire file. +#pragma warning disable 436 + // NOTE: This file should not be included in mscorlib. This should only be included in FX libraries that need to provide switches using System; using System.Collections.Generic; @@ -126,3 +134,5 @@ internal static void DefineSwitchDefault(string switchName, bool initialValue) } } } + +#pragma warning restore 436 diff --git a/System.IdentityModel/System/IdentityModel/Claims/X509CertificateClaimSet.cs b/System.IdentityModel/System/IdentityModel/Claims/X509CertificateClaimSet.cs index 4113bd328..7561fffb9 100644 --- a/System.IdentityModel/System/IdentityModel/Claims/X509CertificateClaimSet.cs +++ b/System.IdentityModel/System/IdentityModel/Claims/X509CertificateClaimSet.cs @@ -5,12 +5,14 @@ namespace System.IdentityModel.Claims { using System.Collections.Generic; + using System.Diagnostics; using System.IdentityModel.Policy; using System.Net.Mail; using System.Security.Claims; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Principal; + using Globalization; public class X509CertificateClaimSet : ClaimSet, IIdentityInfo, IDisposable { @@ -172,24 +174,7 @@ IList InitializeClaimsCore() if (!string.IsNullOrEmpty(value)) claims.Add(Claim.CreateX500DistinguishedNameClaim(this.certificate.SubjectName)); - // App context switch for disabling support for multiple dns entries in a SAN certificate - if (LocalAppContextSwitches.DisableMultipleDNSEntriesInSANCertificate) - { - // old behavior, default for <= 4.6 - value = this.certificate.GetNameInfo(X509NameType.DnsName, false); - if (!string.IsNullOrEmpty(value)) - claims.Add(Claim.CreateDnsClaim(value)); - } - else - { - // new behavior as this is the default long term behavior - // Since a SAN can have multiple DNS entries - string[] entries = GetDnsFromExtensions(this.certificate); - for (int i = 0; i < entries.Length; ++i) - { - claims.Add(Claim.CreateDnsClaim(entries[i])); - } - } + claims.AddRange(GetDnsClaims(this.certificate)); value = this.certificate.GetNameInfo(X509NameType.SimpleName, false); if (!string.IsNullOrEmpty(value)) @@ -258,25 +243,8 @@ public override IEnumerable FindClaims(string claimType, string right) { if (right == null || Rights.PossessProperty.Equals(right)) { - // App context switch for disabling support for multiple dns entries in a SAN certificate - if (LocalAppContextSwitches.DisableMultipleDNSEntriesInSANCertificate) - { - // old behavior, default for <= 4.6 - string value = this.certificate.GetNameInfo(X509NameType.DnsName, false); - if (!string.IsNullOrEmpty(value)) - { - yield return Claim.CreateDnsClaim(value); - } - } - else - { - // new behavior since this is the default long term behavior - string[] entries = GetDnsFromExtensions(certificate); - for (int i = 0; i < entries.Length; ++i) - { - yield return Claim.CreateDnsClaim(entries[i]); - } - } + foreach (var claim in GetDnsClaims(certificate)) + yield return claim; } } else @@ -299,31 +267,44 @@ public override IEnumerable FindClaims(string claimType, string right) } } - // Fixing Bug 795660: SAN having multiple DNS entries - private static string[] GetDnsFromExtensions(X509Certificate2 cert) + private static List GetDnsClaims(X509Certificate2 cert) { - foreach (X509Extension ext in cert.Extensions) + List dnsClaimEntries = new List(); + + // old behavior, default for <= 4.6 + string value = cert.GetNameInfo(X509NameType.DnsName, false); + if (!string.IsNullOrEmpty(value)) + dnsClaimEntries.Add(Claim.CreateDnsClaim(value)); + + // App context switch for disabling support for multiple dns entries in a SAN certificate + // If we can't dynamically parse the alt subject names, we will not add any dns claims ONLY for the alt subject names. + // In this way, if the X509NameType.DnsName was enough to succeed for the out-bound-message. We would have a success. + if (!LocalAppContextSwitches.DisableMultipleDNSEntriesInSANCertificate && X509SubjectAlternativeNameConstants.SuccessfullyInitialized) { - // Extension is SAN or SAN2 - if (ext.Oid.Value == "2.5.29.7" || ext.Oid.Value == "2.5.29.17") + foreach (X509Extension ext in cert.Extensions) { - string asnString = ext.Format(true); - if (string.IsNullOrEmpty(asnString)) + // Extension is SAN or SAN2 + if (ext.Oid.Value == X509SubjectAlternativeNameConstants.SanOid || ext.Oid.Value == X509SubjectAlternativeNameConstants.San2Oid) { - return new string[0]; - } - - string[] rawDnsEntries = asnString.Split(new string[1] { "\n" }, StringSplitOptions.RemoveEmptyEntries); - string[] dnsEntries = new string[rawDnsEntries.Length]; - for (int i = 0; i < rawDnsEntries.Length; ++i) - { - int equalSignIndex = rawDnsEntries[i].IndexOf('='); - dnsEntries[i] = rawDnsEntries[i].Substring(equalSignIndex + 1).Trim(); + string asnString = ext.Format(false); + if (string.IsNullOrWhiteSpace(asnString)) + break; + + // SubjectAlternativeNames might contain something other than a dNSName, + // so we have to parse through and only use the dNSNames + // + string[] rawDnsEntries = asnString.Split(X509SubjectAlternativeNameConstants.SeparatorArray, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < rawDnsEntries.Length; i++) + { + string[] keyval = rawDnsEntries[i].Split(X509SubjectAlternativeNameConstants.Delimiter); + if (string.Equals(keyval[0], X509SubjectAlternativeNameConstants.Identifier)) + dnsClaimEntries.Add(Claim.CreateDnsClaim(keyval[1])); + } } - return dnsEntries; } } - return new string[0]; + + return dnsClaimEntries; } public override IEnumerator GetEnumerator() @@ -367,6 +348,107 @@ public IIdentity Identity get { return this.identity; } } } + + // We don't have a strongly typed extension to parse Subject Alt Names, so we have to do a workaround + // to figure out what the identifier, delimiter, and separator is by using a well-known extension + private static class X509SubjectAlternativeNameConstants + { + public const string SanOid = "2.5.29.7"; + public const string San2Oid = "2.5.29.17"; + + public static string Identifier + { + get; + private set; + } + + public static char Delimiter + { + get; + private set; + } + + public static string Separator + { + get; + private set; + } + + public static string[] SeparatorArray + { + get; + private set; + } + + public static bool SuccessfullyInitialized + { + get; + private set; + } + + // static initializer will run before properties are accessed + static X509SubjectAlternativeNameConstants() + { + // Extracted a well-known X509Extension + byte[] x509ExtensionBytes = new byte[] { + 48, 36, 130, 21, 110, 111, 116, 45, 114, 101, 97, 108, 45, 115, 117, 98, 106, 101, 99, + 116, 45, 110, 97, 109, 101, 130, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109 + }; + const string subjectName = "not-real-subject-name"; + string x509ExtensionFormattedString = string.Empty; + try + { + X509Extension x509Extension = new X509Extension(SanOid, x509ExtensionBytes, true); + x509ExtensionFormattedString = x509Extension.Format(false); + + // Each OS has a different dNSName identifier and delimiter + // On Windows, dNSName == "DNS Name" (localizable), on Linux, dNSName == "DNS" + // e.g., + // Windows: x509ExtensionFormattedString is: "DNS Name=not-real-subject-name, DNS Name=example.com" + // Linux: x509ExtensionFormattedString is: "DNS:not-real-subject-name, DNS:example.com" + // Parse: + + int delimiterIndex = x509ExtensionFormattedString.IndexOf(subjectName) - 1; + Delimiter = x509ExtensionFormattedString[delimiterIndex]; + + // Make an assumption that all characters from the the start of string to the delimiter + // are part of the identifier + Identifier = x509ExtensionFormattedString.Substring(0, delimiterIndex); + + int separatorFirstChar = delimiterIndex + subjectName.Length + 1; + int separatorLength = 1; + for (int i = separatorFirstChar + 1; i < x509ExtensionFormattedString.Length; i++) + { + // We advance until the first character of the identifier to determine what the + // separator is. This assumes that the identifier assumption above is correct + if (x509ExtensionFormattedString[i] == Identifier[0]) + { + break; + } + + separatorLength++; + } + + Separator = x509ExtensionFormattedString.Substring(separatorFirstChar, separatorLength); + SeparatorArray = new string[1] { Separator }; + SuccessfullyInitialized = true; + } + catch (Exception ex) + { + SuccessfullyInitialized = false; + DiagnosticUtility.TraceHandledException( + new FormatException(string.Format(CultureInfo.InvariantCulture, + "There was an error parsing the SubjectAlternativeNames: '{0}'. See inner exception for more details.{1}Detected values were: Identifier: '{2}'; Delimiter:'{3}'; Separator:'{4}'", + x509ExtensionFormattedString, + Environment.NewLine, + Identifier, + Delimiter, + Separator), + ex), + TraceEventType.Warning); + } + } + } } class X509Identity : GenericIdentity, IDisposable diff --git a/System.IdentityModel/System/IdentityModel/LocalAppContextSwitches.cs b/System.IdentityModel/System/IdentityModel/LocalAppContextSwitches.cs index f18ab5dd5..b6bc82224 100644 --- a/System.IdentityModel/System/IdentityModel/LocalAppContextSwitches.cs +++ b/System.IdentityModel/System/IdentityModel/LocalAppContextSwitches.cs @@ -15,9 +15,11 @@ internal static class LocalAppContextSwitches { private const string EnableCachedEmptyDefaultAuthorizationContextString = "Switch.System.IdentityModel.EnableCachedEmptyDefaultAuthorizationContext"; private const string DisableMultipleDNSEntriesInSANCertificateString = "Switch.System.IdentityModel.DisableMultipleDNSEntriesInSANCertificate"; + private const string DisableUpdatingRsaProviderTypeString = "Switch.System.IdentityModel.DisableUpdatingRsaProviderType"; private static int enableCachedEmptyDefaultAuthorizationContext; private static int disableMultipleDNSEntriesInSANCertificate; + private static int disableUpdatingRsaProviderType; public static bool EnableCachedEmptyDefaultAuthorizationContext { @@ -37,16 +39,29 @@ public static bool DisableMultipleDNSEntriesInSANCertificate } } + public static bool DisableUpdatingRsaProviderType + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return LocalAppContext.GetCachedSwitchValue(DisableUpdatingRsaProviderTypeString, ref disableUpdatingRsaProviderType); + } + } + public static void SetDefaultsLessOrEqual_452() { +#pragma warning disable BCL0012 // Define the switches that should be true for 4.5.2 or less, false for 4.6+. LocalAppContext.DefineSwitchDefault(EnableCachedEmptyDefaultAuthorizationContextString, true); +#pragma warning restore BCL0012 } public static void SetDefaultsLessOrEqual_46() { +#pragma warning disable BCL0012 // Define the switches that should be true for 4.6 or less, false for 4.6.1+. LocalAppContext.DefineSwitchDefault(DisableMultipleDNSEntriesInSANCertificateString, true); +#pragma warning restore BCL0012 } } } diff --git a/System.IdentityModel/System/IdentityModel/Tokens/X509AsymmetricSecurityKey.cs b/System.IdentityModel/System/IdentityModel/Tokens/X509AsymmetricSecurityKey.cs index 397194f64..fb1918c6c 100644 --- a/System.IdentityModel/System/IdentityModel/Tokens/X509AsymmetricSecurityKey.cs +++ b/System.IdentityModel/System/IdentityModel/Tokens/X509AsymmetricSecurityKey.cs @@ -309,22 +309,26 @@ public override AsymmetricSignatureFormatter GetSignatureFormatter(string algori { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm")); } - // We support one of the two algoritms, but not both. + + // We support: // XmlDsigDSAUrl = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; // XmlDsigRSASHA1Url = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; + // RsaSha256Signature = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; + AsymmetricAlgorithm privateKey = LevelUpRsa(this.PrivateKey, algorithm); + object algorithmObject = CryptoHelper.GetAlgorithmFromConfig(algorithm); if (algorithmObject != null) { SignatureDescription description = algorithmObject as SignatureDescription; if (description != null) - return description.CreateFormatter(this.PrivateKey); + return description.CreateFormatter(privateKey); try { AsymmetricSignatureFormatter asymmetricSignatureFormatter = algorithmObject as AsymmetricSignatureFormatter; if (asymmetricSignatureFormatter != null) { - asymmetricSignatureFormatter.SetKey(this.PrivateKey); + asymmetricSignatureFormatter.SetKey(privateKey); return asymmetricSignatureFormatter; } } @@ -356,19 +360,10 @@ public override AsymmetricSignatureFormatter GetSignatureFormatter(string algori case SecurityAlgorithms.RsaSha256Signature: // Ensure that we have an RSA algorithm object. - RSACryptoServiceProvider rsa_prov_full = (this.PrivateKey as RSACryptoServiceProvider); - if (rsa_prov_full == null) + RSA rsaSha256 = (privateKey as RSA); + if (rsaSha256 == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.PrivateKeyNotRSA))); - CspParameters csp = new CspParameters(); - csp.ProviderType = 24; - csp.KeyContainerName = rsa_prov_full.CspKeyContainerInfo.KeyContainerName; - csp.KeyNumber = (int)rsa_prov_full.CspKeyContainerInfo.KeyNumber; - if (rsa_prov_full.CspKeyContainerInfo.MachineKeyStore) - csp.Flags = CspProviderFlags.UseMachineKeyStore; - - csp.Flags |= CspProviderFlags.UseExistingKey; - - return new RSAPKCS1SignatureFormatter(new RSACryptoServiceProvider(csp)); + return new RSAPKCS1SignatureFormatter(rsaSha256); default: throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.UnsupportedCryptoAlgorithm, algorithm))); @@ -376,6 +371,45 @@ public override AsymmetricSignatureFormatter GetSignatureFormatter(string algori } + private static AsymmetricAlgorithm LevelUpRsa(AsymmetricAlgorithm asymmetricAlgorithm, string algorithm) + { + // If user turned off leveling up at app level, return + if (LocalAppContextSwitches.DisableUpdatingRsaProviderType) + return asymmetricAlgorithm; + + if (asymmetricAlgorithm == null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("asymmetricAlgorithm")); + + if (string.IsNullOrEmpty(algorithm)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(algorithm, SR.GetString(SR.EmptyOrNullArgumentString, "algorithm")); + + // only level up if alg is sha256 + if (!string.Equals(algorithm, SecurityAlgorithms.RsaSha256Signature)) + return asymmetricAlgorithm; + + RSACryptoServiceProvider rsaCsp = asymmetricAlgorithm as RSACryptoServiceProvider; + if (rsaCsp == null) + return asymmetricAlgorithm; + + // ProviderType == 1(PROV_RSA_FULL) and providerType == 12(PROV_RSA_SCHANNEL) are provider types that only support SHA1. Change them to PROV_RSA_AES=24 that supports SHA2 also. + // Only levels up if the associated key is not a hardware key. + // Another provider type related to rsa, PROV_RSA_SIG == 2 that only supports Sha1 is no longer supported + if ((rsaCsp.CspKeyContainerInfo.ProviderType == 1 || rsaCsp.CspKeyContainerInfo.ProviderType == 12) && !rsaCsp.CspKeyContainerInfo.HardwareDevice) + { + CspParameters csp = new CspParameters(); + csp.ProviderType = 24; + csp.KeyContainerName = rsaCsp.CspKeyContainerInfo.KeyContainerName; + csp.KeyNumber = (int)rsaCsp.CspKeyContainerInfo.KeyNumber; + if (rsaCsp.CspKeyContainerInfo.MachineKeyStore) + csp.Flags = CspProviderFlags.UseMachineKeyStore; + + csp.Flags |= CspProviderFlags.UseExistingKey; + return new RSACryptoServiceProvider(csp); + } + + return rsaCsp; + } + public override bool HasPrivateKey() { return (this.PrivateKey != null); diff --git a/System.Runtime.Caching/System/Caching/CacheMemoryMonitor.cs b/System.Runtime.Caching/System/Caching/CacheMemoryMonitor.cs index 8b32bedf7..05af14359 100644 --- a/System.Runtime.Caching/System/Caching/CacheMemoryMonitor.cs +++ b/System.Runtime.Caching/System/Caching/CacheMemoryMonitor.cs @@ -27,7 +27,7 @@ internal sealed class CacheMemoryMonitor : MemoryMonitor, IDisposable { private long[] _cacheSizeSamples; private DateTime[] _cacheSizeSampleTimes; private int _idx; - private SRef _sizedRef; + private SRefMultiple _sizedRefMultiple; private int _gen2Count; private long _memoryLimit; @@ -51,7 +51,7 @@ internal CacheMemoryMonitor(MemoryCache memoryCache, int cacheMemoryLimitMegabyt private void InitDisposableMembers(int cacheMemoryLimitMegabytes) { bool dispose = true; try { - _sizedRef = new SRef(_memoryCache); + _sizedRefMultiple = new SRefMultiple(_memoryCache.AllSRefTargets); SetLimit(cacheMemoryLimitMegabytes); InitHistory(); dispose = false; @@ -112,8 +112,8 @@ private static long AutoPrivateBytesLimit { } public void Dispose() { - SRef sref = _sizedRef; - if (sref != null && Interlocked.CompareExchange(ref _sizedRef, null, sref) == sref) { + SRefMultiple sref = _sizedRefMultiple; + if (sref != null && Interlocked.CompareExchange(ref _sizedRefMultiple, null, sref) == sref) { sref.Dispose(); } IMemoryCacheManager memoryCacheManager = s_memoryCacheManager; @@ -139,7 +139,7 @@ protected override int GetCurrentPressure() { // This update must happen, otherwise the CacheManager won't // know the total cache size. int gen2Count = GC.CollectionCount(2); - SRef sref = _sizedRef; + SRefMultiple sref = _sizedRefMultiple; if (gen2Count != _gen2Count && sref != null) { // update _gen2Count _gen2Count = gen2Count; diff --git a/System.Runtime.Caching/System/Caching/MemoryCache.cs b/System.Runtime.Caching/System/Caching/MemoryCache.cs index e807bbdd8..cfc0c0315 100644 --- a/System.Runtime.Caching/System/Caching/MemoryCache.cs +++ b/System.Runtime.Caching/System/Caching/MemoryCache.cs @@ -27,9 +27,8 @@ public class MemoryCache : ObjectCache, IEnumerable, IDisposable { private static object s_initLock = new object(); private static MemoryCache s_defaultCache; private static CacheEntryRemovedCallback s_sentinelRemovedCallback = new CacheEntryRemovedCallback(SentinelEntry.OnCacheEntryRemovedCallback); - private MemoryCacheStore[] _stores; + private GCHandleRef[] _storeRefs; private int _storeCount; - private int _storeMask; private int _disposed; private MemoryCacheStatistics _stats; private string _name; @@ -150,8 +149,18 @@ internal MemoryCacheStore GetStore(MemoryCacheKey cacheKey) { if (hashCode < 0) { hashCode = (hashCode == Int32.MinValue) ? 0 : -hashCode; } - int idx = hashCode & _storeMask; - return _stores[idx]; + int idx = hashCode % _storeCount; + return _storeRefs[idx].Target; + } + + internal object[] AllSRefTargets { + get { + var allStores = new MemoryCacheStore[_storeCount]; + for (int i = 0; i < _storeCount; i++) { + allStores[i] = _storeRefs[i].Target; + } + return allStores; + } } [SecuritySafeCritical] @@ -166,8 +175,8 @@ private void InitDisposableMembers(NameValueCollection config) { catch { // ignore exceptions from perf counters } - for (int i = 0; i < _stores.Length; i++) { - _stores[i] = new MemoryCacheStore(this, _perfCounters); + for (int i = 0; i < _storeCount; i++) { + _storeRefs[i] = new GCHandleRef (new MemoryCacheStore(this, _perfCounters)); } _stats = new MemoryCacheStatistics(this, config); AppDomain appDomain = Thread.GetDomain(); @@ -307,8 +316,7 @@ internal MemoryCache(string name, NameValueCollection config, bool configLess) { private void Init(NameValueCollection config) { _storeCount = Environment.ProcessorCount; - _storeMask = _storeCount - 1; - _stores = new MemoryCacheStore[_storeCount]; + _storeRefs = new GCHandleRef[_storeCount]; InitDisposableMembers(config); } @@ -378,10 +386,10 @@ public void Dispose() { if (_stats != null) { _stats.Dispose(); } - if (_stores != null) { - foreach (MemoryCacheStore store in _stores) { - if (store != null) { - store.Dispose(); + if (_storeRefs != null) { + foreach (var storeRef in _storeRefs) { + if (storeRef != null) { + storeRef.Dispose(); } } } @@ -428,8 +436,8 @@ internal MemoryCacheEntry GetEntry(String key) { IEnumerator IEnumerable.GetEnumerator() { Hashtable h = new Hashtable(); if (!IsDisposed) { - foreach (MemoryCacheStore store in _stores) { - store.CopyTo(h); + foreach (var storeRef in _storeRefs) { + storeRef.Target.CopyTo(h); } } return h.GetEnumerator(); @@ -438,8 +446,8 @@ IEnumerator IEnumerable.GetEnumerator() { protected override IEnumerator> GetEnumerator() { Dictionary h = new Dictionary(); if (!IsDisposed) { - foreach (MemoryCacheStore store in _stores) { - store.CopyTo(h); + foreach (var storeRef in _storeRefs) { + storeRef.Target.CopyTo(h); } } return h.GetEnumerator(); @@ -457,8 +465,8 @@ public long Trim(int percent) { } long trimmed = 0; if (_disposed == 0) { - foreach (MemoryCacheStore store in _stores) { - trimmed += store.TrimInternal(percent); + foreach (var storeRef in _storeRefs) { + trimmed += storeRef.Target.TrimInternal(percent); } } return trimmed; @@ -666,8 +674,8 @@ public override long GetCount(string regionName = null) { } long count = 0; if (!IsDisposed) { - foreach (MemoryCacheStore store in _stores) { - count += store.Count; + foreach (var storeRef in _storeRefs) { + count += storeRef.Target.Count; } } return count; diff --git a/System.Runtime.Caching/System/Caching/MemoryCacheStatistics.cs b/System.Runtime.Caching/System/Caching/MemoryCacheStatistics.cs index 61d3ce3ac..99196c05f 100644 --- a/System.Runtime.Caching/System/Caching/MemoryCacheStatistics.cs +++ b/System.Runtime.Caching/System/Caching/MemoryCacheStatistics.cs @@ -27,7 +27,7 @@ internal sealed class MemoryCacheStatistics : IDisposable { private int _lastTrimPercent; private DateTime _lastTrimTime; private int _pollingInterval; - private Timer _timer; + private GCHandleRef _timerHandleRef; private Object _timerLock; private long _totalCountBeforeTrim; @@ -44,16 +44,18 @@ private MemoryCacheStatistics() { private void AdjustTimer() { lock (_timerLock) { - if (_timer == null) + if (_timerHandleRef == null) return; + Timer timer = _timerHandleRef.Target; + // the order of these if statements is important // When above the high pressure mark, interval should be 5 seconds or less if (_physicalMemoryMonitor.IsAboveHighPressure() || _cacheMemoryMonitor.IsAboveHighPressure()) { if (_pollingInterval > MEMORYSTATUS_INTERVAL_5_SECONDS) { _pollingInterval = MEMORYSTATUS_INTERVAL_5_SECONDS; - _timer.Change(_pollingInterval, _pollingInterval); + timer.Change(_pollingInterval, _pollingInterval); } return; } @@ -65,7 +67,7 @@ private void AdjustTimer() { int newPollingInterval = Math.Min(_configPollingInterval, MEMORYSTATUS_INTERVAL_30_SECONDS); if (_pollingInterval != newPollingInterval) { _pollingInterval = newPollingInterval; - _timer.Change(_pollingInterval, _pollingInterval); + timer.Change(_pollingInterval, _pollingInterval); } return; } @@ -73,7 +75,7 @@ private void AdjustTimer() { // there is no pressure, interval should be the value from config if (_pollingInterval != _configPollingInterval) { _pollingInterval = _configPollingInterval; - _timer.Change(_pollingInterval, _pollingInterval); + timer.Change(_pollingInterval, _pollingInterval); } } } @@ -126,7 +128,8 @@ private void InitDisposableMembers() { bool dispose = true; try { _cacheMemoryMonitor = new CacheMemoryMonitor(_memoryCache, _configCacheMemoryLimitMegabytes); - _timer = new Timer(new TimerCallback(CacheManagerTimerCallback), null, _configPollingInterval, _configPollingInterval); + Timer timer = new Timer(new TimerCallback(CacheManagerTimerCallback), null, _configPollingInterval, _configPollingInterval); + _timerHandleRef = new GCHandleRef(timer); dispose = false; } finally { @@ -246,9 +249,9 @@ internal long CacheManagerThread(int minPercent) { public void Dispose() { if (Interlocked.Exchange(ref _disposed, 1) == 0) { lock (_timerLock) { - Timer timer = _timer; - if (timer != null && Interlocked.CompareExchange(ref _timer, null, timer) == timer) { - timer.Dispose(); + GCHandleRef timerHandleRef = _timerHandleRef; + if (timerHandleRef != null && Interlocked.CompareExchange(ref _timerHandleRef, null, timerHandleRef) == timerHandleRef) { + timerHandleRef.Dispose(); Dbg.Trace("MemoryCacheStats", "Stopped CacheMemoryTimers"); } } diff --git a/System.Runtime.Caching/System/Caching/SRef.cs b/System.Runtime.Caching/System/Caching/SRef.cs index 27ee134e4..44122d6d2 100644 --- a/System.Runtime.Caching/System/Caching/SRef.cs +++ b/System.Runtime.Caching/System/Caching/SRef.cs @@ -7,6 +7,7 @@ using System.Security; using System.Security.Permissions; using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; namespace System.Runtime.Caching { internal class SRef { @@ -48,4 +49,77 @@ internal void Dispose() { CultureInfo.InvariantCulture); } } + + internal class SRefMultiple { + private SRef[] _srefs; + private long[] _sizes; // Getting SRef size in the debugger is extremely tedious so we keep the last read value here + + internal SRefMultiple(object[] targets) { + _srefs = new SRef[targets.Length]; + _sizes = new long[targets.Length]; + for (int i = 0; i < targets.Length; i++) { + _srefs[i] = new SRef(targets[i]); + } + } + + internal long ApproximateSize { + get { + long size = 0; + for (int i = 0; i < _srefs.Length; i++) { + size += (_sizes[i] = _srefs[i].ApproximateSize); + } + return size; + } + } + + internal void Dispose() { + foreach (SRef s in _srefs) { + s.Dispose(); + } + } + } + + internal class GCHandleRef : IDisposable + where T : class, IDisposable { + GCHandle _handle; + T _t; + + [SecuritySafeCritical] + [PermissionSet(SecurityAction.Assert, Unrestricted = true)] + public GCHandleRef(T t) { + _handle = GCHandle.Alloc(t); + } + + public T Target { + [SecuritySafeCritical] + [PermissionSet(SecurityAction.Assert, Unrestricted = true)] + get { + try { + T t = (T)_handle.Target; + if (t != null) { + return t; + } + } + catch (InvalidOperationException) { + // use the normal reference instead of throwing an exception when _handle is already freed + } + return _t; + } + } + + [SecuritySafeCritical] + [PermissionSet(SecurityAction.Assert, Unrestricted = true)] + public void Dispose() { + Target.Dispose(); + // Safe to call Dispose more than once but not thread-safe + if (_handle.IsAllocated) { + // We must free the GC handle to avoid leaks. + // However after _handle is freed we no longer have access to its Target + // which will cause AVs and various race conditions under stress. + // We revert to using normal references after disposing the GC handle + _t = (T)_handle.Target; + _handle.Free(); + } + } + } } diff --git a/System.Runtime.Serialization/System/Runtime/Serialization/DataContract.cs b/System.Runtime.Serialization/System/Runtime/Serialization/DataContract.cs index f4697ca68..368467483 100644 --- a/System.Runtime.Serialization/System/Runtime/Serialization/DataContract.cs +++ b/System.Runtime.Serialization/System/Runtime/Serialization/DataContract.cs @@ -454,7 +454,8 @@ internal static DataContract GetGetOnlyCollectionDataContractSkipValidation(int if (dataContract == null) { dataContract = CreateGetOnlyCollectionDataContract(id, typeHandle, type); - dataContractCache[id] = dataContract; + + AssignDataContractToId(dataContract, id); } return dataContract; } @@ -476,7 +477,10 @@ internal static int GetIdForInitialization(ClassDataContract classContract) { return id; } - for (int i = 0; i < DataContractCriticalHelper.dataContractID; i++) + + int currentDataContractId = DataContractCriticalHelper.dataContractID; + + for (int i = 0; i < currentDataContractId; i++) { if (ContractMatches(classContract, dataContractCache[i])) { @@ -538,49 +542,66 @@ static IntRef GetNextId() // check whether a corresponding update is required in ClassDataContract.IsNonAttributedTypeValidForSerialization static DataContract CreateDataContract(int id, RuntimeTypeHandle typeHandle, Type type) { - lock (createDataContractLock) + DataContract dataContract = dataContractCache[id]; + + if (dataContract == null) { - DataContract dataContract = dataContractCache[id]; - if (dataContract == null) + lock (createDataContractLock) { - if (type == null) - type = Type.GetTypeFromHandle(typeHandle); - type = UnwrapNullableType(type); - type = GetDataContractAdapterType(type); - dataContract = GetBuiltInDataContract(type); + dataContract = dataContractCache[id]; + if (dataContract == null) { - if (type.IsArray) - dataContract = new CollectionDataContract(type); - else if (type.IsEnum) - dataContract = new EnumDataContract(type); - else if (type.IsGenericParameter) - dataContract = new GenericParameterDataContract(type); - else if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type)) - dataContract = new XmlDataContract(type); - else + if (type == null) + type = Type.GetTypeFromHandle(typeHandle); + type = UnwrapNullableType(type); + type = GetDataContractAdapterType(type); + dataContract = GetBuiltInDataContract(type); + if (dataContract == null) { - //if (type.ContainsGenericParameters) - // ThrowInvalidDataContractException(SR.GetString(SR.TypeMustNotBeOpenGeneric, type), type); - if (type.IsPointer) - type = Globals.TypeOfReflectionPointer; - - if (!CollectionDataContract.TryCreate(type, out dataContract)) + if (type.IsArray) + dataContract = new CollectionDataContract(type); + else if (type.IsEnum) + dataContract = new EnumDataContract(type); + else if (type.IsGenericParameter) + dataContract = new GenericParameterDataContract(type); + else if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type)) + dataContract = new XmlDataContract(type); + else { - if (type.IsSerializable || type.IsDefined(Globals.TypeOfDataContractAttribute, false) || ClassDataContract.IsNonAttributedTypeValidForSerialization(type)) - { - dataContract = new ClassDataContract(type); - } - else + //if (type.ContainsGenericParameters) + // ThrowInvalidDataContractException(SR.GetString(SR.TypeMustNotBeOpenGeneric, type), type); + if (type.IsPointer) + type = Globals.TypeOfReflectionPointer; + + if (!CollectionDataContract.TryCreate(type, out dataContract)) { - ThrowInvalidDataContractException(SR.GetString(SR.TypeNotSerializable, type), type); + if (type.IsSerializable || type.IsDefined(Globals.TypeOfDataContractAttribute, false) || ClassDataContract.IsNonAttributedTypeValidForSerialization(type)) + { + dataContract = new ClassDataContract(type); + } + else + { + ThrowInvalidDataContractException(SR.GetString(SR.TypeNotSerializable, type), type); + } } } } - } + + AssignDataContractToId(dataContract, id); + } } + } + + return dataContract; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void AssignDataContractToId(DataContract dataContract, int id) + { + lock (cacheLock) + { dataContractCache[id] = dataContract; - return dataContract; } } @@ -1196,7 +1217,8 @@ internal MethodInfo ParseMethod parseMethodSet = true; } - return parseMethod; } + return parseMethod; + } } internal virtual void WriteRootElement(XmlWriterDelegator writer, XmlDictionaryString name, XmlDictionaryString ns) diff --git a/System.Runtime.Serialization/System/Runtime/Serialization/Json/JsonWriterDelegator.cs b/System.Runtime.Serialization/System/Runtime/Serialization/Json/JsonWriterDelegator.cs index dc71490b9..60f323a52 100644 --- a/System.Runtime.Serialization/System/Runtime/Serialization/Json/JsonWriterDelegator.cs +++ b/System.Runtime.Serialization/System/Runtime/Serialization/Json/JsonWriterDelegator.cs @@ -202,7 +202,15 @@ void WriteDateTimeInDefaultFormat(DateTime value) // This will break round-tripping of these dates (see bug 9690 in CSD Developer Framework) if (value.Kind != DateTimeKind.Utc) { - long tickCount = value.Ticks - TimeZone.CurrentTimeZone.GetUtcOffset(value).Ticks; + long tickCount; + if (!LocalAppContextSwitches.DoNotUseTimeZoneInfo) + { + tickCount = value.Ticks - TimeZoneInfo.Local.GetUtcOffset(value).Ticks; + } + else + { + tickCount = value.Ticks - TimeZone.CurrentTimeZone.GetUtcOffset(value).Ticks; + } if ((tickCount > DateTime.MaxValue.Ticks) || (tickCount < DateTime.MinValue.Ticks)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( @@ -218,7 +226,15 @@ void WriteDateTimeInDefaultFormat(DateTime value) case DateTimeKind.Unspecified: case DateTimeKind.Local: // +"zzzz"; - TimeSpan ts = TimeZone.CurrentTimeZone.GetUtcOffset(value.ToLocalTime()); + TimeSpan ts; + if (!LocalAppContextSwitches.DoNotUseTimeZoneInfo) + { + ts = TimeZoneInfo.Local.GetUtcOffset(value.ToLocalTime()); + } + else + { + ts = TimeZone.CurrentTimeZone.GetUtcOffset(value.ToLocalTime()); + } if (ts.Ticks < 0) { writer.WriteString("-"); diff --git a/System.Runtime.Serialization/System/Runtime/Serialization/SchemaExporter.cs b/System.Runtime.Serialization/System/Runtime/Serialization/SchemaExporter.cs index b2c4586e3..ff5704a55 100644 --- a/System.Runtime.Serialization/System/Runtime/Serialization/SchemaExporter.cs +++ b/System.Runtime.Serialization/System/Runtime/Serialization/SchemaExporter.cs @@ -75,7 +75,7 @@ void ExportSerializationSchema() if (!Schemas.Contains(Globals.SerializationNamespace)) { StringReader reader = new StringReader(Globals.SerializationSchema); - XmlSchema schema = XmlSchema.Read(reader, null); + XmlSchema schema = XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); if (schema == null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.CouldNotReadSerializationSchema, Globals.SerializationNamespace))); Schemas.Add(schema); diff --git a/System.Runtime.Serialization/System/Runtime/Serialization/SchemaImporter.cs b/System.Runtime.Serialization/System/Runtime/Serialization/SchemaImporter.cs index 32d06e427..29885641a 100644 --- a/System.Runtime.Serialization/System/Runtime/Serialization/SchemaImporter.cs +++ b/System.Runtime.Serialization/System/Runtime/Serialization/SchemaImporter.cs @@ -51,7 +51,7 @@ internal void Import() if (!schemaSet.Contains(Globals.SerializationNamespace)) { StringReader reader = new StringReader(Globals.SerializationSchema); - XmlSchema schema = XmlSchema.Read(reader, null); + XmlSchema schema = XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); if (schema == null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.CouldNotReadSerializationSchema, Globals.SerializationNamespace))); schemaSet.Add(schema); diff --git a/System.ServiceModel.Activation/System/ServiceModel/Activation/HostedAspNetEnvironment.cs b/System.ServiceModel.Activation/System/ServiceModel/Activation/HostedAspNetEnvironment.cs index c0dc0f329..4e785e5b1 100644 --- a/System.ServiceModel.Activation/System/ServiceModel/Activation/HostedAspNetEnvironment.cs +++ b/System.ServiceModel.Activation/System/ServiceModel/Activation/HostedAspNetEnvironment.cs @@ -101,20 +101,13 @@ public override bool UsingIntegratedPipeline } } - // Provides the version of the WebSocket protocol supported by IIS. Throws an exception if called before we determined the value. + // Provides the version of the WebSocket protocol supported by IIS. // Returns null if WebSockets are not supported (because the IIS WebSocketModule is not installed or enabled). public override string WebSocketVersion { get { - if (isWebSocketVersionSet) - { - return webSocketVersion; - } - else - { - throw Fx.AssertAndFailFast("The supported WebSocket protocol version is not determined at this time."); - } + return isWebSocketVersionSet ? webSocketVersion : null; } } @@ -136,8 +129,6 @@ public static void Enable() /// public static void TrySetWebSocketVersion(HttpApplication application) { - Fx.Assert(application != null, "Invalid argument."); - if (!isWebSocketVersionSet) { webSocketVersion = application.Request.ServerVariables[WebSocketVersionServerProperty]; diff --git a/System.ServiceModel.Internals/System/Runtime/Diagnostics/EtwDiagnosticTrace.cs b/System.ServiceModel.Internals/System/Runtime/Diagnostics/EtwDiagnosticTrace.cs index 4a81f353c..c84160682 100644 --- a/System.ServiceModel.Internals/System/Runtime/Diagnostics/EtwDiagnosticTrace.cs +++ b/System.ServiceModel.Internals/System/Runtime/Diagnostics/EtwDiagnosticTrace.cs @@ -288,6 +288,7 @@ public void TraceTransfer(Guid newId) [Fx.Tag.SecurityNote(Critical = "Usage of EventDescriptor, which is protected by a LinkDemand")] [SecurityCritical] + [SuppressMessage("Microsoft.Security.Xml", "CA3057:DoNotUseLoadXml", Justification = "It is internal code. No security concern.")] public void WriteTraceSource(ref EventDescriptor eventDescriptor, string description, TracePayload payload) { if (this.TracingEnabled) diff --git a/System.ServiceModel/InternalApis/Clr/inc/AppContextDefaultValues.cs b/System.ServiceModel/InternalApis/Clr/inc/AppContextDefaultValues.cs index 8a130a0f3..9224bf8b8 100644 --- a/System.ServiceModel/InternalApis/Clr/inc/AppContextDefaultValues.cs +++ b/System.ServiceModel/InternalApis/Clr/inc/AppContextDefaultValues.cs @@ -3,6 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== + +// There are cases where we have multiple assemblies that are going to import this file and +// if they are going to also have InternalsVisibleTo between them, there will be a compiler warning +// that the type is found both in the source and in a referenced assembly. The compiler will prefer +// the version of the type defined in the source +// +// In order to disable the warning for this type we are disabling this warning for this entire file. +#pragma warning disable 436 + using System; using System.Collections.Generic; @@ -167,3 +176,5 @@ private static bool TryParseFrameworkName(String frameworkName, out String ident static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version); } } + +#pragma warning restore 436 diff --git a/System.ServiceModel/InternalApis/Clr/inc/LocalAppContext.cs b/System.ServiceModel/InternalApis/Clr/inc/LocalAppContext.cs index f05b599ed..33662b542 100644 --- a/System.ServiceModel/InternalApis/Clr/inc/LocalAppContext.cs +++ b/System.ServiceModel/InternalApis/Clr/inc/LocalAppContext.cs @@ -4,6 +4,14 @@ // // ==--== +// There are cases where we have multiple assemblies that are going to import this file and +// if they are going to also have InternalsVisibleTo between them, there will be a compiler warning +// that the type is found both in the source and in a referenced assembly. The compiler will prefer +// the version of the type defined in the source +// +// In order to disable the warning for this type we are disabling this warning for this entire file. +#pragma warning disable 436 + // NOTE: This file should not be included in mscorlib. This should only be included in FX libraries that need to provide switches using System; using System.Collections.Generic; @@ -126,3 +134,5 @@ internal static void DefineSwitchDefault(string switchName, bool initialValue) } } } + +#pragma warning restore 436 diff --git a/System.ServiceModel/System/ServiceModel/AppContextDefaultValues.Default.cs b/System.ServiceModel/System/ServiceModel/AppContextDefaultValues.Default.cs index 5f591137e..e086fad27 100644 --- a/System.ServiceModel/System/ServiceModel/AppContextDefaultValues.Default.cs +++ b/System.ServiceModel/System/ServiceModel/AppContextDefaultValues.Default.cs @@ -27,6 +27,11 @@ static partial void PopulateDefaultValuesPartial(string platformIdentifier, stri LocalAppContextSwitches.SetDefaultsLessOrEqual_452(); } + if (version <= 40601) + { + LocalAppContextSwitches.SetDefaultsLessOrEqual_461(); + } + break; } } diff --git a/System.ServiceModel/System/ServiceModel/Channels/HttpChannelListener.cs b/System.ServiceModel/System/ServiceModel/Channels/HttpChannelListener.cs index 785a9c059..5dac93bab 100644 --- a/System.ServiceModel/System/ServiceModel/Channels/HttpChannelListener.cs +++ b/System.ServiceModel/System/ServiceModel/Channels/HttpChannelListener.cs @@ -839,17 +839,11 @@ public HttpChannelListener(HttpTransportBindingElement bindingElement, BindingCo { AspNetEnvironment env = AspNetEnvironment.Current; - // When IIS hosted, WebSockets can be used if the pipeline mode is integrated and the WebSocketModule is loaded. - // Otherwise, the client requests will not be upgraded to web sockets (see the code in HostedHttpTransportManager.HttpContextReceived(..)). - // We do the checks below (and fail the service activation), to avoid starting a WebSockets listener that won't get called. + // When IIS hosted, WebSockets can be used if the pipeline mode is integrated if (!env.UsingIntegratedPipeline) { throw FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.WebSocketsNotSupportedInClassicPipeline))); } - else if (!env.IsWebSocketModuleLoaded) - { - throw FxTrace.Exception.AsError(new NotSupportedException(SR.GetString(SR.WebSocketModuleNotLoaded))); - } } else if (!WebSocketHelper.OSSupportsWebSockets()) { diff --git a/System.ServiceModel/System/ServiceModel/Channels/PipeConnection.cs b/System.ServiceModel/System/ServiceModel/Channels/PipeConnection.cs index f50e36671..ee3fbc127 100644 --- a/System.ServiceModel/System/ServiceModel/Channels/PipeConnection.cs +++ b/System.ServiceModel/System/ServiceModel/Channels/PipeConnection.cs @@ -6,6 +6,7 @@ namespace System.ServiceModel.Channels { using System.Collections.Generic; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Net; @@ -1684,6 +1685,9 @@ internal static string GetPipeName(Uri uri, IPipeTransportFactorySettings transp // then the Exact HostName, and lastly the WeakWildcard string[] hostChoices = new string[] { "+", uri.Host, "*" }; bool[] globalChoices = new bool[] { true, false }; + string matchPath = String.Empty; + string matchPipeName = null; + for (int i = 0; i < hostChoices.Length; i++) { for (int iGlobal = 0; iGlobal < globalChoices.Length; iGlobal++) @@ -1696,7 +1700,7 @@ internal static string GetPipeName(Uri uri, IPipeTransportFactorySettings transp continue; } - // walk up the path hierarchy, looking for first match + // walk up the path hierarchy, looking for match string path = PipeUri.GetPath(uri); while (path.Length > 0) @@ -1713,7 +1717,21 @@ internal static string GetPipeName(Uri uri, IPipeTransportFactorySettings transp string pipeName = sharedMemory.GetPipeName(appInfo); if (pipeName != null) { - return pipeName; + // Found a matching pipe name. + // If the best match app setting is enabled, save the match if it is the best so far and continue. + // Otherwise, just return the first match we find. + if (ServiceModelAppSettings.UseBestMatchNamedPipeUri) + { + if (path.Length > matchPath.Length) + { + matchPath = path; + matchPipeName = pipeName; + } + } + else + { + return pipeName; + } } } finally @@ -1733,9 +1751,14 @@ internal static string GetPipeName(Uri uri, IPipeTransportFactorySettings transp } } - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( - new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, uri.AbsoluteUri), - new PipeException(SR.GetString(SR.PipeEndpointNotFound, uri.AbsoluteUri)))); + if (string.IsNullOrEmpty(matchPipeName)) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( + new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, uri.AbsoluteUri), + new PipeException(SR.GetString(SR.PipeEndpointNotFound, uri.AbsoluteUri)))); + } + + return matchPipeName; } public IAsyncResult BeginConnect(Uri uri, TimeSpan timeout, AsyncCallback callback, object state) @@ -1758,7 +1781,6 @@ void PrepareConnect(Uri remoteUri, TimeSpan timeout, out string resolvedAddress, new StringTraceRecord("Uri", remoteUri.ToString()), this, null); } resolvedAddress = GetPipeName(remoteUri, this.pipeSettings); - const int backoffBufferMilliseconds = 150; TimeSpan backoffTimeout; if (timeout >= TimeSpan.FromMilliseconds(backoffBufferMilliseconds * 2)) @@ -2815,6 +2837,7 @@ static string BuildSharedMemoryName(string hostName, string path, bool global) return builder.ToString(); } + [SuppressMessage("Microsoft.Security.Cryptography", "CA5354:DoNotUseSHA1", Justification = "Cannot change. It will cause compatibility issue. Not used for cryptographic purposes.")] static HashAlgorithm GetHashAlgorithm() { if (SecurityUtilsEx.RequiresFipsCompliance) diff --git a/System.ServiceModel/System/ServiceModel/Channels/TransportDefaults.cs b/System.ServiceModel/System/ServiceModel/Channels/TransportDefaults.cs index 4de327474..d2c0f72a5 100644 --- a/System.ServiceModel/System/ServiceModel/Channels/TransportDefaults.cs +++ b/System.ServiceModel/System/ServiceModel/Channels/TransportDefaults.cs @@ -202,8 +202,8 @@ static class TransportDefaults internal const bool RequireClientCertificate = false; internal const int MaxFaultSize = MaxBufferSize; internal const int MaxSecurityFaultSize = 16384; - internal const SslProtocols SslProtocols = System.Security.Authentication.SslProtocols.Ssl3 | - System.Security.Authentication.SslProtocols.Tls | + + internal const SslProtocols SslProtocols = System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls12; diff --git a/System.ServiceModel/System/ServiceModel/Channels/UnsafeNativeMethods.cs b/System.ServiceModel/System/ServiceModel/Channels/UnsafeNativeMethods.cs index bade3dab6..275270b40 100644 --- a/System.ServiceModel/System/ServiceModel/Channels/UnsafeNativeMethods.cs +++ b/System.ServiceModel/System/ServiceModel/Channels/UnsafeNativeMethods.cs @@ -1101,31 +1101,82 @@ internal static extern int BCryptGetFipsAlgorithmMode( #if !FEATURE_CORECLR - // On CoreCLR this is not the way to determine if a process is a tailored application (which means APPX). - // On CoreCLR AppX is determined by a flag past to the host which is exposed by AppDomain.IsAppXProcess in mscorlib. - // The reason for this if-def is to ensure nobody takes a dependency on this on CoreCLR. - + private static IntPtr GetCurrentProcessToken() { return new IntPtr(-4); } + + enum AppPolicyClrCompat + { + AppPolicyClrCompat_Others = 0, + AppPolicyClrCompat_ClassicDesktop = 1, + AppPolicyClrCompat_Universal = 2, + AppPolicyClrCompat_PackagedDesktop = 3 + }; + + [DllImport(KERNEL32, CharSet = CharSet.None, EntryPoint = "AppPolicyGetClrCompat")] + [System.Security.SecuritySafeCritical] + [return: MarshalAs(UnmanagedType.I4)] + private static extern Int32 _AppPolicyGetClrCompat(IntPtr processToken, out AppPolicyClrCompat appPolicyClrCompat); + // AppModel.h functions (Win8+) [DllImport(KERNEL32, CharSet = CharSet.None, EntryPoint = "GetCurrentPackageId")] - [SecurityCritical] + [System.Security.SecuritySafeCritical] [return: MarshalAs(UnmanagedType.I4)] - private static extern Int32 GetCurrentPackageId(ref Int32 pBufferLength, Byte[] pBuffer); + private static extern Int32 _GetCurrentPackageId(ref Int32 pBufferLength, Byte[] pBuffer); - [Fx.Tag.SecurityNote( - Critical = "Critical because it calls the native function GetCurrentPackageId.", - Safe = "Safe because it takes no user input and it doesn't leak security sensitive information.")] - [SecuritySafeCritical] + [DllImport(KERNEL32, CharSet=System.Runtime.InteropServices.CharSet.Auto, BestFitMapping=false)] + [ResourceExposure(ResourceScope.Machine)] + private static extern IntPtr GetModuleHandle(string modName); + + // Copied from Win32Native.cs + // Note - do NOT use this to call methods. Use P/Invoke, which will + // do much better things w.r.t. marshaling, pinning memory, security + // stuff, better interactions with thread aborts, etc. This is used + // solely by DoesWin32MethodExist for avoiding try/catch EntryPointNotFoundException + // in scenarios where an OS Version check is insufficient + [DllImport(KERNEL32, CharSet=CharSet.Ansi, BestFitMapping=false, SetLastError=true, ExactSpelling=true)] + [ResourceExposure(ResourceScope.None)] + private static extern IntPtr GetProcAddress(IntPtr hModule, String methodName); + + [System.Security.SecurityCritical] // auto-generated + private static bool DoesWin32MethodExist(String moduleName, String methodName) + { + // GetModuleHandle does not increment the module's ref count, so we don't need to call FreeLibrary. + IntPtr hModule = GetModuleHandle(moduleName); + if (hModule == IntPtr.Zero) { + System.Diagnostics.Debug.Assert(hModule != IntPtr.Zero, "GetModuleHandle failed. Dll isn't loaded?"); + return false; + } + IntPtr functionPointer = GetProcAddress(hModule, methodName); + return (functionPointer != IntPtr.Zero); + } + + // On CoreCLR this is not the way to determine if a process is a tailored application (which means APPX). + // On CoreCLR AppX is determined by a flag past to the host which is exposed by AppDomain.IsAppXProcess in mscorlib. + // The reason for this if-def is to ensure nobody takes a dependency on this on CoreCLR. + [System.Security.SecuritySafeCritical] private static bool _IsTailoredApplication() { - if (OSEnvironmentHelper.IsAtLeast(OSVersion.Win8)) + Version windows8Version = new Version(6, 2, 0, 0); + OperatingSystem os = Environment.OSVersion; + bool osSupportsPackagedProcesses = os.Platform == PlatformID.Win32NT && os.Version >= windows8Version; + + if (osSupportsPackagedProcesses && DoesWin32MethodExist(KERNEL32, "AppPolicyGetClrCompat")) { - int bufLen = 0; - // Will return ERROR_INSUFFICIENT_BUFFER when running within a tailored application, + // Use AppPolicyGetClrCompat if it is available. Return true if and only if this is a UWA which means if + // this is packaged desktop app this method will return false. This may cause some confusion however + // this is necessary to make the behavior of packaged desktop apps identical to desktop apps. + AppPolicyClrCompat appPolicyClrCompat; + return _AppPolicyGetClrCompat(GetCurrentProcessToken(), out appPolicyClrCompat) == ERROR_SUCCESS && + appPolicyClrCompat == AppPolicyClrCompat.AppPolicyClrCompat_Universal; + } + else if(osSupportsPackagedProcesses && DoesWin32MethodExist(KERNEL32, "GetCurrentPackageId")) + { + Int32 bufLen = 0; + // Will return ERROR_INSUFFICIENT_BUFFER when running within a packaged application, // and will return ERROR_NO_PACKAGE_IDENTITY otherwise. - return GetCurrentPackageId(ref bufLen, null) == ERROR_INSUFFICIENT_BUFFER; + return _GetCurrentPackageId(ref bufLen, null) == ERROR_INSUFFICIENT_BUFFER; } else - { + { // We must be running on a downlevel OS. return false; } } diff --git a/System.ServiceModel/System/ServiceModel/Channels/WebSocketHelper.cs b/System.ServiceModel/System/ServiceModel/Channels/WebSocketHelper.cs index 928d9be8f..b99db25d1 100644 --- a/System.ServiceModel/System/ServiceModel/Channels/WebSocketHelper.cs +++ b/System.ServiceModel/System/ServiceModel/Channels/WebSocketHelper.cs @@ -6,6 +6,7 @@ namespace System.ServiceModel.Channels { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Net; @@ -44,6 +45,7 @@ static class WebSocketHelper static readonly HashSet InvalidSeparatorSet = new HashSet(new char[] { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ' }); static string currentWebSocketVersion; + [SuppressMessage("Microsoft.Security.Cryptography", "CA5354:DoNotUseSHA1", Justification = "Cannot change. Usage of SHA1 is part of WebSocket spec. Justification in RFC6455 section 10.8")] internal static string ComputeAcceptHeader(string webSocketKey) { Fx.Assert(webSocketKey != null, "webSocketKey should not be null."); diff --git a/System.ServiceModel/System/ServiceModel/Configuration/Properties.cs b/System.ServiceModel/System/ServiceModel/Configuration/Properties.cs index 35c782978..a457268b0 100644 --- a/System.ServiceModel/System/ServiceModel/Configuration/Properties.cs +++ b/System.ServiceModel/System/ServiceModel/Configuration/Properties.cs @@ -3472,7 +3472,7 @@ protected override ConfigurationPropertyCollection Properties { ConfigurationPropertyCollection properties = new ConfigurationPropertyCollection(); properties.Add(new ConfigurationProperty("requireClientCertificate", typeof(System.Boolean), false, null, null, System.Configuration.ConfigurationPropertyOptions.None)); - properties.Add(new ConfigurationProperty("sslProtocols", typeof(System.Security.Authentication.SslProtocols), System.Security.Authentication.SslProtocols.Ssl3 | System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Default | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls12, null, new System.ServiceModel.Configuration.ServiceModelEnumValidator(typeof(System.ServiceModel.Security.SslProtocolsHelper)), System.Configuration.ConfigurationPropertyOptions.None)); + properties.Add(new ConfigurationProperty("sslProtocols", typeof(System.Security.Authentication.SslProtocols), System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls12, null, new System.ServiceModel.Configuration.ServiceModelEnumValidator(typeof(System.ServiceModel.Security.SslProtocolsHelper)), System.Configuration.ConfigurationPropertyOptions.None)); this.properties = properties; } return this.properties; @@ -3527,7 +3527,7 @@ protected override ConfigurationPropertyCollection Properties properties.Add(new ConfigurationProperty("clientCredentialType", typeof(System.ServiceModel.TcpClientCredentialType), System.ServiceModel.TcpClientCredentialType.Windows, null, new System.ServiceModel.Configuration.ServiceModelEnumValidator(typeof(System.ServiceModel.TcpClientCredentialTypeHelper)), System.Configuration.ConfigurationPropertyOptions.None)); properties.Add(new ConfigurationProperty("protectionLevel", typeof(System.Net.Security.ProtectionLevel), System.Net.Security.ProtectionLevel.EncryptAndSign, null, new System.ServiceModel.Configuration.ServiceModelEnumValidator(typeof(System.ServiceModel.Security.ProtectionLevelHelper)), System.Configuration.ConfigurationPropertyOptions.None)); properties.Add(new ConfigurationProperty("extendedProtectionPolicy", typeof(System.Security.Authentication.ExtendedProtection.Configuration.ExtendedProtectionPolicyElement), null, null, null, System.Configuration.ConfigurationPropertyOptions.None)); - properties.Add(new ConfigurationProperty("sslProtocols", typeof(System.Security.Authentication.SslProtocols), System.Security.Authentication.SslProtocols.Ssl3 | System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Default | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls12, null, new System.ServiceModel.Configuration.ServiceModelEnumValidator(typeof(System.ServiceModel.Security.SslProtocolsHelper)), System.Configuration.ConfigurationPropertyOptions.None)); + properties.Add(new ConfigurationProperty("sslProtocols", typeof(System.Security.Authentication.SslProtocols), System.Security.Authentication.SslProtocols.Tls | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls12, null, new System.ServiceModel.Configuration.ServiceModelEnumValidator(typeof(System.ServiceModel.Security.SslProtocolsHelper)), System.Configuration.ConfigurationPropertyOptions.None)); this.properties = properties; } return this.properties; diff --git a/System.ServiceModel/System/ServiceModel/Description/MessageContractExporter.cs b/System.ServiceModel/System/ServiceModel/Description/MessageContractExporter.cs index dbef91c12..e4a23bcc0 100644 --- a/System.ServiceModel/System/ServiceModel/Description/MessageContractExporter.cs +++ b/System.ServiceModel/System/ServiceModel/Description/MessageContractExporter.cs @@ -1347,31 +1347,38 @@ static class StockSchemas internal static XmlSchema CreateWsdl() { - return XmlSchema.Read(new StringReader(wsdl), null); + StringReader reader = new StringReader(wsdl); + return XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); } + internal static XmlSchema CreateSoap() { - return XmlSchema.Read(new StringReader(soap), null); + StringReader reader = new StringReader(soap); + return XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); } internal static XmlSchema CreateSoapEncoding() { - return XmlSchema.Read(new StringReader(soapEncoding), null); + StringReader reader = new StringReader(soapEncoding); + return XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); } internal static XmlSchema CreateFakeSoapEncoding() { - return XmlSchema.Read(new StringReader(fakeSoapEncoding), null); + StringReader reader = new StringReader(fakeSoapEncoding); + return XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); } internal static XmlSchema CreateFakeXsdSchema() { - return XmlSchema.Read(new StringReader(fakeXsd), null); + StringReader reader = new StringReader(fakeXsd); + return XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); } internal static XmlSchema CreateFakeXmlSchema() { - return XmlSchema.Read(new StringReader(fakeXmlSchema), null); + StringReader reader = new StringReader(fakeXmlSchema); + return XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); } internal static bool IsKnownSchema(string ns) diff --git a/System.ServiceModel/System/ServiceModel/Description/WsdlHelper.cs b/System.ServiceModel/System/ServiceModel/Description/WsdlHelper.cs index c056108dd..f8674ce61 100644 --- a/System.ServiceModel/System/ServiceModel/Description/WsdlHelper.cs +++ b/System.ServiceModel/System/ServiceModel/Description/WsdlHelper.cs @@ -5,6 +5,7 @@ namespace System.ServiceModel.Description { using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; @@ -313,6 +314,8 @@ private static WsdlNS.ServiceDescription CloneWsdl(WsdlNS.ServiceDescription ori return newWsdl; } + [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader")] + [SuppressMessage("Microsoft.Security.Xml", "CA3069:ReviewDtdProcessingAssignment", Justification = "This is trusted server code from the application only. We should allow the customer add dtd.")] private static XmlSchema CloneXsd(XmlSchema originalXsd) { Fx.Assert(originalXsd != null, "originalXsd must not be null"); @@ -321,7 +324,7 @@ private static XmlSchema CloneXsd(XmlSchema originalXsd) { originalXsd.Write(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); - newXsd = XmlSchema.Read(memoryStream, null); + newXsd = XmlSchema.Read(new XmlTextReader(memoryStream) { DtdProcessing = DtdProcessing.Parse }, null); } return newXsd; diff --git a/System.ServiceModel/System/ServiceModel/Dispatcher/QueryMatcher.cs b/System.ServiceModel/System/ServiceModel/Dispatcher/QueryMatcher.cs index d34430b87..c7fb34872 100644 --- a/System.ServiceModel/System/ServiceModel/Dispatcher/QueryMatcher.cs +++ b/System.ServiceModel/System/ServiceModel/Dispatcher/QueryMatcher.cs @@ -7,6 +7,7 @@ namespace System.ServiceModel.Dispatcher using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; + using System.Diagnostics.CodeAnalysis; using System.Runtime; using System.ServiceModel.Channels; using System.ServiceModel.Diagnostics; @@ -262,6 +263,7 @@ internal void Push(QueryProcessor p) } } + [SuppressMessage("Microsoft.Security.Xml", "CA3057:DoNotUseLoadXml")] static QueryMatcher() { QueryMatcher.defaultFunctionLibs = new IFunctionLibrary[] { new XPathFunctionLibrary() }; diff --git a/System.ServiceModel/System/ServiceModel/Dispatcher/TaskMethodInvoker.cs b/System.ServiceModel/System/ServiceModel/Dispatcher/TaskMethodInvoker.cs index 9bf6328b8..2b9ff6aed 100644 --- a/System.ServiceModel/System/ServiceModel/Dispatcher/TaskMethodInvoker.cs +++ b/System.ServiceModel/System/ServiceModel/Dispatcher/TaskMethodInvoker.cs @@ -8,12 +8,11 @@ namespace System.ServiceModel.Dispatcher using System.Diagnostics; using System.Reflection; using System.Runtime; - using System.Runtime.Diagnostics; using System.Security; using System.ServiceModel.Description; using System.ServiceModel.Diagnostics; - using System.ServiceModel.Diagnostics.Application; using System.Threading.Tasks; + using Threading; /// /// An invoker used when some operation contract has a return value of Task or its generic counterpart (Task of T) @@ -21,14 +20,12 @@ namespace System.ServiceModel.Dispatcher internal class TaskMethodInvoker : IOperationInvoker { private const string ResultMethodName = "Result"; - private MethodInfo taskMethod; - private bool isGenericTask; + private readonly MethodInfo taskMethod; private InvokeDelegate invokeDelegate; private int inputParameterCount; private int outputParameterCount; - private object[] outputs; - private MethodInfo toAsyncMethodInfo; private MethodInfo taskTResultGetMethod; + private bool isGenericTask; public TaskMethodInvoker(MethodInfo taskMethod, Type taskType) { @@ -41,7 +38,6 @@ public TaskMethodInvoker(MethodInfo taskMethod, Type taskType) if (taskType != ServiceReflector.VoidType) { - this.toAsyncMethodInfo = TaskExtensions.MakeGenericMethod(taskType); this.taskTResultGetMethod = ((PropertyInfo)taskMethod.ReturnType.GetMember(ResultMethodName)[0]).GetGetMethod(); this.isGenericTask = true; } @@ -57,36 +53,11 @@ public MethodInfo TaskMethod get { return this.taskMethod; } } - private InvokeDelegate InvokeDelegate - { - get - { - this.EnsureIsInitialized(); - return this.invokeDelegate; - } - } - - private int InputParameterCount - { - get - { - this.EnsureIsInitialized(); - return this.inputParameterCount; - } - } - - private int OutputParameterCount - { - get - { - this.EnsureIsInitialized(); - return this.outputParameterCount; - } - } - public object[] AllocateInputs() { - return EmptyArray.Allocate(this.InputParameterCount); + EnsureIsInitialized(); + + return EmptyArray.Allocate(this.inputParameterCount); } public object Invoke(object instance, object[] inputs, out object[] outputs) @@ -95,169 +66,249 @@ public object Invoke(object instance, object[] inputs, out object[] outputs) } public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) + { + return ToApm(InvokeAsync(instance, inputs), callback, state); + } + + public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) { if (instance == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxNoServiceObject))); } - if (inputs == null) - { - if (this.InputParameterCount > 0) - { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInputParametersToServiceNull, this.InputParameterCount))); - } - } - else if (inputs.Length != this.InputParameterCount) - { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInputParametersToServiceInvalid, this.InputParameterCount, inputs.Length))); - } - - this.outputs = EmptyArray.Allocate(this.OutputParameterCount); - - AsyncMethodInvoker.StartOperationInvokePerformanceCounters(this.taskMethod.Name); - - IAsyncResult returnValue; + object returnVal = null; bool callFailed = true; bool callFaulted = false; ServiceModelActivity activity = null; + Activity boundOperation = null; try { - Activity boundActivity = null; - AsyncMethodInvoker.CreateActivityInfo(ref activity, ref boundActivity); + AsyncMethodInvoker.GetActivityInfo(ref activity, ref boundOperation); - AsyncMethodInvoker.StartOperationInvokeTrace(this.taskMethod.Name); - - using (boundActivity) + Task> invokeTask = result as Task>; + + if (invokeTask == null) { - if (DiagnosticUtility.ShouldUseActivity) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.SFxInvalidCallbackIAsyncResult)); + } + + AggregateException ae = null; + Tuple tuple = null; + Task task = null; + + if (invokeTask.IsFaulted) + { + Fx.Assert(invokeTask.Exception != null, "Task.IsFaulted guarantees non-null exception."); + ae = invokeTask.Exception; + } + else + { + Fx.Assert(invokeTask.IsCompleted, "Task.Result is expected to be completed"); + + tuple = invokeTask.Result; + task = tuple.Item1 as Task; + + if (task == null) { - string activityName = SR.GetString(SR.ActivityExecuteMethod, this.taskMethod.DeclaringType.FullName, this.taskMethod.Name); - ServiceModelActivity.Start(activity, activityName, ActivityType.ExecuteUserCode); + outputs = tuple.Item2; + return null; } - object taskReturnValue = this.InvokeDelegate(instance, inputs, this.outputs); - - if (taskReturnValue == null) + if (task.IsFaulted) { - throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("task"); + Fx.Assert(task.Exception != null, "Task.IsFaulted guarantees non-null exception."); + ae = task.Exception; } - else if (this.isGenericTask) + } + + if (ae != null && ae.InnerException != null) + { + if (ae.InnerException is FaultException) { - returnValue = (IAsyncResult)this.toAsyncMethodInfo.Invoke(null, new object[] { taskReturnValue, callback, state }); + // If invokeTask.IsFaulted we produce the 'callFaulted' behavior below. + // Any other exception will retain 'callFailed' behavior. + callFaulted = true; + callFailed = false; } - else + + if (ae.InnerException is SecurityException) { - returnValue = ((Task)taskReturnValue).AsAsyncResult(callback, state); + DiagnosticUtility.TraceHandledException(ae.InnerException, TraceEventType.Warning); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(AuthorizationBehavior.CreateAccessDeniedFaultException()); } - callFailed = false; + invokeTask.GetAwaiter().GetResult(); } - } - catch (System.Security.SecurityException e) - { - DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(AuthorizationBehavior.CreateAccessDeniedFaultException()); - } - catch (Exception e) - { - TraceUtility.TraceUserCodeException(e, this.taskMethod); - if (e is FaultException) + + // Task cancellation without an exception indicates failure but we have no + // additional information to provide. Accessing Task.Result will throw a + // TaskCanceledException. For consistency between void Tasks and Task, + // we detect and throw here. + if (task.IsCanceled) { - callFaulted = true; - callFailed = false; + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TaskCanceledException(task)); } - throw; + outputs = tuple.Item2; + + returnVal = this.isGenericTask ? this.taskTResultGetMethod.Invoke(task, Type.EmptyTypes) : null; + callFailed = false; + + return returnVal; } finally { - ServiceModelActivity.Stop(activity); - - // Any exception above means InvokeEnd will not be called, so complete it here. - if (callFailed || callFaulted) + if (boundOperation != null) { - AsyncMethodInvoker.StopOperationInvokeTrace(callFailed, callFaulted, this.TaskMethod.Name); - AsyncMethodInvoker.StopOperationInvokePerformanceCounters(callFailed, callFaulted, this.TaskMethod.Name); + ((IDisposable)boundOperation).Dispose(); } - } - return returnValue; + ServiceModelActivity.Stop(activity); + AsyncMethodInvoker.StopOperationInvokeTrace(callFailed, callFaulted, this.TaskMethod.Name); + AsyncMethodInvoker.StopOperationInvokePerformanceCounters(callFailed, callFaulted, this.TaskMethod.Name); + } } - public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) + private async Task> InvokeAsync(object instance, object[] inputs) { - object returnVal; - bool callFailed = true; - bool callFaulted = false; - ServiceModelActivity activity = null; + EnsureIsInitialized(); if (instance == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxNoServiceObject))); } + if (inputs == null) + { + if (this.inputParameterCount > 0) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInputParametersToServiceNull, this.inputParameterCount))); + } + } + else if (inputs.Length != this.inputParameterCount) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInputParametersToServiceInvalid, this.inputParameterCount, inputs.Length))); + } + + object[] outputs = EmptyArray.Allocate(this.outputParameterCount); + + AsyncMethodInvoker.StartOperationInvokePerformanceCounters(this.taskMethod.Name); + + object returnValue; + ServiceModelActivity activity = null; + Activity boundActivity = null; + try { - Activity boundOperation = null; - AsyncMethodInvoker.GetActivityInfo(ref activity, ref boundOperation); + AsyncMethodInvoker.CreateActivityInfo(ref activity, ref boundActivity); + AsyncMethodInvoker.StartOperationInvokeTrace(this.taskMethod.Name); - using (boundOperation) + if (DiagnosticUtility.ShouldUseActivity) { - Task task = result as Task; + string activityName = SR.GetString(SR.ActivityExecuteMethod, this.taskMethod.DeclaringType.FullName, this.taskMethod.Name); + ServiceModelActivity.Start(activity, activityName, ActivityType.ExecuteUserCode); + } - Fx.Assert(task != null, "InvokeEnd needs to be called with the result returned from InvokeBegin."); - if (task.IsFaulted) - { - Fx.Assert(task.Exception != null, "Task.IsFaulted guarantees non-null exception."); + OperationContext.EnableAsyncFlow(); - // If FaultException is thrown, we will get 'callFaulted' behavior below. - // Any other exception will retain 'callFailed' behavior. - throw FxTrace.Exception.AsError(task.Exception); - } + returnValue = this.invokeDelegate(instance, inputs, outputs); - // Task cancellation without an exception indicates failure but we have no - // additional information to provide. Accessing Task.Result will throw a - // TaskCanceledException. For consistency between void Tasks and Task, - // we detect and throw here. - if (task.IsCanceled) - { - throw FxTrace.Exception.AsError(new TaskCanceledException(task)); - } + if (returnValue == null) + { + throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("task"); + } - outputs = this.outputs; - if (this.isGenericTask) - { - returnVal = this.taskTResultGetMethod.Invoke(result, Type.EmptyTypes); - } - else - { - returnVal = null; - } + var returnValueTask = returnValue as Task; - callFailed = false; + if (returnValueTask != null) + { + // Only return once the task has completed + await returnValueTask; } + + return Tuple.Create(returnValue, outputs); } catch (SecurityException e) { DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(AuthorizationBehavior.CreateAccessDeniedFaultException()); } - catch (FaultException) + catch (Exception e) { - callFaulted = true; - callFailed = false; + TraceUtility.TraceUserCodeException(e, this.taskMethod); throw; } finally { + OperationContext.DisableAsyncFlow(); + + if (boundActivity != null) + { + ((IDisposable)boundActivity).Dispose(); + } + ServiceModelActivity.Stop(activity); - AsyncMethodInvoker.StopOperationInvokeTrace(callFailed, callFaulted, this.TaskMethod.Name); - AsyncMethodInvoker.StopOperationInvokePerformanceCounters(callFailed, callFaulted, this.TaskMethod.Name); } + } + + // Helper method when implementing an APM wrapper around a Task based async method which returns a result. + // In the BeginMethod method, you would call use ToApm to wrap a call to MethodAsync: + // return MethodAsync(params).ToApm(callback, state); + // In the EndMethod, you would use ToApmEnd to ensure the correct exception handling + // This will handle throwing exceptions in the correct place and ensure the IAsyncResult contains the provided + // state object + private static Task ToApm(Task task, AsyncCallback callback, object state) + { + // When using APM, the returned IAsyncResult must have the passed in state object stored in AsyncState. This + // is so the callback can regain state. If the incoming task already holds the state object, there's no need + // to create a TaskCompletionSource to ensure the returned (IAsyncResult)Task has the right state object. + // This is a performance optimization for this special case. + if (task.AsyncState == state) + { + if (callback != null) + { + task.ContinueWith((antecedent, obj) => + { + AsyncCallback callbackObj = (AsyncCallback)obj; + callbackObj(antecedent); + }, callback, CancellationToken.None, TaskContinuationOptions.HideScheduler, TaskScheduler.Default); + } + + return task; + } + + // Need to create a TaskCompletionSource so that the returned Task object has the correct AsyncState value. + var tcs = new TaskCompletionSource(state); + var continuationState = Tuple.Create(tcs, callback); + + task.ContinueWith((antecedent, obj) => + { + Tuple, AsyncCallback> tuple = (Tuple, AsyncCallback>)obj; + TaskCompletionSource tcsObj = tuple.Item1; + AsyncCallback callbackObj = tuple.Item2; + + if (antecedent.IsFaulted) + { + tcsObj.TrySetException(antecedent.Exception.InnerException); + } + else if (antecedent.IsCanceled) + { + tcsObj.TrySetCanceled(); + } + else + { + tcsObj.TrySetResult(antecedent.Result); + } + + if (callbackObj != null) + { + callbackObj(tcsObj.Task); + } + }, continuationState, CancellationToken.None, TaskContinuationOptions.HideScheduler, TaskScheduler.Default); - return returnVal; + return tcs.Task; } private void EnsureIsInitialized() diff --git a/System.ServiceModel/System/ServiceModel/Dispatcher/XmlSerializerOperationFormatter.cs b/System.ServiceModel/System/ServiceModel/Dispatcher/XmlSerializerOperationFormatter.cs index 5d826dfb4..0072f122b 100644 --- a/System.ServiceModel/System/ServiceModel/Dispatcher/XmlSerializerOperationFormatter.cs +++ b/System.ServiceModel/System/ServiceModel/Dispatcher/XmlSerializerOperationFormatter.cs @@ -92,7 +92,7 @@ protected override void AddHeadersToMessage(Message message, MessageDescription bufferWriter.Flush(); XmlDocument doc = new XmlDocument(); memoryStream.Position = 0; - doc.Load(memoryStream); + doc.Load(new XmlTextReader(memoryStream) { DtdProcessing = DtdProcessing.Prohibit }); //doc.Save(Console.Out); foreach (XmlElement element in doc.DocumentElement.ChildNodes) { diff --git a/System.ServiceModel/System/ServiceModel/EndpointAddress10.cs b/System.ServiceModel/System/ServiceModel/EndpointAddress10.cs index dd5b3ef34..d7e24e00a 100644 --- a/System.ServiceModel/System/ServiceModel/EndpointAddress10.cs +++ b/System.ServiceModel/System/ServiceModel/EndpointAddress10.cs @@ -67,7 +67,7 @@ static XmlQualifiedName EprType static XmlSchema GetEprSchema() { - using (XmlTextReader reader = new XmlTextReader(new StringReader(Schema))) + using (XmlTextReader reader = new XmlTextReader(new StringReader(Schema)) { DtdProcessing = DtdProcessing.Prohibit }) { return XmlSchema.Read(reader, null); } diff --git a/System.ServiceModel/System/ServiceModel/EndpointAddressAugust2004.cs b/System.ServiceModel/System/ServiceModel/EndpointAddressAugust2004.cs index b60cc2b28..d74bdeb89 100644 --- a/System.ServiceModel/System/ServiceModel/EndpointAddressAugust2004.cs +++ b/System.ServiceModel/System/ServiceModel/EndpointAddressAugust2004.cs @@ -67,7 +67,7 @@ static XmlQualifiedName EprType static XmlSchema GetEprSchema() { - using (XmlTextReader reader = new XmlTextReader(new StringReader(Schema))) + using (XmlTextReader reader = new XmlTextReader(new StringReader(Schema)) { DtdProcessing = DtdProcessing.Prohibit }) { return XmlSchema.Read(reader, null); } diff --git a/System.ServiceModel/System/ServiceModel/LocalAppContextSwitches.cs b/System.ServiceModel/System/ServiceModel/LocalAppContextSwitches.cs index 616b1fedd..323304ff6 100644 --- a/System.ServiceModel/System/ServiceModel/LocalAppContextSwitches.cs +++ b/System.ServiceModel/System/ServiceModel/LocalAppContextSwitches.cs @@ -14,9 +14,11 @@ internal static class LocalAppContextSwitches { private const string DisableExplicitConnectionCloseHeaderString = "Switch.System.ServiceModel.DisableExplicitConnectionCloseHeader"; private const string AllowUnsignedToHeaderString = "Switch.System.ServiceModel.AllowUnsignedToHeader"; + private const string DisableCngCertificatesString = "Switch.System.ServiceModel.DisableCngCertificates"; private static int disableExplicitConnectionCloseHeader; private static int allowUnsignedToHeader; + private static int disableCngCertificates; public static bool DisableExplicitConnectionCloseHeader { @@ -36,10 +38,29 @@ public static bool AllowUnsignedToHeader } } + public static bool DisableCngCertificates + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return LocalAppContext.GetCachedSwitchValue(DisableCngCertificatesString, ref disableCngCertificates); + } + } + public static void SetDefaultsLessOrEqual_452() { +#pragma warning disable BCL0012 // Define the switches that should be true for 4.5.2 or less, false for 4.6+. LocalAppContext.DefineSwitchDefault(DisableExplicitConnectionCloseHeaderString, true); +#pragma warning restore BCL0012 + } + + public static void SetDefaultsLessOrEqual_461() + { +#pragma warning disable BCL0012 + // Define the switches that should be true for 4.6.1 or less, false for 4.6.2+. + LocalAppContext.DefineSwitchDefault(DisableCngCertificatesString, true); +#pragma warning restore BCL0012 } } } diff --git a/System.ServiceModel/System/ServiceModel/OperationContext.cs b/System.ServiceModel/System/ServiceModel/OperationContext.cs index 087949867..c08a1200c 100644 --- a/System.ServiceModel/System/ServiceModel/OperationContext.cs +++ b/System.ServiceModel/System/ServiceModel/OperationContext.cs @@ -5,19 +5,21 @@ namespace System.ServiceModel { using System.Collections.Generic; - using System.ComponentModel; using System.Runtime; using System.Security.Claims; using System.Security.Principal; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; using System.ServiceModel.Security; + using System.Threading; public sealed class OperationContext : IExtensibleObject { [ThreadStatic] static Holder currentContext; + static AsyncLocal currentAsyncLocalContext = new AsyncLocal(); + ServiceChannel channel; Message clientReply; bool closeClientReply; @@ -33,6 +35,7 @@ public sealed class OperationContext : IExtensibleObject MessageHeaders outgoingMessageHeaders; MessageVersion outgoingMessageVersion; EndpointDispatcher endpointDispatcher; + bool isAsyncFlowEnabled; public event EventHandler OperationCompleted; @@ -92,12 +95,19 @@ public static OperationContext Current { get { - return CurrentHolder.Context; + return ShouldUseAsyncLocalContext ? OperationContext.currentAsyncLocalContext.Value : CurrentHolder.Context; } set { - CurrentHolder.Context = value; + if (ShouldUseAsyncLocalContext) + { + OperationContext.currentAsyncLocalContext.Value = value; + } + else + { + CurrentHolder.Context = value; + } } } @@ -115,6 +125,14 @@ internal static Holder CurrentHolder } } + private static bool ShouldUseAsyncLocalContext + { + get + { + return CurrentHolder.Context == null && OperationContext.currentAsyncLocalContext.Value != null && OperationContext.currentAsyncLocalContext.Value.isAsyncFlowEnabled; + } + } + public EndpointDispatcher EndpointDispatcher { get @@ -339,6 +357,21 @@ internal void ClearClientReplyNoThrow() this.clientReply = null; } + internal static void EnableAsyncFlow() + { + CurrentHolder.Context.isAsyncFlowEnabled = true; + currentAsyncLocalContext.Value = CurrentHolder.Context; + } + + internal static void DisableAsyncFlow() + { + if (OperationContext.Current != null && OperationContext.Current.isAsyncFlowEnabled) + { + OperationContext.Current.isAsyncFlowEnabled = false; + currentAsyncLocalContext.Value = null; + } + } + internal void FireOperationCompleted() { try diff --git a/System.ServiceModel/System/ServiceModel/OperationContextScope.cs b/System.ServiceModel/System/ServiceModel/OperationContextScope.cs index 17da631e1..3d12f54c8 100644 --- a/System.ServiceModel/System/ServiceModel/OperationContextScope.cs +++ b/System.ServiceModel/System/ServiceModel/OperationContextScope.cs @@ -10,14 +10,12 @@ namespace System.ServiceModel public sealed class OperationContextScope : IDisposable { - [ThreadStatic] - static OperationContextScope currentScope; + static AsyncLocal currentScope = new AsyncLocal(); OperationContext currentContext; bool disposed; readonly OperationContext originalContext = OperationContext.Current; - readonly OperationContextScope originalScope = OperationContextScope.currentScope; - readonly Thread thread = Thread.CurrentThread; + readonly OperationContextScope originalScope = OperationContextScope.currentScope.Value; public OperationContextScope(IContextChannel channel) { @@ -41,22 +39,19 @@ public void Dispose() void PushContext(OperationContext context) { this.currentContext = context; - OperationContextScope.currentScope = this; + OperationContextScope.currentScope.Value = this; OperationContext.Current = this.currentContext; } void PopContext() { - if (this.thread != Thread.CurrentThread) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidContextScopeThread0))); - - if (OperationContextScope.currentScope != this) + if (OperationContextScope.currentScope.Value != this) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInterleavedContextScopes0))); if (OperationContext.Current != this.currentContext) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxContextModifiedInsideScope0))); - OperationContextScope.currentScope = this.originalScope; + OperationContextScope.currentScope.Value = this.originalScope; OperationContext.Current = this.originalContext; if (this.currentContext != null) diff --git a/System.ServiceModel/System/ServiceModel/Security/CryptoHelper.cs b/System.ServiceModel/System/ServiceModel/Security/CryptoHelper.cs index ff0d508ec..7c0b7a8cb 100644 --- a/System.ServiceModel/System/ServiceModel/Security/CryptoHelper.cs +++ b/System.ServiceModel/System/ServiceModel/Security/CryptoHelper.cs @@ -17,6 +17,7 @@ namespace System.ServiceModel.Security using System.Text; using System.Xml; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; using Psha1DerivedKeyGenerator = System.IdentityModel.Psha1DerivedKeyGenerator; @@ -57,6 +58,7 @@ internal static HashAlgorithm NewSha256HashAlgorithm() return CryptoHelper.CreateHashAlgorithm(SecurityAlgorithms.Sha256Digest); } + [SuppressMessage("Microsoft.Security.Cryptography", "CA5354:DoNotUseSHA1", Justification = "Cannot change. Required as SOAP spec requires supporting SHA1.")] internal static HashAlgorithm CreateHashAlgorithm(string digestMethod) { object algorithmObject = CryptoAlgorithms.GetAlgorithmFromConfig(digestMethod); @@ -86,6 +88,7 @@ internal static HashAlgorithm CreateHashAlgorithm(string digestMethod) } } + [SuppressMessage("Microsoft.Security.Cryptography", "CA5354:DoNotUseSHA1", Justification = "Cannot change. Required as SOAP spec requires supporting SHA1.")] internal static HashAlgorithm CreateHashForAsymmetricSignature(string signatureMethod) { object algorithmObject = CryptoAlgorithms.GetAlgorithmFromConfig(signatureMethod); diff --git a/System.ServiceModel/System/ServiceModel/Security/SecurityUtils.cs b/System.ServiceModel/System/ServiceModel/Security/SecurityUtils.cs index 913840d03..b62aceefc 100644 --- a/System.ServiceModel/System/ServiceModel/Security/SecurityUtils.cs +++ b/System.ServiceModel/System/ServiceModel/Security/SecurityUtils.cs @@ -899,8 +899,45 @@ internal static void EnsureCertificateCanDoKeyExchange(X509Certificate2 certific [SecuritySafeCritical] static bool CanKeyDoKeyExchange(X509Certificate2 certificate) { - CspKeyContainerInfo info = GetKeyContainerInfo(certificate); - return info != null && info.KeyNumber == KeyNumber.Exchange; + bool canDoKeyExchange = false; + + if (!LocalAppContextSwitches.DisableCngCertificates) + { + X509KeyUsageExtension keyUsageExtension = null; + for (int i = 0; i < certificate.Extensions.Count; i++) + { + keyUsageExtension = certificate.Extensions[i] as X509KeyUsageExtension; + if (keyUsageExtension != null) + { + break; + } + } + + // No KeyUsage extension means most usages are permitted including key exchange. + // See RFC 5280 section 4.2.1.3 (Key Usage) for details. If the extension is non-critical + // then it's non-enforcing and meant as an aid in choosing the best certificate when + // there are multiple certificates to choose from. + if (keyUsageExtension == null || !keyUsageExtension.Critical) + { + return true; + } + + // One of KeyAgreement, KeyEncipherment or DigitalSignature need to be allowed depending on the cipher + // being used. See RFC 5246 section 7.4.6 for more details. + // Additionally, according to msdn docs for PFXImportCertStore, the key specification is set to AT_KEYEXCHANGE + // when the data encipherment usage is set. + canDoKeyExchange = (keyUsageExtension.KeyUsages & + (X509KeyUsageFlags.KeyAgreement | X509KeyUsageFlags.KeyEncipherment | + X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.DataEncipherment)) != X509KeyUsageFlags.None; + } + + if (!canDoKeyExchange) + { + CspKeyContainerInfo info = GetKeyContainerInfo(certificate); + canDoKeyExchange = info != null && info.KeyNumber == KeyNumber.Exchange; + } + + return canDoKeyExchange; } [Fx.Tag.SecurityNote(Critical = "Elevates to call properties: X509Certificate2.PrivateKey and CspKeyContainerInfo. Caller must protect the return value.")] @@ -1866,6 +1903,52 @@ internal static NetworkCredential GetNetworkCredentialOrDefault(NetworkCredentia } } + public static bool CanReadPrivateKey(X509Certificate2 certificate) + { + if (!certificate.HasPrivateKey) + return false; + + try + { + // CNG key, CNG permissions tests + using (RSA rsa = CngLightup.GetRSAPrivateKey(certificate)) + { + if (rsa != null) + { + return true; + } + } + + using (DSA dsa = CngLightup.GetDSAPrivateKey(certificate)) + { + if (dsa != null) + { + return true; + } + } + + using (ECDsa ecdsa = CngLightup.GetECDsaPrivateKey(certificate)) + { + if (ecdsa != null) + { + return true; + } + } + + // CAPI key, CAPI permissions test + if (certificate.PrivateKey != null) + { + return true; + } + + return false; + } + catch (CryptographicException) + { + return false; + } + } + static class NetworkCredentialHelper { [Fx.Tag.SecurityNote(Critical = "Uses unsafe critical methods UnsafeGetUsername, UnsafeGetPassword, and UnsafeGetDomain to access the credential details without a Demand.", diff --git a/System.ServiceModel/System/ServiceModel/Security/TlsSspiNegotiation.cs b/System.ServiceModel/System/ServiceModel/Security/TlsSspiNegotiation.cs index 0032edc9e..597228934 100644 --- a/System.ServiceModel/System/ServiceModel/Security/TlsSspiNegotiation.cs +++ b/System.ServiceModel/System/ServiceModel/Security/TlsSspiNegotiation.cs @@ -543,7 +543,14 @@ static void ValidatePrivateKey(X509Certificate2 certificate) bool hasPrivateKey = false; try { - hasPrivateKey = certificate != null && certificate.PrivateKey != null; + if (System.ServiceModel.LocalAppContextSwitches.DisableCngCertificates) + { + hasPrivateKey = certificate != null && certificate.PrivateKey != null; + } + else + { + hasPrivateKey = certificate.HasPrivateKey && SecurityUtils.CanReadPrivateKey(certificate); + } } catch (SecurityException e) { diff --git a/System.ServiceModel/System/ServiceModel/Security/Tokens/IssuedSecurityTokenProvider.cs b/System.ServiceModel/System/ServiceModel/Security/Tokens/IssuedSecurityTokenProvider.cs index b9f10c42f..5641522d6 100644 --- a/System.ServiceModel/System/ServiceModel/Security/Tokens/IssuedSecurityTokenProvider.cs +++ b/System.ServiceModel/System/ServiceModel/Security/Tokens/IssuedSecurityTokenProvider.cs @@ -461,7 +461,7 @@ XmlElement CreateXmlTokenElement(SecurityToken token, string prefix, string name XmlDocument dom = new XmlDocument(); dom.PreserveWhitespace = true; - dom.Load(stream); + dom.Load(new XmlTextReader(stream) { DtdProcessing = DtdProcessing.Prohibit }); stream.Close(); return dom.DocumentElement; diff --git a/System.ServiceModel/System/ServiceModel/Security/WSSecurityPolicy.cs b/System.ServiceModel/System/ServiceModel/Security/WSSecurityPolicy.cs index e3d7cf18c..417886dd3 100644 --- a/System.ServiceModel/System/ServiceModel/Security/WSSecurityPolicy.cs +++ b/System.ServiceModel/System/ServiceModel/Security/WSSecurityPolicy.cs @@ -2310,7 +2310,7 @@ public virtual XmlElement CreateWsspIssuerElement(EndpointAddress issuerAddress, writer.WriteEndElement(); writer.Flush(); stream.Seek(0, SeekOrigin.Begin); - result = (XmlElement)doc.ReadNode(new XmlTextReader(stream)); + result = (XmlElement)doc.ReadNode(new XmlTextReader(stream) { DtdProcessing = DtdProcessing.Prohibit }); } return result; } diff --git a/System.ServiceModel/System/ServiceModel/Security/WSTrust.cs b/System.ServiceModel/System/ServiceModel/Security/WSTrust.cs index 2795cd2b3..2b65cc932 100644 --- a/System.ServiceModel/System/ServiceModel/Security/WSTrust.cs +++ b/System.ServiceModel/System/ServiceModel/Security/WSTrust.cs @@ -1417,7 +1417,7 @@ public override XmlElement CreateUseKeyElement(SecurityKeyIdentifier keyIdentifi writer.Flush(); stream.Seek(0, SeekOrigin.Begin); XmlNode skiNode; - using (XmlDictionaryReader reader = XmlDictionaryReader.CreateDictionaryReader(new XmlTextReader(stream))) + using (XmlDictionaryReader reader = XmlDictionaryReader.CreateDictionaryReader(new XmlTextReader(stream) { DtdProcessing = DtdProcessing.Prohibit })) { reader.MoveToContent(); skiNode = doc.ReadNode(reader); diff --git a/System.ServiceModel/System/ServiceModel/Security/WSTrustServiceContract.cs b/System.ServiceModel/System/ServiceModel/Security/WSTrustServiceContract.cs index 81df58a30..1bd584b28 100644 --- a/System.ServiceModel/System/ServiceModel/Security/WSTrustServiceContract.cs +++ b/System.ServiceModel/System/ServiceModel/Security/WSTrustServiceContract.cs @@ -2064,7 +2064,8 @@ static XmlSchema GetXmlSchema(WsdlExporter exporter, string ns) throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID5004, ns)); } - return XmlSchema.Read(new StringReader(xmlSchema), null); + StringReader reader = new StringReader(xmlSchema); + return XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); } /// diff --git a/System.ServiceModel/System/ServiceModel/ServiceHost.cs b/System.ServiceModel/System/ServiceModel/ServiceHost.cs index 47a80aaf7..37ca8e48b 100644 --- a/System.ServiceModel/System/ServiceModel/ServiceHost.cs +++ b/System.ServiceModel/System/ServiceModel/ServiceHost.cs @@ -1,4 +1,4 @@ - //----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- @@ -1076,6 +1076,9 @@ protected override void OnOpened() { ManagementExtension.OnServiceOpened(this); } + + // log telemetry data for the current WCF service. + TelemetryTraceLogging.LogSeriveKPIData(this.Description); } base.OnOpened(); diff --git a/System.ServiceModel/System/ServiceModel/ServiceModelAppSettings.cs b/System.ServiceModel/System/ServiceModel/ServiceModelAppSettings.cs index b7c7db2a4..19fba5c99 100644 --- a/System.ServiceModel/System/ServiceModel/ServiceModelAppSettings.cs +++ b/System.ServiceModel/System/ServiceModel/ServiceModelAppSettings.cs @@ -15,12 +15,15 @@ internal static class ServiceModelAppSettings internal const string HttpTransportPerFactoryConnectionPoolString = "wcf:httpTransportBinding:useUniqueConnectionPoolPerFactory"; internal const string EnsureUniquePerformanceCounterInstanceNamesString = "wcf:ensureUniquePerformanceCounterInstanceNames"; internal const string UseConfiguredTransportSecurityHeaderLayoutString = "wcf:useConfiguredTransportSecurityHeaderLayout"; + internal const string UseBestMatchNamedPipeUriString = "wcf:useBestMatchNamedPipeUri"; const bool DefaultHttpTransportPerFactoryConnectionPool = false; const bool DefaultEnsureUniquePerformanceCounterInstanceNames = false; const bool DefaultUseConfiguredTransportSecurityHeaderLayout = false; + const bool DefaultUseBestMatchNamedPipeUri = false; static bool httpTransportPerFactoryConnectionPool; static bool ensureUniquePerformanceCounterInstanceNames; static bool useConfiguredTransportSecurityHeaderLayout; + static bool useBestMatchNamedPipeUri; static volatile bool settingsInitalized = false; static object appSettingsLock = new object(); @@ -54,6 +57,16 @@ internal static bool UseConfiguredTransportSecurityHeaderLayout } } + internal static bool UseBestMatchNamedPipeUri + { + get + { + EnsureSettingsLoaded(); + + return useBestMatchNamedPipeUri; + } + } + [SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104:CaughtAndHandledExceptionsRule", Justification = "Handle the configuration exceptions here to avoid regressions on customer's existing scenarios")] static void EnsureSettingsLoaded() @@ -89,6 +102,11 @@ static void EnsureSettingsLoaded() useConfiguredTransportSecurityHeaderLayout = DefaultUseConfiguredTransportSecurityHeaderLayout; } + if ((appSettingsSection == null) || !bool.TryParse(appSettingsSection[UseBestMatchNamedPipeUriString], out useBestMatchNamedPipeUri)) + { + useBestMatchNamedPipeUri = DefaultUseBestMatchNamedPipeUri; + } + settingsInitalized = true; } } diff --git a/System.ServiceModel/System/UriTemplate.cs b/System.ServiceModel/System/UriTemplate.cs index 969772454..a62d82e0b 100644 --- a/System.ServiceModel/System/UriTemplate.cs +++ b/System.ServiceModel/System/UriTemplate.cs @@ -4,6 +4,7 @@ namespace System { + using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; @@ -11,6 +12,7 @@ namespace System using System.ServiceModel; using System.ServiceModel.Channels; using System.Text; + using System.Threading; using System.Runtime.CompilerServices; using System.Globalization; @@ -31,7 +33,7 @@ public class UriTemplate const string NullableDefault = "null"; readonly WildcardInfo wildcard; IDictionary defaults; - Dictionary unescapedDefaults; + ConcurrentDictionary unescapedDefaults; VariablesCollection variables; @@ -263,7 +265,7 @@ public IDictionary Defaults { if (this.defaults == null) { - this.defaults = new UriTemplateDefaults(this); + Interlocked.CompareExchange>(ref this.defaults, new UriTemplateDefaults(this), null); } return this.defaults; } @@ -943,16 +945,10 @@ string UnescapeDefaultValue(string escapedValue) } if (this.unescapedDefaults == null) { - this.unescapedDefaults = new Dictionary(StringComparer.Ordinal); - } - string unescapedValue; - if (!this.unescapedDefaults.TryGetValue(escapedValue, out unescapedValue)) - { - unescapedValue = Uri.UnescapeDataString(escapedValue); - this.unescapedDefaults.Add(escapedValue, unescapedValue); + this.unescapedDefaults = new ConcurrentDictionary(StringComparer.Ordinal); } - return unescapedValue; + return this.unescapedDefaults.GetOrAdd(escapedValue, Uri.UnescapeDataString); } struct BindInformation @@ -1181,8 +1177,8 @@ public ReadOnlyCollection PathSegmentVariableNames { if (this.pathSegmentVariableNamesSnapshot == null) { - this.pathSegmentVariableNamesSnapshot = new ReadOnlyCollection( - this.pathSegmentVariableNames); + Interlocked.CompareExchange>(ref this.pathSegmentVariableNamesSnapshot, new ReadOnlyCollection( + this.pathSegmentVariableNames), null); } return this.pathSegmentVariableNamesSnapshot; } @@ -1193,8 +1189,8 @@ public ReadOnlyCollection QueryValueVariableNames { if (this.queryValueVariableNamesSnapshot == null) { - this.queryValueVariableNamesSnapshot = new ReadOnlyCollection( - this.queryValueVariableNames); + Interlocked.CompareExchange>(ref this.queryValueVariableNamesSnapshot, new ReadOnlyCollection( + this.queryValueVariableNames), null); } return this.queryValueVariableNamesSnapshot; } diff --git a/System.Web.DynamicData/DynamicData/DynamicValidator.cs b/System.Web.DynamicData/DynamicData/DynamicValidator.cs index 7e89f129e..51d5a3c92 100644 --- a/System.Web.DynamicData/DynamicData/DynamicValidator.cs +++ b/System.Web.DynamicData/DynamicData/DynamicValidator.cs @@ -11,6 +11,7 @@ using System.Web.Resources; using System.Web.UI; using System.Web.UI.WebControls; + using System.Web.DynamicData.Util; /// /// Validator that enforces model validation. It can be used either at the field level or the entity level @@ -163,7 +164,7 @@ private bool ValueIsValid(object value) { } if (!attrib.IsValid(value)) { - ErrorMessage = HttpUtility.HtmlEncode(attrib.FormatErrorMessage(Column.DisplayName)); + ErrorMessage = HttpUtility.HtmlEncode(StringLocalizerUtil.GetLocalizedString(attrib, Column.DisplayName)); return false; } } diff --git a/System.Web.DynamicData/DynamicData/FieldTemplateUserControl.cs b/System.Web.DynamicData/DynamicData/FieldTemplateUserControl.cs index 33381499e..324ddf3a0 100644 --- a/System.Web.DynamicData/DynamicData/FieldTemplateUserControl.cs +++ b/System.Web.DynamicData/DynamicData/FieldTemplateUserControl.cs @@ -516,7 +516,8 @@ private void SetUpRangeValidator(RangeValidator validator, MetaColumn column) { validator.MaximumValue = converter(rangeAttribute.Maximum); if (String.IsNullOrEmpty(validator.ErrorMessage)) { - validator.ErrorMessage = HttpUtility.HtmlEncode(rangeAttribute.FormatErrorMessage(column.DisplayName)); + validator.ErrorMessage = HttpUtility.HtmlEncode( + StringLocalizerUtil.GetLocalizedString(rangeAttribute, column.DisplayName)); } } @@ -533,7 +534,8 @@ private void SetUpRegexValidator(RegularExpressionValidator validator, MetaColum validator.ValidationExpression = regexAttribute.Pattern; if (String.IsNullOrEmpty(validator.ErrorMessage)) { - validator.ErrorMessage = HttpUtility.HtmlEncode(regexAttribute.FormatErrorMessage(column.DisplayName)); + validator.ErrorMessage = HttpUtility.HtmlEncode( + StringLocalizerUtil.GetLocalizedString(regexAttribute, column.DisplayName)); } } diff --git a/System.Web.DynamicData/DynamicData/MetaColumn.cs b/System.Web.DynamicData/DynamicData/MetaColumn.cs index 41ce63ba2..74fb8fb0f 100644 --- a/System.Web.DynamicData/DynamicData/MetaColumn.cs +++ b/System.Web.DynamicData/DynamicData/MetaColumn.cs @@ -250,7 +250,8 @@ public int MaxLength { public string RequiredErrorMessage { get { var requiredAttribute = Metadata.RequiredAttribute; - return requiredAttribute != null ? requiredAttribute.FormatErrorMessage(DisplayName) : String.Empty; + return requiredAttribute != null ? + StringLocalizerUtil.GetLocalizedString(requiredAttribute, DisplayName) : String.Empty; } } @@ -590,7 +591,7 @@ public object DefaultValue { public string Description { get { - return DisplayAttribute.GetPropertyValue(a => a.GetDescription(), null) ?? + return DisplayAttribute.GetLocalizedDescription() ?? DescriptionAttribute.GetPropertyValue(a => a.Description, null); } } @@ -599,14 +600,14 @@ public string Description { public string DisplayName { get { - return DisplayAttribute.GetPropertyValue(a => a.GetName(), null) ?? + return DisplayAttribute.GetLocalizedName() ?? DisplayNameAttribute.GetPropertyValue(a => a.DisplayName, null); } } public string ShortDisplayName { get { - return DisplayAttribute.GetPropertyValue(a => a.GetShortName(), null); + return DisplayAttribute.GetLocalizedShortName(); } } @@ -622,7 +623,7 @@ public string ShortDisplayName { public string Prompt { get { - return DisplayAttribute.GetPropertyValue(a => a.GetPrompt(), null); + return DisplayAttribute.GetLocalizedPrompt(); } } diff --git a/System.Web.Extensions/Compilation/WCFModel/DataSvcMapFileLoader.cs b/System.Web.Extensions/Compilation/WCFModel/DataSvcMapFileLoader.cs index e0c957977..b7a62e03a 100644 --- a/System.Web.Extensions/Compilation/WCFModel/DataSvcMapFileLoader.cs +++ b/System.Web.Extensions/Compilation/WCFModel/DataSvcMapFileLoader.cs @@ -6,6 +6,7 @@ #endregion using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Xml.Schema; using System.Xml.Serialization; @@ -50,6 +51,7 @@ protected override object Unwrap(MapFile mapFile) return mapFile is DataSvcMapFile ? ((DataSvcMapFile)mapFile).Impl : null; } + [SuppressMessage("Microsoft.Security.Xml", "CA3060:UseXmlReaderForSchemaRead", Justification = "asp.net controls this .xsd file")] protected override XmlSchemaSet GetMapFileSchemaSet() { if (_mapFileSchemaSet == null) diff --git a/System.Web.Extensions/Compilation/WCFModel/MetadataFile.cs b/System.Web.Extensions/Compilation/WCFModel/MetadataFile.cs index 9aea425a7..d05896f21 100644 --- a/System.Web.Extensions/Compilation/WCFModel/MetadataFile.cs +++ b/System.Web.Extensions/Compilation/WCFModel/MetadataFile.cs @@ -17,6 +17,7 @@ #if WEB_EXTENSIONS_CODE using System.Web.Resources; +using System.Diagnostics.CodeAnalysis; #else using Microsoft.VSDesigner.WCF.Resources; #endif @@ -551,6 +552,7 @@ internal void LoadContent(string content) /// /// /// + [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Legacy code that trusts our developer-controlled input.")] private void LoadContentFromTextReader(TextReader contentReader) { if (contentReader == null) @@ -597,6 +599,7 @@ internal void CleanUpContent() /// /// /// + [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Legacy code that trusts our developer-controlled input.")] private MetadataContent LoadMetadataContent(MetadataType fileType) { if (ErrorInLoading != null) diff --git a/System.Web.Extensions/Compilation/WCFModel/SvcMapFileLoader.cs b/System.Web.Extensions/Compilation/WCFModel/SvcMapFileLoader.cs index d820af9e0..8b9e1ea29 100644 --- a/System.Web.Extensions/Compilation/WCFModel/SvcMapFileLoader.cs +++ b/System.Web.Extensions/Compilation/WCFModel/SvcMapFileLoader.cs @@ -6,6 +6,7 @@ #endregion using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Xml.Schema; using System.Xml.Serialization; @@ -50,6 +51,7 @@ protected override object Unwrap(MapFile mapFile) return mapFile is SvcMapFile ? ((SvcMapFile)mapFile).Impl : null; } + [SuppressMessage("Microsoft.Security.Xml", "CA3060:UseXmlReaderForSchemaRead", Justification = "asp.net controls this .xsd file")] protected override XmlSchemaSet GetMapFileSchemaSet() { if (_mapFileSchemaSet == null) diff --git a/System.Web.Mobile/UI/MobileControls/Adapters/ChtmlCalendarAdapter.cs b/System.Web.Mobile/UI/MobileControls/Adapters/ChtmlCalendarAdapter.cs index 91b5f5e70..a68af5f0a 100644 --- a/System.Web.Mobile/UI/MobileControls/Adapters/ChtmlCalendarAdapter.cs +++ b/System.Web.Mobile/UI/MobileControls/Adapters/ChtmlCalendarAdapter.cs @@ -59,7 +59,7 @@ public class ChtmlCalendarAdapter : HtmlControlAdapter // This member variable is set each time when calendar info needs to // be accessed and be shared for other helper functions. - private Globalization.Calendar _threadCalendar; + private System.Globalization.Calendar _threadCalendar; private String _textBoxErrorMessage; diff --git a/System.Web.Mobile/UI/MobileControls/Adapters/WmlCalendarAdapter.cs b/System.Web.Mobile/UI/MobileControls/Adapters/WmlCalendarAdapter.cs index ad6d1c2ff..c71652d11 100644 --- a/System.Web.Mobile/UI/MobileControls/Adapters/WmlCalendarAdapter.cs +++ b/System.Web.Mobile/UI/MobileControls/Adapters/WmlCalendarAdapter.cs @@ -57,7 +57,7 @@ public class WmlCalendarAdapter : WmlControlAdapter // This member variable is set each time when calendar info needs to // be accessed and be shared for other helper functions. - private Globalization.Calendar _threadCalendar; + private System.Globalization.Calendar _threadCalendar; private String _textBoxErrorMessage; diff --git a/System.Web/AspNetEventSource.cs b/System.Web/AspNetEventSource.cs index 47ceee67b..13648d0bf 100644 --- a/System.Web/AspNetEventSource.cs +++ b/System.Web/AspNetEventSource.cs @@ -140,6 +140,10 @@ private unsafe void RequestStartedImpl(IIS7WorkerRequest wr) { // Event signals that ASP.NET has started processing a request. // Overload used only for deducing ETW parameters; use the public entry point instead. // + // Visual Studio Online #222067 - This event is hardcoded to opt-out of EventSource activityID tracking. + // This would normally be done by setting ActivityOptions = EventActivityOptions.Disable in the + // Event attribute, but this causes a dependency between System.Web and mscorlib that breaks servicing. + // // !! WARNING !! // The logic in RequestStartedImpl must be kept in [....] with these parameters, otherwise // type safety violations could occur. @@ -150,6 +154,10 @@ private unsafe void RequestStarted(string HttpVerb, string FullUrl, Guid Request } // Event signals that ASP.NET has completed processing a request. + // + // Visual Studio Online #222067 - This event is hardcoded to opt-out of EventSource activityID tracking. + // This would normally be done by setting ActivityOptions = EventActivityOptions.Disable in the + // Event attribute, but this causes a dependency between System.Web and mscorlib that breaks servicing. [Event((int)Events.RequestCompleted, Level = EventLevel.Informational, Task = (EventTask)Tasks.Request, Opcode = EventOpcode.Stop, Version = 1)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RequestCompleted() { diff --git a/System.Web/Cache/CacheDependency.cs b/System.Web/Cache/CacheDependency.cs index 60d00f6e8..9e137fd5f 100644 --- a/System.Web/Cache/CacheDependency.cs +++ b/System.Web/Cache/CacheDependency.cs @@ -909,10 +909,11 @@ internal virtual bool IsFileDependency() return false; } - // - // This method will return only the file dependencies from this dependency - // - internal virtual string[] GetFileDependencies() + /// + /// This method will return only the file dependencies from this dependency + /// + /// + public virtual string[] GetFileDependencies() { #if USE_MEMORY_CACHE if (CacheInternal.UseMemoryCache) { @@ -1121,11 +1122,12 @@ internal override bool IsFileDependency() return true; } - - // - // This method will return only the file dependencies from this dependency - // - internal override string[] GetFileDependencies() + + /// + /// This method will return only the file dependencies from this dependency + /// + /// + public override string[] GetFileDependencies() { ArrayList fileNames = null; CacheDependency[] dependencies = null; diff --git a/System.Web/Cache/SRef.cs b/System.Web/Cache/SRef.cs index 993569bd8..4ecdaa430 100644 --- a/System.Web/Cache/SRef.cs +++ b/System.Web/Cache/SRef.cs @@ -10,6 +10,7 @@ namespace System.Web.Caching { internal class SRef { private static Type s_type = Type.GetType("System.SizedReference", true, false); private Object _sizedRef; + private long _lastReportedSize; // This helps tremendously when looking at large dumps internal SRef(Object target) { _sizedRef = HttpRuntime.CreateNonPublicInstance(s_type, new object[] {target}); @@ -24,7 +25,7 @@ internal long ApproximateSize { _sizedRef, // target null, // args CultureInfo.InvariantCulture); - return (long) o; + return _lastReportedSize = (long) o; } } diff --git a/System.Web/Cache/cache.cs b/System.Web/Cache/cache.cs index 811f6e7da..0dd2f027e 100644 --- a/System.Web/Cache/cache.cs +++ b/System.Web/Cache/cache.cs @@ -519,13 +519,14 @@ class CacheCommon { internal Cache _cachePublic; internal protected CacheMemoryStats _cacheMemoryStats; private object _timerLock = new object(); - private Timer _timer; + private DisposableGCHandleRef _timerHandleRef; private int _currentPollInterval = MEMORYSTATUS_INTERVAL_30_SECONDS; internal int _inCacheManagerThread; internal bool _enableMemoryCollection; internal bool _enableExpiration; internal bool _internalConfigRead; internal SRefMultiple _srefMultiple; + private int _disposed = 0; internal CacheCommon() { _cachePublic = new Cache(0); @@ -537,13 +538,16 @@ internal CacheCommon() { internal void Dispose(bool disposing) { if (disposing) { - EnableCacheMemoryTimer(false); - _cacheMemoryStats.Dispose(); + // This method must be tolerant to multiple calls to Dispose on the same instance + if (Interlocked.Exchange(ref _disposed, 1) == 0) { + EnableCacheMemoryTimer(false); + _cacheMemoryStats.Dispose(); + } } } - internal void AddSRefTarget(CacheInternal c) { - _srefMultiple.AddSRefTarget(c); + internal void AddSRefTarget(object o) { + _srefMultiple.AddSRefTarget(o); } internal void SetCacheInternal(CacheInternal cacheInternal) { @@ -591,19 +595,20 @@ internal void EnableCacheMemoryTimer(bool enable) { if (enable) { - if (_timer == null) { + if (_timerHandleRef == null) { // has not been read yet - _timer = new Timer(new TimerCallback(this.CacheManagerTimerCallback), null, _currentPollInterval, _currentPollInterval); + Timer timer = new Timer(new TimerCallback(this.CacheManagerTimerCallback), null, _currentPollInterval, _currentPollInterval); + _timerHandleRef = new DisposableGCHandleRef(timer); Debug.Trace("Cache", "Started CacheMemoryTimers"); } else { - _timer.Change(_currentPollInterval, _currentPollInterval); + _timerHandleRef.Target.Change(_currentPollInterval, _currentPollInterval); } } else { - Timer timer = _timer; - if (timer != null && Interlocked.CompareExchange(ref _timer, null, timer) == timer) { - timer.Dispose(); + var timerHandleRef = _timerHandleRef; + if (timerHandleRef != null && Interlocked.CompareExchange(ref _timerHandleRef, null, timerHandleRef) == timerHandleRef) { + timerHandleRef.Dispose(); Debug.Trace("Cache", "Stopped CacheMemoryTimers"); } } @@ -620,7 +625,7 @@ internal void EnableCacheMemoryTimer(bool enable) { void AdjustTimer() { lock (_timerLock) { - if (_timer == null) + if (_timerHandleRef == null) return; // the order of these if statements is important @@ -629,7 +634,7 @@ void AdjustTimer() { if (_cacheMemoryStats.IsAboveHighPressure()) { if (_currentPollInterval > MEMORYSTATUS_INTERVAL_5_SECONDS) { _currentPollInterval = MEMORYSTATUS_INTERVAL_5_SECONDS; - _timer.Change(_currentPollInterval, _currentPollInterval); + _timerHandleRef.Target.Change(_currentPollInterval, _currentPollInterval); } return; } @@ -641,7 +646,7 @@ void AdjustTimer() { int newPollInterval = Math.Min(CacheMemorySizePressure.PollInterval, MEMORYSTATUS_INTERVAL_30_SECONDS); if (_currentPollInterval != newPollInterval) { _currentPollInterval = newPollInterval; - _timer.Change(_currentPollInterval, _currentPollInterval); + _timerHandleRef.Target.Change(_currentPollInterval, _currentPollInterval); } return; } @@ -649,7 +654,7 @@ void AdjustTimer() { // there is no pressure, interval should be the value from config if (_currentPollInterval != CacheMemorySizePressure.PollInterval) { _currentPollInterval = CacheMemorySizePressure.PollInterval; - _timer.Change(_currentPollInterval, _currentPollInterval); + _timerHandleRef.Target.Change(_currentPollInterval, _currentPollInterval); } } } @@ -666,7 +671,7 @@ internal long CacheManagerThread(int minPercent) { #endif try { // Dev10 633335: if the timer has been disposed, return without doing anything - if (_timer == null) + if (_timerHandleRef == null) return 0; // The timer thread must always call Update so that the CacheManager @@ -1188,7 +1193,7 @@ internal CacheSingle(CacheCommon cacheCommon, CacheMultiple cacheMultiple, int i _usage = new CacheUsage(this); _lock = new object(); _insertBlock = new ManualResetEvent(true); - cacheCommon.AddSRefTarget(this); + cacheCommon.AddSRefTarget(new { _entries, _expires, _usage }); } /* @@ -1880,24 +1885,32 @@ internal override void EnableExpirationTimer(bool enable) { class CacheMultiple : CacheInternal { int _disposed; - CacheSingle[] _caches; + DisposableGCHandleRef[] _cachesRefs; int _cacheIndexMask; internal CacheMultiple(CacheCommon cacheCommon, int numSingleCaches) : base(cacheCommon) { Debug.Assert(numSingleCaches > 1, "numSingleCaches is not greater than 1"); Debug.Assert((numSingleCaches & (numSingleCaches - 1)) == 0, "numSingleCaches is not a power of 2"); _cacheIndexMask = numSingleCaches - 1; - _caches = new CacheSingle[numSingleCaches]; + + // Each CacheSingle will have its own SRef reporting the size of the data it references. + // Objects in this CacheSingle may have refs to the root Cache and therefore reference other instances of CacheSingle. + // This leads to an unbalanced tree of SRefs and makes GC less efficient while calculating multiple SRefs on multiple cores. + // Using DisposableGCHandleRef here prevents SRefs from calculating data that does not belong to other CacheSingle instances. + _cachesRefs = new DisposableGCHandleRef[numSingleCaches]; for (int i = 0; i < numSingleCaches; i++) { - _caches[i] = new CacheSingle(cacheCommon, this, i); + _cachesRefs[i] = new DisposableGCHandleRef(new CacheSingle(cacheCommon, this, i)); } } protected override void Dispose(bool disposing) { if (disposing) { if (Interlocked.Exchange(ref _disposed, 1) == 0) { - foreach (CacheSingle cacheSingle in _caches) { - cacheSingle.Dispose(); + foreach (var cacheSingleRef in _cachesRefs) { + // Unfortunately the application shutdown logic allows user to access cache even after its disposal. + // We'll keep the GCHandle inside cacheSingleRef until it gets reclaimed during appdomain shutdown. + // And we'll only dispose the Target to preserve the old behavior. + cacheSingleRef.Target.Dispose(); } } } @@ -1908,8 +1921,8 @@ protected override void Dispose(bool disposing) { internal override int PublicCount { get { int count = 0; - foreach (CacheSingle cacheSingle in _caches) { - count += cacheSingle.PublicCount; + foreach (var cacheSingleRef in _cachesRefs) { + count += cacheSingleRef.Target.PublicCount; } return count; @@ -1919,8 +1932,8 @@ internal override int PublicCount { internal override long TotalCount { get { long count = 0; - foreach (CacheSingle cacheSingle in _caches) { - count += cacheSingle.TotalCount; + foreach (var cacheSingleRef in _cachesRefs) { + count += cacheSingleRef.Target.TotalCount; } return count; @@ -1928,22 +1941,23 @@ internal override long TotalCount { } internal override IDictionaryEnumerator CreateEnumerator() { - IDictionaryEnumerator[] enumerators = new IDictionaryEnumerator[_caches.Length]; - for (int i = 0, c = _caches.Length; i < c; i++) { - enumerators[i] = _caches[i].CreateEnumerator(); + IDictionaryEnumerator[] enumerators = new IDictionaryEnumerator[_cachesRefs.Length]; + for (int i = 0, c = _cachesRefs.Length; i < c; i++) { + enumerators[i] = _cachesRefs[i].Target.CreateEnumerator(); } return new AggregateEnumerator(enumerators); } internal CacheSingle GetCacheSingle(int hashCode) { - Debug.Assert(_caches != null && _caches.Length != 0); + Debug.Assert(_cachesRefs != null && _cachesRefs.Length != 0); // Dev10 865907: Math.Abs throws OverflowException for Int32.MinValue if (hashCode < 0) { hashCode = (hashCode == Int32.MinValue) ? 0 : -hashCode; } int index = (hashCode & _cacheIndexMask); - return _caches[index]; + Debug.Assert(_cachesRefs[index].Target != null); + return _cachesRefs[index].Target; } internal override CacheEntry UpdateCache( @@ -1960,15 +1974,15 @@ internal override CacheEntry UpdateCache( internal override long TrimIfNecessary(int percent) { long count = 0; - foreach (CacheSingle cacheSingle in _caches) { - count += cacheSingle.TrimIfNecessary(percent); + foreach (var cacheSingleRef in _cachesRefs) { + count += cacheSingleRef.Target.TrimIfNecessary(percent); } return count; } internal override void EnableExpirationTimer(bool enable) { - foreach (CacheSingle cacheSingle in _caches) { - cacheSingle.EnableExpirationTimer(enable); + foreach (var cacheSingleRef in _cachesRefs) { + cacheSingleRef.Target.EnableExpirationTimer(enable); } } } diff --git a/System.Web/Compilation/BuildManager.cs b/System.Web/Compilation/BuildManager.cs index 9f26b6ed4..65f027200 100644 --- a/System.Web/Compilation/BuildManager.cs +++ b/System.Web/Compilation/BuildManager.cs @@ -2636,6 +2636,7 @@ private void CreatePrecompMarkerFile() { } [SuppressMessage("Microsoft.Security", "MSEC1207:UseXmlReaderForLoad", Justification = "Xml file is created by us and only accessible to admins.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Xml file is created by us and only accessible to admins.")] private static bool ReadPrecompMarkerFile(string appRoot, out bool updatable) { updatable = false; diff --git a/System.Web/Compilation/PreservationFileReader.cs b/System.Web/Compilation/PreservationFileReader.cs index ebcce1503..b19034167 100644 --- a/System.Web/Compilation/PreservationFileReader.cs +++ b/System.Web/Compilation/PreservationFileReader.cs @@ -61,6 +61,7 @@ internal BuildResult ReadBuildResultFromFile(VirtualPath virtualPath, string pre } [SuppressMessage("Microsoft.Security", "MSEC1207:UseXmlReaderForLoad", Justification = "Xml file is created by us and only accessible to admins.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Xml file is created by us and only accessible to admins.")] private BuildResult ReadFileInternal(VirtualPath virtualPath, string preservationFile, long hashCode, bool ensureIsUpToDate) { XmlDocument doc = new XmlDocument(); diff --git a/System.Web/Compilation/XsdBuildProvider.cs b/System.Web/Compilation/XsdBuildProvider.cs index 7802a250b..949883305 100644 --- a/System.Web/Compilation/XsdBuildProvider.cs +++ b/System.Web/Compilation/XsdBuildProvider.cs @@ -30,6 +30,7 @@ namespace System.Web.Compilation { internal class XsdBuildProvider: BuildProvider { [SuppressMessage("Microsoft.Security", "MSEC1207:UseXmlReaderForLoad", Justification = "Developer-controlled .xsd files in application directory are implicitly trusted by ASP.Net.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Developer-controlled .xml files in application directory are implicitly trusted by ASP.Net.")] public override void GenerateCode(AssemblyBuilder assemblyBuilder) { #if !FEATURE_PAL // FEATURE_PAL does not support System.Data.Design // Get the namespace that we will use diff --git a/System.Web/Configuration/BrowserCapabilitiesCodeGenerator.cs b/System.Web/Configuration/BrowserCapabilitiesCodeGenerator.cs index 5dd5d4b54..01ef95f8a 100644 --- a/System.Web/Configuration/BrowserCapabilitiesCodeGenerator.cs +++ b/System.Web/Configuration/BrowserCapabilitiesCodeGenerator.cs @@ -32,6 +32,7 @@ namespace System.Web.Configuration { using Microsoft.Build.Utilities; using Microsoft.CSharp; + using System.Diagnostics.CodeAnalysis; [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)] [PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)] @@ -400,6 +401,7 @@ private bool IsRootNode(string nodeName) { return false; } + [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Developer-controlled .xml files in application directory are implicitly trusted by ASP.Net.")] protected void ProcessBrowserFiles(bool useVirtualPath, string virtualDir) { _browserTree = new BrowserTree(); _defaultTree = new BrowserTree(); @@ -507,6 +509,7 @@ internal void ProcessCustomBrowserFiles() { ProcessCustomBrowserFiles(false, String.Empty); } + [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Developer-controlled .xml files in application directory are implicitly trusted by ASP.Net.")] internal void ProcessCustomBrowserFiles(bool useVirtualPath, string virtualDir) { //get all custom browser files and put them in the "tree" DirectoryInfo browserDirInfo = null; diff --git a/System.Web/Configuration/HttpCapabilitiesSectionHandler.cs b/System.Web/Configuration/HttpCapabilitiesSectionHandler.cs index cddc9f294..2bd04aa4b 100644 --- a/System.Web/Configuration/HttpCapabilitiesSectionHandler.cs +++ b/System.Web/Configuration/HttpCapabilitiesSectionHandler.cs @@ -8,6 +8,7 @@ namespace System.Web.Configuration { using System.Collections; using System.Configuration; + using System.Diagnostics.CodeAnalysis; using System.IO; using System.Security; using System.Security.Permissions; @@ -15,7 +16,6 @@ namespace System.Web.Configuration { using System.Web.Configuration; using System.Web.Util; using System.Xml; - using Pair = System.Web.UI.Pair; // @@ -276,6 +276,7 @@ static void ProcessResult(HttpCapabilitiesDefaultProvider capabilitiesEvaluator, // // ResolveFiles - parse files referenced with // + [SuppressMessage("Microsoft.Security.Xml", "CA3056:UseXmlReaderForLoad", Justification = "Developer-controlled .xml files in application directory are implicitly trusted by ASP.Net.")] static void ResolveFiles(ParseState parseState, object configurationContext) { // diff --git a/System.Web/Configuration/RemoteWebConfigurationHostServer.cs b/System.Web/Configuration/RemoteWebConfigurationHostServer.cs index 8100d4659..e40ceb29e 100644 --- a/System.Web/Configuration/RemoteWebConfigurationHostServer.cs +++ b/System.Web/Configuration/RemoteWebConfigurationHostServer.cs @@ -21,6 +21,7 @@ namespace System.Web.Configuration { using System.Security.AccessControl; #endif // !FEATURE_PAL using System.Security.Permissions; + using System.Diagnostics.CodeAnalysis; #if !FEATURE_PAL // FEATURE_PAL does not enable COM @@ -215,6 +216,7 @@ public string GetFilePaths(int webLevelAsInt, string path, string site, string l return sb.ToString(); } + [SuppressMessage("Microsoft.Security.Xml", "CA3057:DoNotUseLoadXml", Justification = "Developer-controlled xml contents are implicitly trusted by ASP.Net.")] public string DoEncryptOrDecrypt(bool doEncrypt, string xmlString, string protectionProviderName, string protectionProviderType, string[] paramKeys, string[] paramValues) { Type t = Type.GetType(protectionProviderType, true); diff --git a/System.Web/Handlers/TransferRequestHandler.cs b/System.Web/Handlers/TransferRequestHandler.cs index 5c74438c2..0534ff1c5 100644 --- a/System.Web/Handlers/TransferRequestHandler.cs +++ b/System.Web/Handlers/TransferRequestHandler.cs @@ -6,11 +6,21 @@ namespace System.Web.Handlers { using System; + using System.Threading.Tasks; using System.Web.Hosting; - - internal class TransferRequestHandler : IHttpHandler { - - public void ProcessRequest(HttpContext context) { + + internal class TransferRequestHandler : IHttpAsyncHandler { + public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) + { + return TaskAsyncHelper.BeginTask(() => ProcessRequestAsync(context), cb, extraData); + } + + public void EndProcessRequest(IAsyncResult result) + { + TaskAsyncHelper.EndTask(result); + } + + private Task ProcessRequestAsync(HttpContext context) { IIS7WorkerRequest wr = context.WorkerRequest as IIS7WorkerRequest; if (wr == null) { throw new PlatformNotSupportedException(SR.GetString(SR.Requires_Iis_Integrated_Mode)); @@ -24,14 +34,26 @@ public void ProcessRequest(HttpContext context) { context.Request.EntityBody, null, preserveUser: false); - + // force the completion of the current request so that the // child execution can be performed immediately after unwind - context.ApplicationInstance.EnsureReleaseState(); + var releaseStateTask = context.ApplicationInstance.EnsureReleaseStateAsync(); // DevDiv Bugs 162750: IIS7 Integrated Mode: TransferRequest performance issue // Instead of calling Response.End we call HttpApplication.CompleteRequest() - context.ApplicationInstance.CompleteRequest(); + if (releaseStateTask.IsCompleted) { + context.ApplicationInstance.CompleteRequest(); + return TaskAsyncHelper.CompletedTask; + } + else { + return releaseStateTask.ContinueWith((_) => context.ApplicationInstance.CompleteRequest()); + } + } + + public void ProcessRequest(HttpContext context) + { + string errorMessage = SR.GetString(SR.HttpTaskAsyncHandler_CannotExecuteSynchronously, GetType()); + throw new NotSupportedException(errorMessage); } public bool IsReusable { diff --git a/System.Web/Hosting/ApplicationManager.cs b/System.Web/Hosting/ApplicationManager.cs index e02ad7fb5..9a24a5b37 100644 --- a/System.Web/Hosting/ApplicationManager.cs +++ b/System.Web/Hosting/ApplicationManager.cs @@ -82,6 +82,9 @@ public sealed class ApplicationManager : MarshalByRefObject { // delegate OnRespondToPing private WaitCallback _onRespondToPingWaitCallback; + // flag indicates whether any fatal exception has been recorded + private bool _fatalExceptionRecorded = false; + // single instance of app manager private static ApplicationManager _theAppManager; @@ -154,6 +157,16 @@ internal bool ShutdownInProgress { } } + private bool FatalExceptionRecorded + { + get { + return _fatalExceptionRecorded; + } + set { + _fatalExceptionRecorded = value; + } + } + internal static void RecordFatalException(Exception e) { RecordFatalException(AppDomain.CurrentDomain, e); } @@ -168,7 +181,7 @@ internal static void RecordFatalException(AppDomain appDomain, Exception e) { } } - private static void OnUnhandledException(Object sender, UnhandledExceptionEventArgs eventArgs) { + internal static void OnUnhandledException(Object sender, UnhandledExceptionEventArgs eventArgs) { // if the CLR is not terminating, ignore the notification if (!eventArgs.IsTerminating) { return; @@ -184,6 +197,15 @@ private static void OnUnhandledException(Object sender, UnhandledExceptionEventA return; } + // If any fatal exception was recorded in applicaiton AppDomains, + // we wouldn't record exceptions in the default AppDomain. + var appManager = GetApplicationManager(); + if (AppDomain.CurrentDomain.IsDefaultAppDomain() && appManager.FatalExceptionRecorded) { + return; + } + + appManager.FatalExceptionRecorded = true; + RecordFatalException(appDomain, exception); } diff --git a/System.Web/Hosting/HostingEnvironment.cs b/System.Web/Hosting/HostingEnvironment.cs index d5d2c15e3..27ce61302 100644 --- a/System.Web/Hosting/HostingEnvironment.cs +++ b/System.Web/Hosting/HostingEnvironment.cs @@ -184,6 +184,11 @@ public HostingEnvironment() { // start watching for app domain unloading _onAppDomainUnload = new EventHandler(OnAppDomainUnload); Thread.GetDomain().DomainUnload += _onAppDomainUnload; + + // VSO 160528: We used to listen to the default AppDomain's UnhandledException only. + // However, non-serializable exceptions cannot be passed to the default domain. Therefore + // we should try to log exceptions in application AppDomains. + Thread.GetDomain().UnhandledException += new UnhandledExceptionEventHandler(ApplicationManager.OnUnhandledException); } internal long TrimCache(int percent) { diff --git a/System.Web/HttpApplication.cs b/System.Web/HttpApplication.cs index eff422558..41564928c 100644 --- a/System.Web/HttpApplication.cs +++ b/System.Web/HttpApplication.cs @@ -500,17 +500,37 @@ internal EventArgs AppEvent { } } - // DevDiv Bugs 151914: Release session state before executing child request - internal void EnsureReleaseState() { + private ISessionStateModule FindISessionStateModule() { + if (!HttpRuntime.UseIntegratedPipeline) + return null; + if (_moduleCollection != null) { for (int i = 0; i < _moduleCollection.Count; i++) { - IHttpModule module = _moduleCollection.Get(i); - if (module is SessionStateModule) { - ((SessionStateModule) module).EnsureReleaseState(this); - break; + ISessionStateModule module = _moduleCollection.Get(i) as ISessionStateModule; + if (module != null) { + return module; } } } + + return null; + } + + // DevDiv Bugs 151914: Release session state before executing child request + internal void EnsureReleaseState() { + ISessionStateModule module = FindISessionStateModule(); + if (module != null) { + module.ReleaseSessionState(Context); + } + } + + internal Task EnsureReleaseStateAsync() { + ISessionStateModule module = FindISessionStateModule(); + if (module != null) { + return module.ReleaseSessionStateAsync(Context); + } + + return TaskAsyncHelper.CompletedTask; } /// diff --git a/System.Web/HttpCacheParams.cs b/System.Web/HttpCacheParams.cs index 6d4bae316..f54da48a4 100644 --- a/System.Web/HttpCacheParams.cs +++ b/System.Web/HttpCacheParams.cs @@ -39,10 +39,11 @@ internal void Reset() { _ignoreParams = -1; } - /* - * Reset based on the cached vary headers. - */ - internal void ResetFromParams(String[] parameters) { + /// + /// Set the Parameters in Cache Vary + /// + /// + public void SetParams(string[] parameters) { int i, n; Reset(); @@ -75,16 +76,20 @@ internal bool AcceptsParams() { return _ignoreParams == 1 || _paramsStar || _parameters != null; } - internal String[] GetParams() { - String[] s = null; + /// + /// Get the Parameters in Cache Vary + /// + /// + public string[] GetParams() { + string[] s = null; Object item; int i, j, c, n; if (_ignoreParams == 1) { - s = new String[1] {String.Empty}; + s = new string[1] {string.Empty}; } else if (_paramsStar) { - s = new String[1] {"*"}; + s = new string[1] {"*"}; } else if (_parameters != null) { n = _parameters.Size; @@ -102,7 +107,7 @@ internal String[] GetParams() { for (i = 0; i < n; i++) { item = _parameters.GetValue(i); if (item != null) { - s[j] = (String) item; + s[j] = (string) item; j++; } } @@ -116,7 +121,7 @@ internal String[] GetParams() { // // Public methods and properties - // + // /// diff --git a/System.Web/HttpCachePolicy.cs b/System.Web/HttpCachePolicy.cs index 5cdde5d30..0e5efebd8 100644 --- a/System.Web/HttpCachePolicy.cs +++ b/System.Web/HttpCachePolicy.cs @@ -30,7 +30,7 @@ namespace System.Web { // // Public constants for cache-control // - + /// /// @@ -573,12 +573,10 @@ internal void ResetFromHttpCachePolicySettings( int i, n; string[] fields; - - _utcTimestampRequest = utcTimestampRequest; - - _varyByContentEncodings.ResetFromContentEncodings(settings.VaryByContentEncodings); - _varyByHeaders.ResetFromHeaders(settings.VaryByHeaders); - _varyByParams.ResetFromParams(settings.VaryByParams); + + _varyByContentEncodings.SetContentEncodings(settings.VaryByContentEncodings); + _varyByHeaders.SetHeaders(settings.VaryByHeaders); + _varyByParams.SetParams(settings.VaryByParams); _isModified = settings.IsModified; _hasSetCookieHeader = settings.hasSetCookieHeader; @@ -645,7 +643,11 @@ internal void ResetFromHttpCachePolicySettings( } } - internal bool IsModified() { + /// + /// Return true if the CachePolicy has been modified + /// + /// + public bool IsModified() { return _isModified || _varyByContentEncodings.IsModified() || _varyByHeaders.IsModified() || _varyByParams.IsModified(); } @@ -771,20 +773,18 @@ void UpdateCachedHeaders(HttpResponse response) { return; } - Debug.Assert((_utcTimestampCreated == DateTime.MinValue && _utcTimestampRequest == DateTime.MinValue) || - (_utcTimestampCreated != DateTime.MinValue && _utcTimestampRequest != DateTime.MinValue), - "_utcTimestampCreated and _utcTimestampRequest are out of [....] in UpdateCachedHeaders"); - + //To enable Out of Band OutputCache Module support, we will always refresh the UtcTimestampRequest. if (_utcTimestampCreated == DateTime.MinValue) { - _utcTimestampCreated = _utcTimestampRequest = response.Context.UtcTimestamp; + _utcTimestampCreated = response.Context.UtcTimestamp; } + _utcTimestampRequest = response.Context.UtcTimestamp; if (_slidingExpiration != 1) { _slidingDelta = TimeSpan.Zero; } else if (_isMaxAgeSet) { _slidingDelta = _maxAge; - } + } else if (_isExpiresSet) { _slidingDelta = _utcExpires - _utcTimestampCreated; } @@ -840,7 +840,7 @@ void UpdateCachedHeaders(HttpResponse response) { } sb.Append('\"'); - } + } if (_noStore) { AppendValueToHeader(sb, "no-store"); @@ -1056,10 +1056,10 @@ internal void GetHeaders(ArrayList headers, HttpResponse response) { headers.Add(_headerVaryBy); } } - + /* - * Public methods - */ + * Public methods + */ internal HttpCachePolicySettings GetCurrentSettings(HttpResponse response) { String[] varyByContentEncodings; @@ -1191,6 +1191,16 @@ internal DateTime UtcGetAbsoluteExpiration() { return absoluteExpiration; } + // Expose this property to OutputCacheUtility class + // In order to enable Out of Band output cache module to access the Validation Callback Info + internal IEnumerable GetValidationCallbacks() { + if (_validationCallbackInfo == null) { + return new ArrayList(); + } + + return _validationCallbackInfo; + } + /* * Cache at server? */ @@ -1203,7 +1213,11 @@ public void SetNoServerCaching() { _noServerCaching = true; } - internal bool GetNoServerCaching() { + /// + /// Return True if we should stops all server caching for current response + /// + /// + public bool GetNoServerCaching() { return _noServerCaching; } @@ -1229,6 +1243,13 @@ public void SetVaryByCustom(string custom) { _varyByCustom = custom; } + /// + /// Get the Vary by Custom Value + /// + /// + public string GetVaryByCustom() { + return _varyByCustom; + } /* * Cache-Control: extension */ @@ -1250,6 +1271,14 @@ public void AppendCacheExtension(String extension) { } } + /// + /// Get Cache Extensions Value + /// + /// + public string GetCacheExtensions() { + return _cacheExtension; + } + /* * Cache-Control: no-transform */ @@ -1263,11 +1292,27 @@ public void SetNoTransforms() { _noTransforms = true; } + /// + /// Return true if No-transform directive, enables the sending of the CacheControl + /// + /// + public bool GetNoTransforms() { + return _noTransforms; + } + internal void SetIgnoreRangeRequests() { Dirtied(); _ignoreRangeRequests = true; } + /// + /// Return true if ignore range request + /// + /// + public bool GetIgnoreRangeRequests() { + return _ignoreRangeRequests; + } + /// /// Contains policy for the Vary: header. /// @@ -1320,11 +1365,15 @@ public void SetCacheability(HttpCacheability cacheability) { } } - internal HttpCacheability GetCacheability() { + /// + /// Get the Cache-control (public, private and no-cache) directive + /// + /// + public HttpCacheability GetCacheability() { return _cacheability; } - - + + /// /// Sets the Cache-Control header to one of the values of HttpCacheability in /// conjunction with a field-level exclusion directive. @@ -1376,6 +1425,14 @@ internal void SetDependencies(bool hasUserProvidedDependencies) { Dirtied(); _hasUserProvidedDependencies = hasUserProvidedDependencies; } + + /// + /// return true if no store is set + /// + /// + public bool GetNoStore() { + return _noStore; + } /* * Expiration policy. @@ -1405,6 +1462,14 @@ public void SetExpires(DateTime date) { } } + /// + /// Return the expire header as absolute expire datetime + /// + /// + public DateTime GetExpires() { + return _utcExpires; + } + /* * Cache-Control: max-age=delta-seconds */ @@ -1428,6 +1493,14 @@ public void SetMaxAge(TimeSpan delta) { } } + /// + /// Get the Cache-Control Max Age + /// + /// + public TimeSpan GetMaxAge() { + return _maxAge; + } + // Suppress max-age and s-maxage in cache-control header (required for IIS6 kernel mode cache) internal void SetNoMaxAgeInCacheControl() { _noMaxAgeInCacheControl = true; @@ -1452,6 +1525,14 @@ public void SetProxyMaxAge(TimeSpan delta) { } } + /// + /// Get the Cache-Control: Proxy Max Age Value + /// + /// + public TimeSpan GetProxyMaxAge() { + return _proxyMaxAge; + } + /* * Sliding Expiration */ @@ -1470,6 +1551,17 @@ public void SetSlidingExpiration(bool slide) { } } + /// + /// Return true if to make expiration sliding. that is, if cached, it should be renewed with each + /// response. This feature is identical in spirit to the IIS + /// configuration option to add an expiration header relative to the current response + /// time. This feature is identical in spirit to the IIS configuration option to add + /// an expiration header relative to the current response time. + /// + /// + public bool HasSlidingExpiration() { + return _slidingExpiration == 1; + } public void SetValidUntilExpires(bool validUntilExpires) { if (_validUntilExpires == -1 || _validUntilExpires == 1) { @@ -1478,6 +1570,13 @@ public void SetValidUntilExpires(bool validUntilExpires) { } } + /// + /// Return true if valid until expires + /// + /// + public bool IsValidUntilExpires() { + return _validUntilExpires == 1; + } public void SetAllowResponseInBrowserHistory(bool allow) { if (_allowInHistory == -1 || _allowInHistory == 1) { @@ -1512,7 +1611,17 @@ public void SetRevalidation(HttpCacheRevalidation revalidation) { } } - /* + /// + /// Get the Cache-Control: header to reflect either the must-revalidate or + /// proxy-revalidate directives. + /// The default is to not send either of these directives unless explicitly enabled using this method. + /// + /// + public HttpCacheRevalidation GetRevalidation() { + return _revalidation; + } + + /* * Etag */ @@ -1537,6 +1646,16 @@ public void SetETag(String etag) { _etag = etag; } + /// + /// Get the ETag header. Once an ETag is set, + /// subsequent attempts to set it will fail and an exception will be thrown. + /// + /// + public string GetETag() { + return _etag; + } + + /* * Last-Modified: RFC Date */ @@ -1577,6 +1696,14 @@ void UtcSetLastModified(DateTime utcDate) { } } + /// + /// Get the Last-Modified header. + /// + /// + public DateTime GetUtcLastModified() { + return _utcLastModified; + } + /// /// Sets the Last-Modified: header based on the timestamps of the @@ -1587,6 +1714,15 @@ public void SetLastModifiedFromFileDependencies() { _generateLastModifiedFromFiles = true; } + /// + /// Return true if the Last-Modified header is set to base on the timestamps of the + /// file dependencies of the handler. + /// + /// + public bool GetLastModifiedFromFileDependencies() { + return _generateLastModifiedFromFiles; + } + /// /// Sets the Etag header based on the timestamps of the file @@ -1601,6 +1737,14 @@ public void SetETagFromFileDependencies() { _generateEtagFromFiles = true; } + /// + /// Return true if the Etag header has been set to base on the timestamps of the file + /// dependencies of the handler + /// + /// + public bool GetETagFromFileDependencies() { + return _generateEtagFromFiles; + } public void SetOmitVaryStar(bool omit) { Dirtied(); @@ -1610,6 +1754,13 @@ public void SetOmitVaryStar(bool omit) { } } + /// + /// Return true if to omit Vary Star + /// + /// + public int GetOmitVaryStar() { + return _omitVaryStar; + } /// /// Registers a validation callback for the current response. @@ -1628,5 +1779,16 @@ public void AddValidationCallback( _validationCallbackInfo.Add(new ValidationCallbackInfo(handler, data)); } + /// + /// Utc Timestamp Created + /// + public DateTime UtcTimestampCreated { + get { + return _utcTimestampCreated; + } + set { + _utcTimestampCreated = value; + } + } } } diff --git a/System.Web/HttpCacheVary.cs b/System.Web/HttpCacheVary.cs index e55c71d05..7f8a952f0 100644 --- a/System.Web/HttpCacheVary.cs +++ b/System.Web/HttpCacheVary.cs @@ -37,10 +37,12 @@ internal void Reset() { _headers = null; } - /* - * Reset based on the cached vary headers. - */ - internal void ResetFromHeaders(String[] headers) { + /// + /// Set the Headers in Cache Vary + /// + /// + public void SetHeaders(string[] headers) { + int i, n; if (headers == null) { @@ -48,7 +50,7 @@ internal void ResetFromHeaders(String[] headers) { _varyStar = false; _headers = null; } - else { + else { _isModified = true; if (headers[0].Equals("*")) { Debug.Assert(headers.Length == 1, "headers.Length == 1"); @@ -97,19 +99,19 @@ internal String ToHeaderString() { return null; } + + /// + /// Get the Headers in Cache Vary + /// + /// + public string[] GetHeaders() { + string[] s = null; - /* - * Returns the headers, for package access only. - * - * @return the headers. - */ - internal String[] GetHeaders() { - String[] s = null; Object item; int i, j, c, n; if (_varyStar) { - return new String[1] {"*"}; + return new string[1] {"*"}; } else if (_headers != null) { n = _headers.Size; @@ -127,7 +129,7 @@ internal String[] GetHeaders() { for (i = 0; i < n; i++) { item = _headers.GetValue(i); if (item != null) { - s[j] = (String) item; + s[j] = (string) item; j++; } } @@ -138,7 +140,7 @@ internal String[] GetHeaders() { return s; } - + // // Public methods and properties // diff --git a/System.Web/HttpCacheVaryByContentEncodings.cs b/System.Web/HttpCacheVaryByContentEncodings.cs index fe1e6d355..236adffa9 100644 --- a/System.Web/HttpCacheVaryByContentEncodings.cs +++ b/System.Web/HttpCacheVaryByContentEncodings.cs @@ -32,11 +32,13 @@ internal void Reset() { _isModified = false; _contentEncodings = null; } + + /// + /// Set the Content Encodings in Cache Vary + /// + /// + public void SetContentEncodings(string[] contentEncodings) { - /* - * Reset based on content encodings. - */ - internal void ResetFromContentEncodings(String[] contentEncodings) { Reset(); if (contentEncodings != null) { _isModified = true; @@ -75,9 +77,18 @@ internal bool IsCacheableEncoding(string coding) { internal bool IsModified() { return _isModified; } - - internal String[] GetContentEncodings() { - return _contentEncodings; + + /// + /// Get the Content Encodings in Cache Vary + /// + /// + public string[] GetContentEncodings() { + if (_contentEncodings != null) { + string[] contentEncodings = new string[_contentEncodings.Length]; + _contentEncodings.CopyTo(contentEncodings, 0); + return contentEncodings; + } + return null; } // diff --git a/System.Web/HttpContext.cs b/System.Web/HttpContext.cs index 4d02a0a8a..812c77906 100644 --- a/System.Web/HttpContext.cs +++ b/System.Web/HttpContext.cs @@ -22,6 +22,7 @@ namespace System.Web { using System.Linq; using System.Net; using System.Reflection; + using System.Runtime.CompilerServices; using System.Runtime.Remoting.Messaging; using System.Security.Permissions; using System.Security.Principal; @@ -1009,6 +1010,8 @@ public HttpSessionState Session { if (_delayedSessionState) { lock (this) { if (_delayedSessionState) { + Debug.Assert(_sessionStateModule != null, "_sessionStateModule != null"); + // If it's not null, it means we have a delayed session state item _sessionStateModule.InitStateStoreItem(true); _delayedSessionState = false; @@ -1020,8 +1023,21 @@ public HttpSessionState Session { } } + [MethodImpl(MethodImplOptions.NoInlining)] internal void EnsureSessionStateIfNecessary() { - Debug.Assert(_sessionStateModule != null, "_sessionStateModule != null"); + if (_sessionStateModule == null) + { + // If _sessionStateModule is null, we wouldn't be able to call + // _sessionStateModule.EnsureStateStoreItemLocked(), so we return here. + // _sessionStateModule could be null in the following cases, + // 1. No session state acquired. + // 2. HttpResponse.Flush() happens after session state being released. + // 3. The session state module in use is not System.Web.SessionState.SessionStateModule. + // + // This method is for the in-framework SessionStateModule only. + // OOB SessionStateModule can achieve this by using HttpResponse.AddOnSendingHeaders. + return; + } HttpSessionState session = (HttpSessionState)Items[SessionStateUtility.SESSION_KEY]; @@ -1042,7 +1058,6 @@ internal void AddHttpSessionStateModule(SessionStateModule module, bool delayed) } internal void RemoveHttpSessionStateModule() { - Debug.Assert(_sessionStateModule != null, "_sessionStateModule != null"); _delayedSessionState = false; _sessionStateModule = null; } diff --git a/System.Web/HttpResponse.cs b/System.Web/HttpResponse.cs index b92ce6252..db4f72143 100644 --- a/System.Web/HttpResponse.cs +++ b/System.Web/HttpResponse.cs @@ -1126,9 +1126,18 @@ internal void UseSnapshot(HttpRawResponse rawResponse, bool sendBody) { } // restore content - _httpWriter.UseSnapshot(rawResponse.Buffers); + SetResponseBuffers(rawResponse.Buffers); + + _suppressContent = !sendBody; + } + + // set the response content bufffers + internal void SetResponseBuffers(ArrayList buffers) { + if (_httpWriter == null) { + throw new HttpException(SR.GetString(SR.Cannot_use_snapshot_for_TextWriter)); + } - _suppressContent = !sendBody; + _httpWriter.UseSnapshot(buffers); } internal void CloseConnectionAfterError() { diff --git a/System.Web/ModelBinding/DataAnnotationsModelMetadataProvider.cs b/System.Web/ModelBinding/DataAnnotationsModelMetadataProvider.cs index aa91b3306..3c0296df9 100644 --- a/System.Web/ModelBinding/DataAnnotationsModelMetadataProvider.cs +++ b/System.Web/ModelBinding/DataAnnotationsModelMetadataProvider.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; + using System.Web.Globalization; public class DataAnnotationsModelMetadataProvider : AssociatedMetadataProvider { @@ -11,7 +12,7 @@ protected override ModelMetadata CreateMetadata(IEnumerable attribute List attributeList = new List(attributes); DisplayColumnAttribute displayColumnAttribute = attributeList.OfType().FirstOrDefault(); DataAnnotationsModelMetadata result = new DataAnnotationsModelMetadata(this, containerType, modelAccessor, modelType, propertyName, displayColumnAttribute); - + #if UNDEF // Do [HiddenInput] before [UIHint], so you can override the template hint HiddenInputAttribute hiddenInputAttribute = attributeList.OfType().FirstOrDefault(); @@ -71,12 +72,13 @@ protected override ModelMetadata CreateMetadata(IEnumerable attribute DisplayAttribute display = attributes.OfType().FirstOrDefault(); string name = null; if (display != null) { - result.Description = display.GetDescription(); - result.ShortDisplayName = display.GetShortName(); - result.Watermark = display.GetPrompt(); - result.Order = display.GetOrder() ?? ModelMetadata.DefaultOrder; + var displayAdapter = new DisplayAttributeAdapter(display); + result.Description = displayAdapter.GetDescription(); + result.ShortDisplayName = displayAdapter.GetShortName(); + result.Watermark = displayAdapter.GetPrompt(); + result.Order = displayAdapter.GetOrder() ?? ModelMetadata.DefaultOrder; - name = display.GetName(); + name = displayAdapter.GetName(); } if (name != null) { diff --git a/System.Web/ModelBinding/DataAnnotationsModelValidator.cs b/System.Web/ModelBinding/DataAnnotationsModelValidator.cs index cff887cbb..79bf561a2 100644 --- a/System.Web/ModelBinding/DataAnnotationsModelValidator.cs +++ b/System.Web/ModelBinding/DataAnnotationsModelValidator.cs @@ -2,8 +2,10 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; + using System.Threading; + using System.Web.Globalization; - public class DataAnnotationsModelValidator : ModelValidator { + public class DataAnnotationsModelValidator : ModelValidator { public DataAnnotationsModelValidator(ModelMetadata metadata, ModelBindingExecutionContext context, ValidationAttribute attribute) : base(metadata, context) { @@ -18,7 +20,24 @@ public DataAnnotationsModelValidator(ModelMetadata metadata, ModelBindingExecuti protected internal string ErrorMessage { get { - return Attribute.FormatErrorMessage(Metadata.GetDisplayName()); + if (UseStringLocalizerProvider) { + var errorMsg = GetLocalizedString(Attribute.ErrorMessage); + + return errorMsg ?? Attribute.FormatErrorMessage(Metadata.GetDisplayName()); + } + else { + return Attribute.FormatErrorMessage(Metadata.GetDisplayName()); + } + } + } + + protected string GetLocalizedString(string name, params object[] arguments) { + if (StringLocalizerProviders.DataAnnotationStringLocalizerProvider != null) { + return StringLocalizerProviders.DataAnnotationStringLocalizerProvider + .GetLocalizedString(Thread.CurrentThread.CurrentUICulture, name, arguments); + } + else { + return null; } } @@ -55,9 +74,37 @@ public override IEnumerable Validate(object container) { ValidationResult result = Attribute.GetValidationResult(Metadata.Model, context); if (result != ValidationResult.Success) { yield return new ModelValidationResult { - Message = result.ErrorMessage + Message = GetValidationErrorMessage(result) }; } } + + protected virtual string GetLocalizedErrorMessage(string errorMessage) { + return GetLocalizedString(errorMessage, Metadata.GetDisplayName()); + } + + private string GetValidationErrorMessage(ValidationResult result) { + string errorMsg; + + if (UseStringLocalizerProvider) { + errorMsg = GetLocalizedErrorMessage(Attribute.ErrorMessage); + + errorMsg = errorMsg ?? result.ErrorMessage; + } + else { + errorMsg = result.ErrorMessage; + } + return errorMsg; + } + + private bool UseStringLocalizerProvider { + get { + // if developer already uses existing localization feature, + // then we don't opt in the new localization feature. + return (!string.IsNullOrEmpty(Attribute.ErrorMessage) && + string.IsNullOrEmpty(Attribute.ErrorMessageResourceName) && + Attribute.ErrorMessageResourceType == null); + } + } } } diff --git a/System.Web/ModelBinding/DataAnnotationsModelValidatorProvider.cs b/System.Web/ModelBinding/DataAnnotationsModelValidatorProvider.cs index 11cce2416..ae952e12a 100644 --- a/System.Web/ModelBinding/DataAnnotationsModelValidatorProvider.cs +++ b/System.Web/ModelBinding/DataAnnotationsModelValidatorProvider.cs @@ -1,5 +1,5 @@ namespace System.Web.ModelBinding { - using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; @@ -7,6 +7,7 @@ using System.Linq; using System.Reflection; using System.Threading; +using System.Web.Globalization; // A factory for validators based on ValidationAttribute public delegate ModelValidator DataAnnotationsModelValidationFactory(ModelMetadata metadata, ModelBindingExecutionContext context, ValidationAttribute attribute); @@ -26,7 +27,7 @@ public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProvider { private static bool _addImplicitRequiredAttributeForValueTypes = true; private static ReaderWriterLockSlim _adaptersLock = new ReaderWriterLockSlim(); - + // Factories for validation attributes internal static DataAnnotationsModelValidationFactory DefaultAttributeFactory = @@ -49,6 +50,14 @@ public class DataAnnotationsModelValidatorProvider : AssociatedValidatorProvider typeof(StringLengthAttribute), (metadata, context, attribute) => new StringLengthAttributeAdapter(metadata, context, (StringLengthAttribute)attribute) }, + { + typeof(MinLengthAttribute), + (metadata, context, attribute) => new MinLengthAttributeAdapter(metadata, context, (MinLengthAttribute)attribute) + }, + { + typeof(MaxLengthAttribute), + (metadata, context, attribute) => new MaxLengthAttributeAdapter(metadata, context, (MaxLengthAttribute)attribute) + }, }; // Factories for IValidatableObject models @@ -66,7 +75,7 @@ public static bool AddImplicitRequiredAttributeForValueTypes { _addImplicitRequiredAttributeForValueTypes = value; } } - + protected override IEnumerable GetValidators(ModelMetadata metadata, ModelBindingExecutionContext context, IEnumerable attributes) { _adaptersLock.EnterReadLock(); diff --git a/System.Web/ModelBinding/RangeAttributeAdapter.cs b/System.Web/ModelBinding/RangeAttributeAdapter.cs index 2c5acf046..28858643c 100644 --- a/System.Web/ModelBinding/RangeAttributeAdapter.cs +++ b/System.Web/ModelBinding/RangeAttributeAdapter.cs @@ -6,6 +6,11 @@ public RangeAttributeAdapter(ModelMetadata metadata, ModelBindingExecutionContex : base(metadata, context, attribute) { } + protected override string GetLocalizedErrorMessage(string errorMessage) { + return GetLocalizedString(errorMessage, Metadata.GetDisplayName(), Attribute.Minimum, Attribute.Maximum); + + } + #if UNDEF public override IEnumerable GetClientValidationRules() { string errorMessage = ErrorMessage; // Per Dev10 Bug #923283, need to make sure ErrorMessage is called before Minimum/Maximum diff --git a/System.Web/ModelBinding/RegularExpressionAttributeAdapter.cs b/System.Web/ModelBinding/RegularExpressionAttributeAdapter.cs index c5c0f0d52..2a49853c2 100644 --- a/System.Web/ModelBinding/RegularExpressionAttributeAdapter.cs +++ b/System.Web/ModelBinding/RegularExpressionAttributeAdapter.cs @@ -6,6 +6,10 @@ public RegularExpressionAttributeAdapter(ModelMetadata metadata, ModelBindingExe : base(metadata, context, attribute) { } + protected override string GetLocalizedErrorMessage(string errorMessage) { + return GetLocalizedString(errorMessage, Metadata.GetDisplayName(), Attribute.Pattern); + } + #if UNDEF public override IEnumerable GetClientValidationRules() { return new[] { new ModelClientValidationRegexRule(ErrorMessage, Attribute.Pattern) }; diff --git a/System.Web/ModelBinding/StringLengthAttributeAdapter.cs b/System.Web/ModelBinding/StringLengthAttributeAdapter.cs index 354cc3a86..a27cba17d 100644 --- a/System.Web/ModelBinding/StringLengthAttributeAdapter.cs +++ b/System.Web/ModelBinding/StringLengthAttributeAdapter.cs @@ -6,6 +6,10 @@ public StringLengthAttributeAdapter(ModelMetadata metadata, ModelBindingExecutio : base(metadata, context, attribute) { } + protected override string GetLocalizedErrorMessage(string errorMessage) { + return GetLocalizedString(errorMessage, Metadata.GetDisplayName(), Attribute.MinimumLength, Attribute.MaximumLength); + } + #if UNDEF public override IEnumerable GetClientValidationRules() { return new[] { new ModelClientValidationStringLengthRule(ErrorMessage, Attribute.MinimumLength, Attribute.MaximumLength) }; diff --git a/System.Web/Security/Cryptography/CryptoAlgorithms.cs b/System.Web/Security/Cryptography/CryptoAlgorithms.cs index a4fcc08f6..a4147901f 100644 --- a/System.Web/Security/Cryptography/CryptoAlgorithms.cs +++ b/System.Web/Security/Cryptography/CryptoAlgorithms.cs @@ -44,6 +44,7 @@ internal static DES CreateDES() { return new DESCryptoServiceProvider(); } + [SuppressMessage("Microsoft.Security.Cryptography", "CA5354:SHA1CannotBeUsed", Justification = @"This is only used by legacy code; new features do not use this algorithm.")] internal static HMACSHA1 CreateHMACSHA1() { return new HMACSHA1(); } diff --git a/System.Web/Security/FormsIdentity.cs b/System.Web/Security/FormsIdentity.cs index 2fba9c459..a07c22340 100644 --- a/System.Web/Security/FormsIdentity.cs +++ b/System.Web/Security/FormsIdentity.cs @@ -17,6 +17,7 @@ namespace System.Web.Security { using System.Security; using System.Security.Claims; using System.Security.Permissions; + using System.Security.Principal; /// /// This class is an IIdentity derived class @@ -77,7 +78,7 @@ public FormsIdentity (FormsAuthenticationTicket ticket) { /// Constructor. /// protected FormsIdentity(FormsIdentity identity) - : base(identity) + : base((IIdentity)identity) { _Ticket = identity._Ticket; } diff --git a/System.Web/State/SessionStateModule.cs b/System.Web/State/SessionStateModule.cs index 2e9144cce..84b3d55a8 100644 --- a/System.Web/State/SessionStateModule.cs +++ b/System.Web/State/SessionStateModule.cs @@ -27,6 +27,7 @@ namespace System.Web.SessionState { using System.Globalization; using System.Security.Permissions; using System.Text; + using System.Threading.Tasks; using System.Web.Hosting; using System.Web.Management; using Microsoft.Win32; @@ -111,7 +112,7 @@ internal void RaiseSessionOnEnd(String id, SessionStateStoreData item) { /// /// [To be supplied.] /// - public sealed class SessionStateModule : IHttpModule { + public sealed class SessionStateModule : ISessionStateModule { internal const string SQL_CONNECTION_STRING_DEFAULT = "data source=localhost;Integrated Security=SSPI"; internal const string STATE_CONNECTION_STRING_DEFAULT = "tcpip=loopback:42424"; @@ -1446,15 +1447,19 @@ internal bool SessionIDManagerUseCookieless { } } } - - // DevDiv Bugs 151914: Release session state before executing child request - internal void EnsureReleaseState(HttpApplication app) { + + public void ReleaseSessionState(HttpContext context) { if (HttpRuntime.UseIntegratedPipeline && _acquireCalled && !_releaseCalled) { try { - OnReleaseState(app, null); + OnReleaseState(context.ApplicationInstance, null); } catch { } } } + + public Task ReleaseSessionStateAsync(HttpContext context) { + ReleaseSessionState(context); + return TaskAsyncHelper.CompletedTask; + } } } diff --git a/System.Web/State/SessionStateUtil.cs b/System.Web/State/SessionStateUtil.cs index 8116fbc27..19c6d7393 100644 --- a/System.Web/State/SessionStateUtil.cs +++ b/System.Web/State/SessionStateUtil.cs @@ -73,6 +73,24 @@ static public HttpStaticObjectsCollection GetSessionStaticObjects(HttpContext co return context.Application.SessionStaticObjects.Clone(); } + /// + /// Gets a value that indicates whether session state is required by the context. + /// + /// The HttpContext. + /// A value that indicates whether session state is required by the context. + static public bool IsSessionStateRequired(HttpContext context) { + return context.RequiresSessionState; + } + + /// + /// Gets a value that indicates whether session state is read-only in the context. + /// + /// The HttpContext. + /// A value that indicates whether session state is read-only in the context. + static public bool IsSessionStateReadOnly(HttpContext context) { + return context.ReadOnlySessionState; + } + internal static SessionStateStoreData CreateLegitStoreData(HttpContext context, ISessionStateItemCollection sessionItems, HttpStaticObjectsCollection staticObjects, diff --git a/System.Web/TaskAsyncHelper.cs b/System.Web/TaskAsyncHelper.cs index 2558f75a0..85e2bef8a 100644 --- a/System.Web/TaskAsyncHelper.cs +++ b/System.Web/TaskAsyncHelper.cs @@ -16,6 +16,8 @@ namespace System.Web { internal static class TaskAsyncHelper { + private static readonly Task s_completedTask = Task.FromResult(null); + internal static IAsyncResult BeginTask(Func taskFunc, AsyncCallback callback, object state) { Task task = taskFunc(); if (task == null) { @@ -76,5 +78,10 @@ internal static void EndTask(IAsyncResult ar) { taskWrapper.Task.GetAwaiter().GetResult(); } + internal static Task CompletedTask { + get { + return s_completedTask; + } + } } } diff --git a/System.Web/UI/PartialCachingControl.cs b/System.Web/UI/PartialCachingControl.cs index 9efc13a58..e70f02a26 100644 --- a/System.Web/UI/PartialCachingControl.cs +++ b/System.Web/UI/PartialCachingControl.cs @@ -6,27 +6,27 @@ namespace System.Web.UI { -using System; -using System.IO; -using System.Text; -using System.Collections; -using System.Collections.Specialized; -using System.ComponentModel; -using System.ComponentModel.Design; -using System.Globalization; -using System.Web; -using System.Web.Util; -using System.Web.UI.HtmlControls; -using System.Web.UI.WebControls; -using System.Web.Caching; -using System.Web.Compilation; -using System.Web.Configuration; -using System.Security.Permissions; - - -// Keeps track of one call to Page Register* API -// The semantics of the fields depends to the call type -[Serializable] + using System; + using System.IO; + using System.Text; + using System.Collections; + using System.Collections.Specialized; + using System.ComponentModel; + using System.ComponentModel.Design; + using System.Globalization; + using System.Web; + using System.Web.Util; + using System.Web.UI.HtmlControls; + using System.Web.UI.WebControls; + using System.Web.Caching; + using System.Web.Compilation; + using System.Web.Configuration; + using System.Security.Permissions; + + + // Keeps track of one call to Page Register* API + // The semantics of the fields depends to the call type + [Serializable] internal class RegisterCallData { internal ClientAPIRegisterType Type; internal ScriptKey Key; @@ -406,8 +406,8 @@ protected internal override void Render(HtmlTextWriter output) { } else { string[] varyByParams = null; - if (_varyByParamsCollection != null) - varyByParams = _varyByParamsCollection.GetParams(); + if (_varyByParamsCollection != null) + varyByParams = _varyByParamsCollection.GetParams(); cachedVary = new ControlCachedVary(varyByParams, _varyByControlsCollection, _varyByCustom); @@ -576,7 +576,7 @@ internal void SetVaryByParamsCollectionFromString(string varyByParams) { string[] varyByParamsStrings = varyByParams.Split(varySeparator); _varyByParamsCollection = new HttpCacheVaryByParams(); - _varyByParamsCollection.ResetFromParams(varyByParamsStrings); + _varyByParamsCollection.SetParams(varyByParamsStrings); } internal void RegisterPostBackScript() { diff --git a/System.Web/UI/WebControls/Calendar.cs b/System.Web/UI/WebControls/Calendar.cs index ce3d85949..ea3c1e08b 100644 --- a/System.Web/UI/WebControls/Calendar.cs +++ b/System.Web/UI/WebControls/Calendar.cs @@ -56,7 +56,7 @@ public class Calendar : WebControl, IPostBackEventHandler { private ArrayList dateList; private SelectedDatesCollection selectedDates; - private Globalization.Calendar threadCalendar; + private System.Globalization.Calendar threadCalendar; private DateTime minSupportedDate; private DateTime maxSupportedDate; #if DEBUG diff --git a/System.Web/UI/WebControls/QueryExtensions.cs b/System.Web/UI/WebControls/QueryExtensions.cs index ac1bbae57..3657d75cc 100644 --- a/System.Web/UI/WebControls/QueryExtensions.cs +++ b/System.Web/UI/WebControls/QueryExtensions.cs @@ -32,7 +32,17 @@ public static IQueryable SortBy(this IQueryable source, string sortExpr } ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty); - MemberExpression property = Expression.Property(parameter, sortExpression); + //VSO bug 173528-- Add support for sorting by nested property names + MemberExpression property = null; + string[] sortExpressionFields = sortExpression.Split('.'); + foreach (string sortExpressionField in sortExpressionFields) { + if (property == null) { + property = Expression.Property(parameter, sortExpressionField); + } + else { + property = Expression.Property(property, sortExpressionField); + } + } LambdaExpression lambda = Expression.Lambda(property, parameter); string methodName = (isDescending) ? "OrderByDescending" : "OrderBy" ; diff --git a/System.Web/UI/WebControls/xml.cs b/System.Web/UI/WebControls/xml.cs index bb97a7c28..93ec3e60d 100644 --- a/System.Web/UI/WebControls/xml.cs +++ b/System.Web/UI/WebControls/xml.cs @@ -41,6 +41,7 @@ public override Type GetChildControlType(string tagName, IDictionary attribs) { [SuppressMessage("Microsoft.Security", "MSEC1220:ReviewDtdProcessingAssignment", Justification = "Dtd processing is needed for back-compat, but is being done as safely as possible.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3069:ReviewDtdProcessingAssignment", Justification = "Dtd processing is needed for back-compat, but is being done as safely as possible.")] public override void SetTagInnerText(string text) { if (!Util.IsWhiteSpaceString(text)) { @@ -114,7 +115,9 @@ public class Xml : Control { #pragma warning restore 0618 [SuppressMessage("Microsoft.Security", "MSEC1201:DoNotUseXslTransform", Justification = "_identityTransform contents are trusted hard-coded string.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3050:DoNotUseXslTransform", Justification = "_identityTransform contents are trusted hard-coded string.")] [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "_identityTransform contents are trusted hard-coded string.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "_identityTransform contents are trusted hard-coded string.")] [PermissionSet(SecurityAction.Assert, Unrestricted = true)] static Xml() { @@ -332,6 +335,7 @@ public XPathNavigator XPathNavigator { [SuppressMessage("Microsoft.Security", "MSEC1218:ReviewWebControlForSet_DocumentContent", Justification = "Legacy code that trusts our developer input. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3067:ReviewWebControlForSet_DocumentContent", Justification = "Legacy code that trusts our developer input. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] protected override void AddParsedSubObject(object obj) { if (obj is LiteralControl) { // Trim the initial whitespaces since XML is very picky (related to ASURT 58100) diff --git a/System.Web/Util/GCUtil.cs b/System.Web/Util/GCUtil.cs index 0e48e6d16..f6c84a217 100644 --- a/System.Web/Util/GCUtil.cs +++ b/System.Web/Util/GCUtil.cs @@ -36,4 +36,33 @@ public static object UnrootObject(IntPtr pointer) { } } + + // This wrapper around a managed object is opaque to SizedReference GC handle + // and therefore helps with calculating size of only relevant graph of objects + internal class DisposableGCHandleRef : IDisposable + where T : class, IDisposable { + GCHandle _handle; + [PermissionSet(SecurityAction.Assert, Unrestricted = true)] + public DisposableGCHandleRef(T t) { + Debug.Assert(t != null); + _handle = GCHandle.Alloc(t); + } + + public T Target { + [PermissionSet(SecurityAction.Assert, Unrestricted = true)] + get { + Debug.Assert(_handle.IsAllocated); + return (T)_handle.Target; + } + } + + [PermissionSet(SecurityAction.Assert, Unrestricted = true)] + public void Dispose() { + Target.Dispose(); + Debug.Assert(_handle.IsAllocated); + if (_handle.IsAllocated) { + _handle.Free(); + } + } + } } diff --git a/System.Web/Util/StringUtil.cs b/System.Web/Util/StringUtil.cs index 5e580ff76..df59b0cf0 100644 --- a/System.Web/Util/StringUtil.cs +++ b/System.Web/Util/StringUtil.cs @@ -15,6 +15,7 @@ namespace System.Web.Util { using System.Globalization; using System.Runtime.InteropServices; using System.Web.Hosting; +using System.Diagnostics.CodeAnalysis; /* * Various string handling utilities @@ -294,6 +295,7 @@ internal static int GetNonRandomizedHashCode(string s, bool ignoreCase = false) // Instead use the default AppDomain, because it doesn't have string hash randomization enabled. // Marshal the call to reuse the default StringComparer behavior. // PERF isn't optimal, so apply consideration! + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "We carefully control the callers.")] internal static int GetNonRandomizedStringComparerHashCode(string s) { // Preserve the default behavior when string hash randomization is off if (!AppSettings.UseRandomizedStringHashAlgorithm) { diff --git a/System.Web/Util/SynchronizationHelper.cs b/System.Web/Util/SynchronizationHelper.cs index d442f0cc8..64a86b681 100644 --- a/System.Web/Util/SynchronizationHelper.cs +++ b/System.Web/Util/SynchronizationHelper.cs @@ -105,7 +105,7 @@ public void QueueAsynchronous(Action action) { // This method only schedules work; it doesn't itself do any work. The lock is held for a very // short period of time. lock (_lockObj) { - Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action)); + Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action), TaskScheduler.Default); _lastScheduledTask = newTask; // the newly-created task is now the last one } } diff --git a/System.Web/Util/XmlUtils.cs b/System.Web/Util/XmlUtils.cs index eb237040d..a07e9df58 100644 --- a/System.Web/Util/XmlUtils.cs +++ b/System.Web/Util/XmlUtils.cs @@ -17,6 +17,7 @@ internal static class XmlUtils public static readonly long MaxEntityExpansion = 1024 * 1024; [SuppressMessage("Microsoft.Security", "MSEC1208:DoNotUseLoadXml", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3057:DoNotUseLoadXml", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] public static XmlDocument CreateXmlDocumentFromContent(string content) { XmlDocument doc = new XmlDocument(); @@ -35,6 +36,7 @@ public static XmlDocument CreateXmlDocumentFromContent(string content) } [SuppressMessage("Microsoft.Security", "MSEC1210:UseXmlReaderForXPathDocument", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3059:UseXmlReaderForXPathDocument", Justification = "Handles developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] public static XPathDocument CreateXPathDocumentFromContent(string content) { StringReader reader = new StringReader(content); @@ -47,6 +49,7 @@ public static XPathDocument CreateXPathDocumentFromContent(string content) } [SuppressMessage("Microsoft.Security", "MSEC1220:ReviewDtdProcessingAssignment", Justification = "Dtd processing is needed for back-compat, but is being done as safely as possible.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3069:ReviewDtdProcessingAssignment", Justification = "Dtd processing is needed for back-compat, but is being done as safely as possible.")] public static XmlReaderSettings CreateXmlReaderSettings() { XmlReaderSettings settings = new XmlReaderSettings(); @@ -68,7 +71,9 @@ public static XmlReaderSettings CreateXmlReaderSettings() // try to guess at how to set matching defaults with XmlReader.Create(). // (E.g. DtdProcessing is Parse by default using XmlTextReader directly. It's Prohibit in default XmlReaderSettings.) [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3074:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")] public static XmlReader CreateXmlReader(string filepath) { if (AppSettings.RestrictXmlControls) @@ -84,7 +89,9 @@ public static XmlReader CreateXmlReader(string filepath) } [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3074:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")] public static XmlReader CreateXmlReader(Stream datastream) { if (AppSettings.RestrictXmlControls) @@ -100,7 +107,9 @@ public static XmlReader CreateXmlReader(Stream datastream) } [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3074:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")] public static XmlReader CreateXmlReader(TextReader reader) { if (AppSettings.RestrictXmlControls) @@ -116,7 +125,9 @@ public static XmlReader CreateXmlReader(TextReader reader) } [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Handles trusted or developer-controlled input xml. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] [SuppressMessage("Microsoft.Security", "MSEC1225:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3074:ReviewClassesDerivedFromXmlTextReader", Justification = "NoEntitiesXmlReader is our internal mechanism for using XmlTextReaders in a reasonably safe manner.")] public static XmlReader CreateXmlReader(Stream contentStream, string baseURI) { if (AppSettings.RestrictXmlControls) @@ -179,6 +190,7 @@ public static XslCompiledTransform CreateXslCompiledTransform(XmlReader xmlReade #pragma warning disable 0618 // To avoid deprecation warning [SuppressMessage("Microsoft.Security", "MSEC1201:DoNotUseXslTransform", Justification = "Handles developer-controlled input xsl. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3050:DoNotUseXslTransform", Justification = "Handles developer-controlled input xsl. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] public static XslTransform CreateXslTransform(XmlReader reader) { if (!AppSettings.RestrictXmlControls) @@ -191,6 +203,7 @@ public static XslTransform CreateXslTransform(XmlReader reader) } [SuppressMessage("Microsoft.Security", "MSEC1201:DoNotUseXslTransform", Justification = "Handles developer-controlled input xsl. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3050:DoNotUseXslTransform", Justification = "Handles developer-controlled input xsl. Optional safer codepath available via appSettings/aspnet:RestrictXmlControls configuration.")] public static XslTransform CreateXslTransform(XmlReader reader, XmlResolver resolver) { if (!AppSettings.RestrictXmlControls) diff --git a/System.Web/XmlSiteMapProvider.cs b/System.Web/XmlSiteMapProvider.cs index 736c8f48b..917aeea91 100644 --- a/System.Web/XmlSiteMapProvider.cs +++ b/System.Web/XmlSiteMapProvider.cs @@ -232,6 +232,7 @@ protected virtual void AddProvider(string providerName, SiteMapNode parentNode) [SuppressMessage("Microsoft.Security", "MSEC1205:DoNotAllowDtdOnXmlTextReader", Justification = "Legacy code that trusts our developer-controlled input.")] + [SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader", Justification = "Legacy code that trusts our developer-controlled input.")] public override SiteMapNode BuildSiteMap() { SiteMapNode tempNode = _siteMapNode; diff --git a/System.Workflow.ComponentModel/AuthoringOM/Compiler/CompileXomlTask.cs b/System.Workflow.ComponentModel/AuthoringOM/Compiler/CompileXomlTask.cs index 5290f57da..d1a7668ea 100644 --- a/System.Workflow.ComponentModel/AuthoringOM/Compiler/CompileXomlTask.cs +++ b/System.Workflow.ComponentModel/AuthoringOM/Compiler/CompileXomlTask.cs @@ -6,6 +6,7 @@ namespace System.Workflow.ComponentModel.Compiler using System.Diagnostics; using System.Globalization; using System.IO; + using System.Xml; using System.Text; using System.Collections; using System.Collections.Specialized; @@ -1142,7 +1143,7 @@ internal static string GetXomlManifestName(string fileName, string linkFileName, string name = null; try { - Xml.XmlTextReader reader = new Xml.XmlTextReader(binaryStream); + Xml.XmlTextReader reader = new Xml.XmlTextReader(binaryStream) { DtdProcessing = DtdProcessing.Prohibit }; if (reader.MoveToContent() == System.Xml.XmlNodeType.Element) { if (reader.MoveToAttribute("Class", StandardXomlKeys.Definitions_XmlNs)) diff --git a/System.Workflow.ComponentModel/AuthoringOM/Design/ComponentSerializationService.cs b/System.Workflow.ComponentModel/AuthoringOM/Design/ComponentSerializationService.cs index 1e658c689..5488a93ae 100644 --- a/System.Workflow.ComponentModel/AuthoringOM/Design/ComponentSerializationService.cs +++ b/System.Workflow.ComponentModel/AuthoringOM/Design/ComponentSerializationService.cs @@ -193,7 +193,7 @@ internal IList Deserialize(IServiceProvider serviceProvider) { ArrayList objects = new ArrayList(); WorkflowMarkupSerializationManager xomlSerializationManager = new WorkflowMarkupSerializationManager(serializationManager); - XmlTextReader reader = new XmlTextReader(this.serializedXmlString, XmlNodeType.Element, null); + XmlTextReader reader = new XmlTextReader(this.serializedXmlString, XmlNodeType.Element, null) { DtdProcessing = DtdProcessing.Prohibit }; reader.MoveToElement(); do { @@ -235,7 +235,7 @@ internal void DeserializeTo(IServiceProvider serviceProvider, IContainer contain xomlSerializationManager.AddSerializationProvider(propertySegmentSerializationProvider); StringReader stringReader = new StringReader(this.serializedXmlString); - using (XmlTextReader reader = new XmlTextReader(stringReader)) + using (XmlTextReader reader = new XmlTextReader(stringReader) { DtdProcessing = DtdProcessing.Prohibit }) { while (reader.NodeType != XmlNodeType.Element && reader.NodeType != XmlNodeType.ProcessingInstruction && reader.Read()); diff --git a/System.Workflow.Runtime/Tracking.cs b/System.Workflow.Runtime/Tracking.cs index 55ef70d71..df277deae 100644 --- a/System.Workflow.Runtime/Tracking.cs +++ b/System.Workflow.Runtime/Tracking.cs @@ -1837,18 +1837,25 @@ internal static Guid HashServiceType(Type serviceType) return HashServiceType(serviceType.AssemblyQualifiedName); } - [SuppressMessage("Microsoft.Cryptographic.Standard", "CA5350:MD5CannotBeUsed", + [SuppressMessage("Microsoft.Cryptographic.Standard", "CA5350:MD5CannotBeUsed", Justification = "Design has been approved. We are not using MD5 for any security or cryptography purposes but rather as a hash.")] internal static Guid HashServiceType(String serviceFullTypeName) { - MD5 md5 = new MD5CryptoServiceProvider(); byte[] data; byte[] result; UnicodeEncoding ue = new UnicodeEncoding(); data = ue.GetBytes(serviceFullTypeName); - result = md5.ComputeHash(data); + if (AppSettings.FIPSRequired) + { + result = MD5PInvokeHelper.CalculateHash(data); + } + else + { + MD5 md5 = new MD5CryptoServiceProvider(); + result = md5.ComputeHash(data); + } return new Guid(result); } diff --git a/System.Workflow.Runtime/Tracking/TrackingProfileSerializer.cs b/System.Workflow.Runtime/Tracking/TrackingProfileSerializer.cs index 79d95e345..7c6905f92 100644 --- a/System.Workflow.Runtime/Tracking/TrackingProfileSerializer.cs +++ b/System.Workflow.Runtime/Tracking/TrackingProfileSerializer.cs @@ -28,7 +28,8 @@ public class TrackingProfileSerializer { public TrackingProfileSerializer() { - _schema = XmlSchema.Read(new StringReader(_xsd), null); + StringReader reader = new StringReader(_xsd); + _schema = XmlSchema.Read(new XmlTextReader(reader) { DtdProcessing = DtdProcessing.Prohibit }, null); _schema.Namespaces.Add("", _ns); } diff --git a/System.Workflow.Runtime/WorkflowRuntime.cs b/System.Workflow.Runtime/WorkflowRuntime.cs index 718b7556e..1697c0856 100644 --- a/System.Workflow.Runtime/WorkflowRuntime.cs +++ b/System.Workflow.Runtime/WorkflowRuntime.cs @@ -129,6 +129,17 @@ static WorkflowRuntime() // listen to activity definition resolve events Activity.ActivityResolve += OnActivityDefinitionResolve; Activity.WorkflowChangeActionsResolve += OnWorkflowChangeActionsResolve; + + try + { + using (TelemetryEventSource eventSource = new TelemetryEventSource()) + { + eventSource.V1Runtime(); + } + } + catch + { + } } public WorkflowRuntime() diff --git a/System.Xml/InternalApis/Clr/inc/AppContextDefaultValues.cs b/System.Xml/InternalApis/Clr/inc/AppContextDefaultValues.cs index 8a130a0f3..9224bf8b8 100644 --- a/System.Xml/InternalApis/Clr/inc/AppContextDefaultValues.cs +++ b/System.Xml/InternalApis/Clr/inc/AppContextDefaultValues.cs @@ -3,6 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== + +// There are cases where we have multiple assemblies that are going to import this file and +// if they are going to also have InternalsVisibleTo between them, there will be a compiler warning +// that the type is found both in the source and in a referenced assembly. The compiler will prefer +// the version of the type defined in the source +// +// In order to disable the warning for this type we are disabling this warning for this entire file. +#pragma warning disable 436 + using System; using System.Collections.Generic; @@ -167,3 +176,5 @@ private static bool TryParseFrameworkName(String frameworkName, out String ident static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version); } } + +#pragma warning restore 436 diff --git a/System.Xml/InternalApis/Clr/inc/LocalAppContext.cs b/System.Xml/InternalApis/Clr/inc/LocalAppContext.cs index f05b599ed..33662b542 100644 --- a/System.Xml/InternalApis/Clr/inc/LocalAppContext.cs +++ b/System.Xml/InternalApis/Clr/inc/LocalAppContext.cs @@ -4,6 +4,14 @@ // // ==--== +// There are cases where we have multiple assemblies that are going to import this file and +// if they are going to also have InternalsVisibleTo between them, there will be a compiler warning +// that the type is found both in the source and in a referenced assembly. The compiler will prefer +// the version of the type defined in the source +// +// In order to disable the warning for this type we are disabling this warning for this entire file. +#pragma warning disable 436 + // NOTE: This file should not be included in mscorlib. This should only be included in FX libraries that need to provide switches using System; using System.Collections.Generic; @@ -126,3 +134,5 @@ internal static void DefineSwitchDefault(string switchName, bool initialValue) } } } + +#pragma warning restore 436 diff --git a/System.Xml/System/Xml/Core/AppContextDefaultValues.Defaults.cs b/System.Xml/System/Xml/Core/AppContextDefaultValues.Defaults.cs index 01ac463e4..ac29b8938 100644 --- a/System.Xml/System/Xml/Core/AppContextDefaultValues.Defaults.cs +++ b/System.Xml/System/Xml/Core/AppContextDefaultValues.Defaults.cs @@ -28,6 +28,10 @@ static partial void PopulateDefaultValuesPartial(string platformIdentifier, stri LocalAppContext.DefineSwitchDefault("Switch.System.Xml.DontThrowOnInvalidSurrogatePairs", true); LocalAppContext.DefineSwitchDefault("Switch.System.Xml.IgnoreEmptyKeySequences", true); } + if (version <= 40601) + { + LocalAppContext.DefineSwitchDefault("Switch.System.Xml.IgnoreKindInUtcTimeSerialization", true); + } break; } case "WindowsPhone": @@ -37,6 +41,7 @@ static partial void PopulateDefaultValuesPartial(string platformIdentifier, stri { LocalAppContext.DefineSwitchDefault("Switch.System.Xml.DontThrowOnInvalidSurrogatePairs", true); LocalAppContext.DefineSwitchDefault("Switch.System.Xml.IgnoreEmptyKeySequences", true); + LocalAppContext.DefineSwitchDefault("Switch.System.Xml.IgnoreKindInUtcTimeSerialization", true); } break; } diff --git a/System.Xml/System/Xml/Core/LocalAppContextSwitches.cs b/System.Xml/System/Xml/Core/LocalAppContextSwitches.cs index c744ba1c0..ddc332341 100644 --- a/System.Xml/System/Xml/Core/LocalAppContextSwitches.cs +++ b/System.Xml/System/Xml/Core/LocalAppContextSwitches.cs @@ -29,5 +29,15 @@ public static bool IgnoreEmptyKeySequences return LocalAppContext.GetCachedSwitchValue(@"Switch.System.Xml.IgnoreEmptyKeySequences", ref _ignoreEmptyKeySequences); } } + + private static int _ignoreKindInUtcTimeSerialization; + public static bool IgnoreKindInUtcTimeSerialization + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return LocalAppContext.GetCachedSwitchValue(@"Switch.System.Xml.IgnoreKindInUtcTimeSerialization", ref _ignoreKindInUtcTimeSerialization); + } + } } } diff --git a/System.Xml/System/Xml/Serialization/Xmlcustomformatter.cs b/System.Xml/System/Xml/Serialization/Xmlcustomformatter.cs index 81244cfe3..1b814edf1 100644 --- a/System.Xml/System/Xml/Serialization/Xmlcustomformatter.cs +++ b/System.Xml/System/Xml/Serialization/Xmlcustomformatter.cs @@ -75,7 +75,14 @@ internal static string FromDate(DateTime value) { } internal static string FromTime(DateTime value) { - return XmlConvert.ToString(DateTime.MinValue + value.TimeOfDay, "HH:mm:ss.fffffffzzzzzz"); + if (!LocalAppContextSwitches.IgnoreKindInUtcTimeSerialization && value.Kind == DateTimeKind.Utc) + { + return XmlConvert.ToString(DateTime.MinValue + value.TimeOfDay, "HH:mm:ss.fffffffZ"); + } + else + { + return XmlConvert.ToString(DateTime.MinValue + value.TimeOfDay, "HH:mm:ss.fffffffzzzzzz"); + } } internal static string FromDateTime(DateTime value) { @@ -332,7 +339,14 @@ internal static DateTime ToDate(string value) { } internal static DateTime ToTime(string value) { - return DateTime.ParseExact(value, allTimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowLeadingWhite|DateTimeStyles.AllowTrailingWhite|DateTimeStyles.NoCurrentDateDefault); + if (!LocalAppContextSwitches.IgnoreKindInUtcTimeSerialization) + { + return DateTime.ParseExact(value, allTimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite | DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.RoundtripKind); + } + else + { + return DateTime.ParseExact(value, allTimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite | DateTimeStyles.NoCurrentDateDefault); + } } internal static char ToChar(string value) { diff --git a/System/InternalApis/Clr/inc/AppContextDefaultValues.cs b/System/InternalApis/Clr/inc/AppContextDefaultValues.cs index 8a130a0f3..9224bf8b8 100644 --- a/System/InternalApis/Clr/inc/AppContextDefaultValues.cs +++ b/System/InternalApis/Clr/inc/AppContextDefaultValues.cs @@ -3,6 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== + +// There are cases where we have multiple assemblies that are going to import this file and +// if they are going to also have InternalsVisibleTo between them, there will be a compiler warning +// that the type is found both in the source and in a referenced assembly. The compiler will prefer +// the version of the type defined in the source +// +// In order to disable the warning for this type we are disabling this warning for this entire file. +#pragma warning disable 436 + using System; using System.Collections.Generic; @@ -167,3 +176,5 @@ private static bool TryParseFrameworkName(String frameworkName, out String ident static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version); } } + +#pragma warning restore 436 diff --git a/System/InternalApis/Clr/inc/LocalAppContext.cs b/System/InternalApis/Clr/inc/LocalAppContext.cs index f05b599ed..33662b542 100644 --- a/System/InternalApis/Clr/inc/LocalAppContext.cs +++ b/System/InternalApis/Clr/inc/LocalAppContext.cs @@ -4,6 +4,14 @@ // // ==--== +// There are cases where we have multiple assemblies that are going to import this file and +// if they are going to also have InternalsVisibleTo between them, there will be a compiler warning +// that the type is found both in the source and in a referenced assembly. The compiler will prefer +// the version of the type defined in the source +// +// In order to disable the warning for this type we are disabling this warning for this entire file. +#pragma warning disable 436 + // NOTE: This file should not be included in mscorlib. This should only be included in FX libraries that need to provide switches using System; using System.Collections.Generic; @@ -126,3 +134,5 @@ internal static void DefineSwitchDefault(string switchName, bool initialValue) } } } + +#pragma warning restore 436 diff --git a/System/InternalApis/NDP_Common/inc/PinnableBufferCache.cs b/System/InternalApis/NDP_Common/inc/PinnableBufferCache.cs index 872cfa80d..19ec1053a 100644 --- a/System/InternalApis/NDP_Common/inc/PinnableBufferCache.cs +++ b/System/InternalApis/NDP_Common/inc/PinnableBufferCache.cs @@ -155,7 +155,14 @@ internal void Free(object buffer) if (PinnableBufferCacheEventSource.Log.IsEnabled()) PinnableBufferCacheEventSource.Log.FreeBuffer(m_CacheName, PinnableBufferCacheEventSource.AddressOf(buffer), buffer.GetHashCode(), m_FreeList.Count); - + if(buffer == null) + { + if (PinnableBufferCacheEventSource.Log.IsEnabled()) + PinnableBufferCacheEventSource.Log.FreeBufferNull(m_CacheName, m_FreeList.Count); + + return; + } + // After we've done 3 gen1 GCs, assume that all buffers have aged into gen2 on the free path. if ((m_gen1CountAtLastRestock + 3) > GC.CollectionCount(GC.MaxGeneration - 1)) { @@ -567,6 +574,7 @@ public void AllocateBufferCreatingNewBuffers(string cacheName, int totalBuffsBef public void AllocateBufferAged(string cacheName, int agedCount) {} public void AllocateBufferFreeListEmpty(string cacheName, int notGen2CountBefore) {} public void FreeBuffer(string cacheName, ulong objectId, int objectHash, int freeCountBefore) {} + public void FreeBufferNull(string cacheName, int freeCountBefore) { } public void FreeBufferStillTooYoung(string cacheName, int notGen2CountBefore) {} public void TrimCheck(string cacheName, int totalBuffs, bool neededMoreThanFreeList, int deltaMSec) {} public void TrimFree(string cacheName, int totalBuffs, int freeListCount, int toBeFreed) {} @@ -643,6 +651,8 @@ internal sealed class PinnableBufferCacheEventSource : EventSource public void AgePendingBuffersResults(string cacheName, int promotedToFreeListCount, int heldBackCount) { if (IsEnabled()) WriteEvent(20, cacheName, promotedToFreeListCount, heldBackCount); } [Event(21)] public void WalkFreeListResult(string cacheName, int freeListCount, int gen0BuffersInFreeList) { if (IsEnabled()) WriteEvent(21, cacheName, freeListCount, gen0BuffersInFreeList); } + [Event(22)] + public void FreeBufferNull(string cacheName, int freeCountBefore) { if(IsEnabled()) WriteEvent(22, cacheName, freeCountBefore); } static internal ulong AddressOf(object obj) diff --git a/System/compmod/microsoft/win32/UnsafeNativeMethods.cs b/System/compmod/microsoft/win32/UnsafeNativeMethods.cs index a1911293b..958740229 100644 --- a/System/compmod/microsoft/win32/UnsafeNativeMethods.cs +++ b/System/compmod/microsoft/win32/UnsafeNativeMethods.cs @@ -169,6 +169,23 @@ public static extern IntPtr CreateWindowEx(int exStyle, string lpszClassName, st [ResourceExposure(ResourceScope.None)] public static extern bool WTSUnRegisterSessionNotification(HandleRef hWnd); + private static IntPtr GetCurrentProcessToken() { return new IntPtr(-4); } + + private const int ERROR_SUCCESS = 0; + + enum AppPolicyClrCompat + { + AppPolicyClrCompat_Others = 0, + AppPolicyClrCompat_ClassicDesktop = 1, + AppPolicyClrCompat_Universal = 2, + AppPolicyClrCompat_PackagedDesktop = 3 + }; + + [DllImport(ExternDll.Kernel32, CharSet = CharSet.None, EntryPoint = "AppPolicyGetClrCompat")] + [System.Security.SecuritySafeCritical] + [return: MarshalAs(UnmanagedType.I4)] + private static extern Int32 _AppPolicyGetClrCompat(IntPtr processToken, out AppPolicyClrCompat appPolicyClrCompat); + private const int ERROR_INSUFFICIENT_BUFFER = 0x007A; private const int ERROR_NO_PACKAGE_IDENTITY = 0x3d54; @@ -204,8 +221,20 @@ private static bool DoesWin32MethodExist(String moduleName, String methodName) [System.Security.SecuritySafeCritical] private static bool _IsPackagedProcess() { + Version windows8Version = new Version(6, 2, 0, 0); OperatingSystem os = Environment.OSVersion; - if(os.Platform == PlatformID.Win32NT && os.Version >= new Version(6,2,0,0) && DoesWin32MethodExist(ExternDll.Kernel32, "GetCurrentPackageId")) + bool osSupportsPackagedProcesses = os.Platform == PlatformID.Win32NT && os.Version >= windows8Version; + + if (osSupportsPackagedProcesses && DoesWin32MethodExist(ExternDll.Kernel32, "AppPolicyGetClrCompat")) + { + // Use AppPolicyGetClrCompat if it is available. Return true if and only if this is a UWA which means if + // this is packaged desktop app this method will return false. This may cause some confusion however + // this is necessary to make the behavior of packaged desktop apps identical to desktop apps. + AppPolicyClrCompat appPolicyClrCompat; + return _AppPolicyGetClrCompat(GetCurrentProcessToken(), out appPolicyClrCompat) == ERROR_SUCCESS && + appPolicyClrCompat == AppPolicyClrCompat.AppPolicyClrCompat_Universal; + } + else if(osSupportsPackagedProcesses && DoesWin32MethodExist(ExternDll.Kernel32, "GetCurrentPackageId")) { Int32 bufLen = 0; // Will return ERROR_INSUFFICIENT_BUFFER when running within a packaged application, diff --git a/System/compmod/system/componentmodel/MemberDescriptor.cs b/System/compmod/system/componentmodel/MemberDescriptor.cs index c16486800..ebc36a83d 100644 --- a/System/compmod/system/componentmodel/MemberDescriptor.cs +++ b/System/compmod/system/componentmodel/MemberDescriptor.cs @@ -326,9 +326,19 @@ public override bool Equals(object obj) { return false; } - if ((mdObj.description == null) != (description == null) || - (description != null && !mdObj.category.Equals(description))) { - return false; + // VSO 149471 - Technically fixing this could cause a behavior change, so we are + // adding a quirk in case anyone is bit by this and needs the old, buggy behavior. + if (!LocalAppContextSwitches.MemberDescriptorEqualsReturnsFalseIfEquivalent) { + if ((mdObj.description == null) != (description == null) || + (description != null && !mdObj.description.Equals(description))) { + return false; + } + } + else { + if ((mdObj.description == null) != (description == null) || + (description != null && !mdObj.category.Equals(description))) { + return false; + } } if ((mdObj.attributes == null) != (attributes == null)) { diff --git a/System/net/System/Net/HttpListenerRequest.cs b/System/net/System/Net/HttpListenerRequest.cs index 320e1d69f..a789a6ea2 100644 --- a/System/net/System/Net/HttpListenerRequest.cs +++ b/System/net/System/Net/HttpListenerRequest.cs @@ -300,6 +300,9 @@ internal HttpListenerContext HttpListenerContext { } } + // Note: RequestBuffer may get moved in memory. If you dereference a pointer from inside the RequestBuffer, + // you must use 'OriginalBlobAddress' below to adjust the location of the pointer to match the location of + // RequestBuffer. internal byte[] RequestBuffer { get @@ -1061,7 +1064,7 @@ private void ProcessTlsTokenBindings() { m_TokenBindings = new List(); - UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO* pTokenBindingInfo = GetTlsTokenBindingRequestInfo(); + UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO* pTokenBindingInfo = UnsafeNclNativeMethods.HttpApi.GetTlsTokenBindingRequestInfo(RequestBuffer, OriginalBlobAddress); if (pTokenBindingInfo == null) { @@ -1116,23 +1119,6 @@ private void ProcessTlsTokenBindings() { } } - private UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO* GetTlsTokenBindingRequestInfo() { - fixed (byte* pMemoryBlob = RequestBuffer) - { - UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2* request = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)pMemoryBlob; - - for (int i = 0; i < request->RequestInfoCount; i++) - { - UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO* pThisInfo = &request->pRequestInfo[i]; - if (pThisInfo != null && pThisInfo->InfoType == UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslTokenBinding) - { - return (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO*)pThisInfo->pInfo; - } - } - } - return null; - } - internal void CheckDisposed() { if (m_IsDisposed) { throw new ObjectDisposedException(this.GetType().FullName); diff --git a/System/net/System/Net/Internal.cs b/System/net/System/Net/Internal.cs index cc9012469..0a0b503ff 100644 --- a/System/net/System/Net/Internal.cs +++ b/System/net/System/Net/Internal.cs @@ -1383,6 +1383,7 @@ public enum Flags { ValidateManual = 0x08, NoDefaultCred = 0x10, ValidateAuto = 0x20, + SendAuxRecord = 0x00200000, UseStrongCrypto = 0x00400000, } diff --git a/System/net/System/Net/SecureProtocols/_SslState.cs b/System/net/System/Net/SecureProtocols/_SslState.cs index 257492c82..2f9355f69 100644 --- a/System/net/System/Net/SecureProtocols/_SslState.cs +++ b/System/net/System/Net/SecureProtocols/_SslState.cs @@ -575,9 +575,17 @@ internal void ProcessAuthentication(LazyAsyncResult lazyResult) KeyExchangeStrength)); } } + catch (Exception) + { + // If an exception emerges synchronously, the asynchronous operation was not + // initiated, so no operation is in progress. + _NestedAuth = 0; + + throw; + } finally { - if (lazyResult == null || _Exception != null) + if (lazyResult == null) { _NestedAuth = 0; } diff --git a/System/net/System/Net/ServicePointManager.cs b/System/net/System/Net/ServicePointManager.cs index 3f70019de..d1ea370d2 100644 --- a/System/net/System/Net/ServicePointManager.cs +++ b/System/net/System/Net/ServicePointManager.cs @@ -245,12 +245,15 @@ public class ServicePointManager { private static volatile CertPolicyValidationCallback s_CertPolicyValidationCallback = new CertPolicyValidationCallback(); private static volatile ServerCertValidationCallback s_ServerCertValidationCallback = null; + private const string sendAuxRecordValueName = "SchSendAuxRecord"; + private const string sendAuxRecordAppSetting = "System.Net.ServicePointManager.SchSendAuxRecord"; private const string strongCryptoValueName = "SchUseStrongCrypto"; private const string secureProtocolAppSetting = "System.Net.ServicePointManager.SecurityProtocol"; - private static object disableStrongCryptoLock = new object(); - private static volatile bool disableStrongCryptoInitialized = false; + private static object configurationLoadedLock = new object(); + private static volatile bool configurationLoaded = false; private static bool disableStrongCrypto = false; + private static bool disableSendAuxRecord = false; private static SecurityProtocolType s_SecurityProtocolType = SecurityProtocolType.Tls | SecurityProtocolType.Ssl3; @@ -425,11 +428,11 @@ private ServicePointManager() { [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety")] public static SecurityProtocolType SecurityProtocol { get { - EnsureStrongCryptoSettingsInitialized(); + EnsureConfigurationLoaded(); return s_SecurityProtocolType; } set { - EnsureStrongCryptoSettingsInitialized(); + EnsureConfigurationLoaded(); ValidateSecurityProtocol(value); s_SecurityProtocolType = value; } @@ -638,77 +641,118 @@ internal static ServerCertValidationCallback ServerCertValidationCallback { internal static bool DisableStrongCrypto { get { - EnsureStrongCryptoSettingsInitialized(); - return (bool)disableStrongCrypto; + EnsureConfigurationLoaded(); + return disableStrongCrypto; } } - private static void EnsureStrongCryptoSettingsInitialized() { - - if (disableStrongCryptoInitialized) { + internal static bool DisableSendAuxRecord { + get { + EnsureConfigurationLoaded(); + return disableSendAuxRecord; + } + } + + private static void EnsureConfigurationLoaded() { + if (configurationLoaded) { return; } - lock (disableStrongCryptoLock) { - if (disableStrongCryptoInitialized) { + lock (configurationLoadedLock) { + if (configurationLoaded) { return; } - try { - bool disableStrongCryptoInternal = false; - int schUseStrongCryptoKeyValue = 0; + LoadDisableStrongCryptoConfiguration(); + LoadDisableSendAuxRecordConfiguration(); - if (LocalAppContextSwitches.DontEnableSchUseStrongCrypto) - { - //.Net 4.5.2 and below will default to false unless the registry key is specifically set to 1. - schUseStrongCryptoKeyValue = - RegistryConfiguration.GlobalConfigReadInt(strongCryptoValueName, 0); + configurationLoaded = true; + } + } - disableStrongCryptoInternal = schUseStrongCryptoKeyValue != 1; - } - else - { - // .Net 4.6 and above will default to true unless the registry key is specifically set to 0. - schUseStrongCryptoKeyValue = - RegistryConfiguration.GlobalConfigReadInt(strongCryptoValueName, 1); + private static void LoadDisableStrongCryptoConfiguration() { + try { + bool disableStrongCryptoInternal = false; + int schUseStrongCryptoKeyValue = 0; - disableStrongCryptoInternal = schUseStrongCryptoKeyValue == 0; - } - - if (disableStrongCryptoInternal) { - // Revert the SecurityProtocol selection to the legacy combination. - s_SecurityProtocolType = SecurityProtocolType.Tls | SecurityProtocolType.Ssl3; - } - else { - s_SecurityProtocolType = - SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; + if (LocalAppContextSwitches.DontEnableSchUseStrongCrypto) { + //.Net 4.5.2 and below will default to false unless the registry key is specifically set to 1. + schUseStrongCryptoKeyValue = + RegistryConfiguration.GlobalConfigReadInt(strongCryptoValueName, 0); - string appSetting = RegistryConfiguration.AppConfigReadString(secureProtocolAppSetting, null); + disableStrongCryptoInternal = schUseStrongCryptoKeyValue != 1; + } + else { + // .Net 4.6 and above will default to true unless the registry key is specifically set to 0. + schUseStrongCryptoKeyValue = + RegistryConfiguration.GlobalConfigReadInt(strongCryptoValueName, 1); - SecurityProtocolType value; - try { - value = (SecurityProtocolType)Enum.Parse(typeof(SecurityProtocolType), appSetting); - ValidateSecurityProtocol(value); - s_SecurityProtocolType = value; - } - // Ignore all potential exceptions caused by Enum.Parse. - catch (ArgumentNullException) { } - catch (ArgumentException) { } - catch (NotSupportedException) { } - catch (OverflowException) { } - } + disableStrongCryptoInternal = schUseStrongCryptoKeyValue == 0; + } - disableStrongCrypto = disableStrongCryptoInternal; - disableStrongCryptoInitialized = true; + if (disableStrongCryptoInternal) { + // Revert the SecurityProtocol selection to the legacy combination. + s_SecurityProtocolType = SecurityProtocolType.Tls | SecurityProtocolType.Ssl3; } - catch (Exception e) { - if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { - throw; + else { + s_SecurityProtocolType = + SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; + + string appSetting = RegistryConfiguration.AppConfigReadString(secureProtocolAppSetting, null); + + SecurityProtocolType value; + try { + value = (SecurityProtocolType)Enum.Parse(typeof(SecurityProtocolType), appSetting); + ValidateSecurityProtocol(value); + s_SecurityProtocolType = value; } + // Ignore all potential exceptions caused by Enum.Parse. + catch (ArgumentNullException) { } + catch (ArgumentException) { } + catch (NotSupportedException) { } + catch (OverflowException) { } + } + + disableStrongCrypto = disableStrongCryptoInternal; + } + catch (Exception e) + { + if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) + { + throw; } } } - + + private static void LoadDisableSendAuxRecordConfiguration() { + try { + if (LocalAppContextSwitches.DontEnableSchSendAuxRecord) { + disableSendAuxRecord = true; + return; + } + + int schSendAuxRecordKeyValue = 1; + schSendAuxRecordKeyValue = RegistryConfiguration.AppConfigReadInt(sendAuxRecordAppSetting, 1); + if (schSendAuxRecordKeyValue == 0) { + disableSendAuxRecord = true; + return; + } + + schSendAuxRecordKeyValue = RegistryConfiguration.GlobalConfigReadInt(sendAuxRecordValueName, 1); + if (schSendAuxRecordKeyValue == 0) { + disableSendAuxRecord = true; + return; + } + } + catch (Exception e) + { + if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) + { + throw; + } + } + } + public static bool ReusePort { get { EnsureReusePortSettingsInitialized(); diff --git a/System/net/System/Net/UnsafeNativeMethods.cs b/System/net/System/Net/UnsafeNativeMethods.cs index 7cf970ed0..680ea9813 100644 --- a/System/net/System/Net/UnsafeNativeMethods.cs +++ b/System/net/System/Net/UnsafeNativeMethods.cs @@ -1482,7 +1482,7 @@ internal static extern SocketError WSAIoctl_Blocking_Internal( [Out] out int bytesTransferred, [In] SafeHandle overlapped, [In] IntPtr completionRoutine - ); + ); [DllImport(WS2_32,SetLastError=true)] internal static extern SocketError WSAEnumNetworkEvents( @@ -3147,6 +3147,25 @@ internal static IPEndPoint GetLocalEndPoint(byte[] memoryBlob, IntPtr originalAd GlobalLog.Leave("HttpApi::GetLocalEndPoint()"); return endpoint; } + + internal static HTTP_REQUEST_TOKEN_BINDING_INFO* GetTlsTokenBindingRequestInfo(byte[] memoryBlob, IntPtr originalAddress){ + + fixed (byte* pMemoryBlob = memoryBlob) + { + HTTP_REQUEST_V2* request = (HTTP_REQUEST_V2*)pMemoryBlob; + long fixup = pMemoryBlob - (byte*) originalAddress; + + for (int i = 0; i < request->RequestInfoCount; i++) + { + HTTP_REQUEST_INFO* pThisInfo = (HTTP_REQUEST_INFO*)(fixup + (byte*)&request->pRequestInfo[i]); + if (pThisInfo != null && pThisInfo->InfoType == HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslTokenBinding) + { + return (HTTP_REQUEST_TOKEN_BINDING_INFO*)pThisInfo->pInfo; + } + } + } + return null; + } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] private static void CopyOutAddress(IntPtr address, ref SocketAddress v4address, ref SocketAddress v6address) diff --git a/System/net/System/Net/_SecureChannel.cs b/System/net/System/Net/_SecureChannel.cs index 0879e4f52..82360ec49 100644 --- a/System/net/System/Net/_SecureChannel.cs +++ b/System/net/System/Net/_SecureChannel.cs @@ -349,7 +349,7 @@ X509Certificate2 EnsurePrivateKey(X509Certificate certificate) if (store != null) { collectionEx = store.Certificates.Find(X509FindType.FindByThumbprint, certHash, false); - if (collectionEx.Count > 0 && collectionEx[0].PrivateKey != null) + if (collectionEx.Count > 0 && collectionEx[0].HasPrivateKey) { if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_found_cert_in_store, (m_ServerMode ? "LocalMachine" : "CurrentUser"))); return collectionEx[0]; @@ -360,7 +360,7 @@ X509Certificate2 EnsurePrivateKey(X509Certificate certificate) if (store != null) { collectionEx = store.Certificates.Find(X509FindType.FindByThumbprint, certHash, false); - if (collectionEx.Count > 0 && collectionEx[0].PrivateKey != null) + if (collectionEx.Count > 0 && collectionEx[0].HasPrivateKey) { if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_found_cert_in_store, (m_ServerMode ? "CurrentUser" : "LocalMachine"))); return collectionEx[0]; @@ -741,6 +741,12 @@ private bool AcquireClientCredentials(ref byte[] thumbPrint) else { SecureCredential.Flags flags = SecureCredential.Flags.ValidateManual | SecureCredential.Flags.NoDefaultCred; + + if (!ServicePointManager.DisableSendAuxRecord) + { + flags |= SecureCredential.Flags.SendAuxRecord; + } + if (!ServicePointManager.DisableStrongCrypto && ((m_ProtocolFlags & (SchProtocols.Tls10 | SchProtocols.Tls11 | SchProtocols.Tls12)) != 0) && (m_EncryptionPolicy != EncryptionPolicy.AllowNoEncryption) && (m_EncryptionPolicy != EncryptionPolicy.NoEncryption)) @@ -824,7 +830,14 @@ private bool AcquireServerCredentials(ref byte[] thumbPrint) } else { - SecureCredential secureCredential = new SecureCredential(SecureCredential.CurrentVersion, selectedCert, SecureCredential.Flags.Zero, m_ProtocolFlags, m_EncryptionPolicy); + SecureCredential.Flags flags = SecureCredential.Flags.Zero; + + if (!ServicePointManager.DisableSendAuxRecord) + { + flags |= SecureCredential.Flags.SendAuxRecord; + } + + SecureCredential secureCredential = new SecureCredential(SecureCredential.CurrentVersion, selectedCert, flags, m_ProtocolFlags, m_EncryptionPolicy); m_CredentialsHandle = AcquireCredentialsHandle(CredentialUse.Inbound, ref secureCredential); thumbPrint = guessedThumbPrint; m_ServerCertificate = localCertificate; diff --git a/System/services/io/system/io/FileSystemWatcher.cs b/System/services/io/system/io/FileSystemWatcher.cs index ed0965d38..500280aed 100644 --- a/System/services/io/system/io/FileSystemWatcher.cs +++ b/System/services/io/system/io/FileSystemWatcher.cs @@ -586,9 +586,6 @@ rename event with the Name field null and then fire the next action. } else { if (oldName != null) { - Debug.Assert(false, "FileSystemWatcher: FILE_ACTION_RENAMED_OLD_NAME with no" + - "new name! [" + oldName + "]"); - NotifyRenameEventArgs(WatcherChangeTypes.Renamed, null, oldName); oldName = null; } diff --git a/System/sys/AppContextDefaultValues.Defaults.cs b/System/sys/AppContextDefaultValues.Defaults.cs index a084bb5dc..da13e1930 100644 --- a/System/sys/AppContextDefaultValues.Defaults.cs +++ b/System/sys/AppContextDefaultValues.Defaults.cs @@ -27,6 +27,11 @@ static partial void PopulateDefaultValuesPartial(string platformIdentifier, stri { LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.DontEnableSchUseStrongCryptoName, true); } + + if (version <= 40601) + { + LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.MemberDescriptorEqualsReturnsFalseIfEquivalentName, true); + } break; } case "WindowsPhone": @@ -34,7 +39,7 @@ static partial void PopulateDefaultValuesPartial(string platformIdentifier, stri { if (version <= 80100) { - LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.DontEnableSchUseStrongCryptoName, true); + LocalAppContext.DefineSwitchDefault(LocalAppContextSwitches.DontEnableSchUseStrongCryptoName, true); } break; } diff --git a/System/sys/LocalAppContextSwitches.cs b/System/sys/LocalAppContextSwitches.cs index 50f2bac56..0d28af159 100644 --- a/System/sys/LocalAppContextSwitches.cs +++ b/System/sys/LocalAppContextSwitches.cs @@ -11,7 +11,21 @@ namespace System internal static class LocalAppContextSwitches { -#region System.Net quirks + #region System quirks + private static int _memberDescriptorEqualsReturnsFalseIfEquivalent; + internal const string MemberDescriptorEqualsReturnsFalseIfEquivalentName = @"Switch.System.MemberDescriptorEqualsReturnsFalseIfEquivalent"; + + public static bool MemberDescriptorEqualsReturnsFalseIfEquivalent + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return LocalAppContext.GetCachedSwitchValue(MemberDescriptorEqualsReturnsFalseIfEquivalentName, ref _memberDescriptorEqualsReturnsFalseIfEquivalent); + } + } + #endregion + + #region System.Net quirks private static int _dontEnableSchUseStrongCrypto; internal const string DontEnableSchUseStrongCryptoName = @"Switch.System.Net.DontEnableSchUseStrongCrypto"; @@ -23,9 +37,7 @@ public static bool DontEnableSchUseStrongCrypto return LocalAppContext.GetCachedSwitchValue(DontEnableSchUseStrongCryptoName, ref _dontEnableSchUseStrongCrypto); } } -#endregion -#region System.Net.WebSockets.HttpListenerAsyncEventArgs private static int _allocateOverlappedOnDemand; internal const string AllocateOverlappedOnDemandName = @"Switch.System.Net.WebSockets.HttpListenerAsyncEventArgs.AllocateOverlappedOnDemand"; @@ -37,7 +49,18 @@ public static bool AllocateOverlappedOnDemand return LocalAppContext.GetCachedSwitchValue(AllocateOverlappedOnDemandName, ref _allocateOverlappedOnDemand); } } -#endregion + private static int _dontEnableSchSendAuxRecord; + internal const string DontEnableSchSendAuxRecordName = @"Switch.System.Net.DontEnableSchSendAuxRecord"; + + public static bool DontEnableSchSendAuxRecord + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return LocalAppContext.GetCachedSwitchValue(DontEnableSchSendAuxRecordName, ref _dontEnableSchSendAuxRecord); + } + } + #endregion } } diff --git a/XamlBuildTask/Microsoft/Build/Tasks/Xaml/PartialClassGenerationTask.cs b/XamlBuildTask/Microsoft/Build/Tasks/Xaml/PartialClassGenerationTask.cs index 30a681e70..fb57cc3cb 100644 --- a/XamlBuildTask/Microsoft/Build/Tasks/Xaml/PartialClassGenerationTask.cs +++ b/XamlBuildTask/Microsoft/Build/Tasks/Xaml/PartialClassGenerationTask.cs @@ -198,8 +198,11 @@ bool ReuseAppDomainAndExecute() } else { - AppDomain.Unload(inProcessAppDomain); - inProcessAppDomain = null; + if (inProcessAppDomain != null) + { + AppDomain.Unload(inProcessAppDomain); + inProcessAppDomain = null; + } return GetAppDomainAndExecute(); } } diff --git a/XamlBuildTask/Microsoft/Build/Tasks/Xaml/XamlBuildTaskServices.cs b/XamlBuildTask/Microsoft/Build/Tasks/Xaml/XamlBuildTaskServices.cs index 03a90fba6..a21ac391e 100644 --- a/XamlBuildTask/Microsoft/Build/Tasks/Xaml/XamlBuildTaskServices.cs +++ b/XamlBuildTask/Microsoft/Build/Tasks/Xaml/XamlBuildTaskServices.cs @@ -215,6 +215,12 @@ public static AppDomain CreateAppDomain(string friendlyName, string buildTaskPat appDomainSetup.ApplicationBase = buildTaskPath; appDomainSetup.LoaderOptimization = LoaderOptimization.MultiDomainHost; + // Set the AppDomainManager class name and assembly name to the empty string. We don't want the AppDomain to try to load the + // Microsoft.VisualStudio.Platform.AppDomainManager.dll because it is no longer in the GAC, so it won't be found + // by the AppDomain that is created for the in-process build to support Intellisense in Visual Studio. + appDomainSetup.AppDomainManagerType = ""; + appDomainSetup.AppDomainManagerAssembly = ""; + // Create appdomain with fulltrust. return AppDomain.CreateDomain( friendlyName, diff --git a/mscorlib/InternalApis/NDP_Common/inc/PinnableBufferCache.cs b/mscorlib/InternalApis/NDP_Common/inc/PinnableBufferCache.cs index 872cfa80d..19ec1053a 100644 --- a/mscorlib/InternalApis/NDP_Common/inc/PinnableBufferCache.cs +++ b/mscorlib/InternalApis/NDP_Common/inc/PinnableBufferCache.cs @@ -155,7 +155,14 @@ internal void Free(object buffer) if (PinnableBufferCacheEventSource.Log.IsEnabled()) PinnableBufferCacheEventSource.Log.FreeBuffer(m_CacheName, PinnableBufferCacheEventSource.AddressOf(buffer), buffer.GetHashCode(), m_FreeList.Count); - + if(buffer == null) + { + if (PinnableBufferCacheEventSource.Log.IsEnabled()) + PinnableBufferCacheEventSource.Log.FreeBufferNull(m_CacheName, m_FreeList.Count); + + return; + } + // After we've done 3 gen1 GCs, assume that all buffers have aged into gen2 on the free path. if ((m_gen1CountAtLastRestock + 3) > GC.CollectionCount(GC.MaxGeneration - 1)) { @@ -567,6 +574,7 @@ public void AllocateBufferCreatingNewBuffers(string cacheName, int totalBuffsBef public void AllocateBufferAged(string cacheName, int agedCount) {} public void AllocateBufferFreeListEmpty(string cacheName, int notGen2CountBefore) {} public void FreeBuffer(string cacheName, ulong objectId, int objectHash, int freeCountBefore) {} + public void FreeBufferNull(string cacheName, int freeCountBefore) { } public void FreeBufferStillTooYoung(string cacheName, int notGen2CountBefore) {} public void TrimCheck(string cacheName, int totalBuffs, bool neededMoreThanFreeList, int deltaMSec) {} public void TrimFree(string cacheName, int totalBuffs, int freeListCount, int toBeFreed) {} @@ -643,6 +651,8 @@ internal sealed class PinnableBufferCacheEventSource : EventSource public void AgePendingBuffersResults(string cacheName, int promotedToFreeListCount, int heldBackCount) { if (IsEnabled()) WriteEvent(20, cacheName, promotedToFreeListCount, heldBackCount); } [Event(21)] public void WalkFreeListResult(string cacheName, int freeListCount, int gen0BuffersInFreeList) { if (IsEnabled()) WriteEvent(21, cacheName, freeListCount, gen0BuffersInFreeList); } + [Event(22)] + public void FreeBufferNull(string cacheName, int freeCountBefore) { if(IsEnabled()) WriteEvent(22, cacheName, freeCountBefore); } static internal ulong AddressOf(object obj) diff --git a/mscorlib/microsoft/win32/win32native.cs b/mscorlib/microsoft/win32/win32native.cs index 9c2df18a2..1bf3586fe 100644 --- a/mscorlib/microsoft/win32/win32native.cs +++ b/mscorlib/microsoft/win32/win32native.cs @@ -937,6 +937,10 @@ internal static extern bool IsWow64Process( [ResourceExposure(ResourceScope.Machine)] internal unsafe static extern int GetFullPathName(char* path, int numBufferChars, char* buffer, IntPtr mustBeZero); + [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)] + [ResourceExposure(ResourceScope.Machine)] + internal unsafe static extern uint GetFullPathNameW(char* path, uint numBufferChars, SafeHandle buffer, IntPtr mustBeZero); + [DllImport(KERNEL32, SetLastError=true, CharSet=CharSet.Auto, BestFitMapping=false)] [ResourceExposure(ResourceScope.Machine)] internal unsafe static extern int GetFullPathName(String path, int numBufferChars, [Out]StringBuilder buffer, IntPtr mustBeZero); @@ -949,6 +953,14 @@ internal static extern bool IsWow64Process( [ResourceExposure(ResourceScope.Machine)] internal static extern int GetLongPathName(String path, [Out]StringBuilder longPathBuffer, int bufferLength); + [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)] + [ResourceExposure(ResourceScope.Machine)] + internal static extern uint GetLongPathNameW(SafeHandle lpszShortPath, SafeHandle lpszLongPath, uint cchBuffer); + + [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)] + [ResourceExposure(ResourceScope.Machine)] + internal static extern uint GetLongPathNameW(string lpszShortPath, SafeHandle lpszLongPath, uint cchBuffer); + // Disallow access to all non-file devices from methods that take // a String. This disallows DOS devices like "con:", "com1:", // "lpt1:", etc. Use this to avoid security problems, like allowing @@ -1397,6 +1409,10 @@ internal static extern int GetCurrentDirectory( int nBufferLength, [Out]StringBuilder lpBuffer); + [DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)] + [ResourceExposure(ResourceScope.Machine)] + internal static extern uint GetCurrentDirectoryW(uint nBufferLength, SafeHandle lpBuffer); + [DllImport(KERNEL32, SetLastError=true, CharSet=CharSet.Auto, BestFitMapping=false)] [ResourceExposure(ResourceScope.None)] internal static extern bool GetFileAttributesEx(String name, int fileInfoLevel, ref WIN32_FILE_ATTRIBUTE_DATA lpFileInformation); diff --git a/mscorlib/system/AppContext/AppContext.cs b/mscorlib/system/AppContext/AppContext.cs index f6f74fa46..0f8a02ac9 100644 --- a/mscorlib/system/AppContext/AppContext.cs +++ b/mscorlib/system/AppContext/AppContext.cs @@ -18,8 +18,7 @@ private enum SwitchValueState HasLookedForOverride = 0x4, UnknownValue = 0x8 // Has no default and could not find an override } - private static Dictionary s_switchMap = new Dictionary(); - private static readonly object s_syncLock = new object(); + private static readonly Dictionary s_switchMap = new Dictionary(); public static string BaseDirectory { @@ -151,11 +150,12 @@ public static void SetSwitch(string switchName, bool isEnabled) if (switchName.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "switchName"); - lock (s_syncLock) + SwitchValueState switchValue = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue) + | SwitchValueState.HasLookedForOverride; + lock (s_switchMap) { // Store the new value and the fact that we checked in the dictionary - s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue) - | SwitchValueState.HasLookedForOverride; + s_switchMap[switchName] = switchValue; } } diff --git a/mscorlib/system/AppContext/AppContextDefaultValues.Defaults.cs b/mscorlib/system/AppContext/AppContextDefaultValues.Defaults.cs index bc1f347ab..d934f6ac1 100644 --- a/mscorlib/system/AppContext/AppContextDefaultValues.Defaults.cs +++ b/mscorlib/system/AppContext/AppContextDefaultValues.Defaults.cs @@ -1,4 +1,4 @@ -// ==++== +// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // @@ -13,8 +13,11 @@ internal static partial class AppContextDefaultValues internal static readonly string SwitchNoAsyncCurrentCulture = "Switch.System.Globalization.NoAsyncCurrentCulture"; internal static readonly string SwitchThrowExceptionIfDisposedCancellationTokenSource = "Switch.System.Threading.ThrowExceptionIfDisposedCancellationTokenSource"; internal static readonly string SwitchPreserveEventListnerObjectIdentity = "Switch.System.Diagnostics.EventSource.PreserveEventListnerObjectIdentity"; + internal static readonly string SwitchUseLegacyPathHandling = "Switch.System.IO.UseLegacyPathHandling"; + internal static readonly string SwitchBlockLongPaths = "Switch.System.IO.BlockLongPaths"; + internal static readonly string SwitchSetActorAsReferenceWhenCopyingClaimsIdentity = "Switch.System.Security.ClaimsIdentity.SetActorAsReferenceWhenCopyingClaimsIdentity"; + - // This is a partial method. Platforms can provide an implementation of it that will set override values // from whatever mechanism is available on that platform. If no implementation is provided, the compiler is going to remove the calls // to it from the code @@ -41,6 +44,14 @@ static partial void PopulateDefaultValuesPartial(string platformIdentifier, stri AppContext.DefineSwitchDefault(SwitchNoAsyncCurrentCulture, true); AppContext.DefineSwitchDefault(SwitchThrowExceptionIfDisposedCancellationTokenSource, true); } + + if (version <= 40601) + { + AppContext.DefineSwitchDefault(SwitchUseLegacyPathHandling, true); + AppContext.DefineSwitchDefault(SwitchBlockLongPaths, true); + AppContext.DefineSwitchDefault(SwitchSetActorAsReferenceWhenCopyingClaimsIdentity, true); + } + break; } case "WindowsPhone": @@ -50,6 +61,8 @@ static partial void PopulateDefaultValuesPartial(string platformIdentifier, stri { AppContext.DefineSwitchDefault(SwitchNoAsyncCurrentCulture, true); AppContext.DefineSwitchDefault(SwitchThrowExceptionIfDisposedCancellationTokenSource, true); + AppContext.DefineSwitchDefault(SwitchUseLegacyPathHandling, true); + AppContext.DefineSwitchDefault(SwitchBlockLongPaths, true); } break; } diff --git a/mscorlib/system/AppContext/AppContextDefaultValues.cs b/mscorlib/system/AppContext/AppContextDefaultValues.cs index 8a130a0f3..9224bf8b8 100644 --- a/mscorlib/system/AppContext/AppContextDefaultValues.cs +++ b/mscorlib/system/AppContext/AppContextDefaultValues.cs @@ -3,6 +3,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== + +// There are cases where we have multiple assemblies that are going to import this file and +// if they are going to also have InternalsVisibleTo between them, there will be a compiler warning +// that the type is found both in the source and in a referenced assembly. The compiler will prefer +// the version of the type defined in the source +// +// In order to disable the warning for this type we are disabling this warning for this entire file. +#pragma warning disable 436 + using System; using System.Collections.Generic; @@ -167,3 +176,5 @@ private static bool TryParseFrameworkName(String frameworkName, out String ident static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version); } } + +#pragma warning restore 436 diff --git a/mscorlib/system/AppContext/AppContextSwitches.cs b/mscorlib/system/AppContext/AppContextSwitches.cs index 539e87701..72eb1cccb 100644 --- a/mscorlib/system/AppContext/AppContextSwitches.cs +++ b/mscorlib/system/AppContext/AppContextSwitches.cs @@ -41,6 +41,48 @@ public static bool PreserveEventListnerObjectIdentity } } + private static int _useLegacyPathHandling; + + /// + /// Use legacy path normalization logic and blocking of extended syntax. + /// + public static bool UseLegacyPathHandling + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return GetCachedSwitchValue(AppContextDefaultValues.SwitchUseLegacyPathHandling, ref _useLegacyPathHandling); + } + } + + private static int _blockLongPaths; + + /// + /// Throw PathTooLongException for paths greater than MAX_PATH or directories greater than 248 (as per CreateDirectory Win32 limitations) + /// + public static bool BlockLongPaths + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return GetCachedSwitchValue(AppContextDefaultValues.SwitchBlockLongPaths, ref _blockLongPaths); + } + } + + private static int _cloneActor; + + /// + /// When copying a ClaimsIdentity.Actor this switch controls whether ClaimsIdentity.Actor should be set as a reference or the result of Actor.Clone() + /// + public static bool SetActorAsReferenceWhenCopyingClaimsIdentity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return GetCachedSwitchValue(AppContextDefaultValues.SwitchSetActorAsReferenceWhenCopyingClaimsIdentity, ref _cloneActor); + } + } + // // Implementation details // diff --git a/mscorlib/system/AppDomainSetup.cs b/mscorlib/system/AppDomainSetup.cs index fb850445c..5509b4025 100644 --- a/mscorlib/system/AppDomainSetup.cs +++ b/mscorlib/system/AppDomainSetup.cs @@ -318,17 +318,23 @@ public String ApplicationBase } } - [ResourceExposure(ResourceScope.Machine)] - [ResourceConsumption(ResourceScope.Machine)] - private String NormalizePath(String path, bool useAppBase) + [System.Security.SecuritySafeCritical] + private string NormalizePath(string path, bool useAppBase) { if(path == null) return null; // If we add very long file name support ("\\?\") to the Path class then this is unnecesary, // but we do not plan on doing this for now. + + // Long path checks can be quirked, and as loading default quirks too early in the setup of an AppDomain is risky + // we'll avoid checking path lengths- we'll still fail at MAX_PATH later if we're !useAppBase when we call Path's + // NormalizePath. if (!useAppBase) - path = System.Security.Util.URLString.PreProcessForExtendedPathRemoval(path, false); + path = System.Security.Util.URLString.PreProcessForExtendedPathRemoval( + checkPathLength: false, + url: path, + isFileUrl: false); int len = path.Length; if (len == 0) @@ -417,10 +423,10 @@ private String NormalizePath(String path, bool useAppBase) if (localPath) { - if (useAppBase && - ( (len == 1) || (path[1] != ':') )) { - String appBase = Value[(int) LoaderInformation.ApplicationBaseValue]; + ((len == 1) || (path[1] != ':'))) + { + String appBase = Value[(int)LoaderInformation.ApplicationBaseValue]; if ((appBase == null) || (appBase.Length == 0)) throw new MemberAccessException(Environment.GetResourceString("AppDomain_AppBaseNotSet")); @@ -429,7 +435,9 @@ private String NormalizePath(String path, bool useAppBase) bool slash = false; if ((path[0] == '/') || (path[0] == '\\')) { - String pathRoot = Path.GetPathRoot(appBase); + // Emulate Path.GetPathRoot without hitting code paths that check quirks + string pathRoot = AppDomain.NormalizePath(appBase, fullCheck: false); + pathRoot = pathRoot.Substring(0, System.IO.PathInternal.GetRootLength(pathRoot)); if (pathRoot.Length == 0) { // URL int index = appBase.IndexOf(":/", StringComparison.Ordinal); if (index == -1) @@ -439,11 +447,11 @@ private String NormalizePath(String path, bool useAppBase) int urlLen = appBase.Length; for (index += 1; (index < urlLen) && ((appBase[index] == '/') || (appBase[index] == '\\')); - index++); + index++) ; // Now find the next slash to get domain name - for(; (index < urlLen) && (appBase[index] != '/') && (appBase[index] != '\\'); - index++); + for (; (index < urlLen) && (appBase[index] != '/') && (appBase[index] != '\\'); + index++) ; pathRoot = appBase.Substring(0, index); } @@ -472,7 +480,9 @@ private String NormalizePath(String path, bool useAppBase) path = StringBuilderCache.GetStringAndRelease(result); } else - path = Path.GetFullPathInternal(path); + { + path = AppDomain.NormalizePath(path, fullCheck: true); + } } return path; @@ -792,20 +802,32 @@ public bool DisallowApplicationBaseProbing [ResourceConsumption(ResourceScope.Machine)] private String VerifyDir(String dir, bool normalize) { - if (dir != null) { + if (dir != null) + { if (dir.Length == 0) + { dir = null; - else { + } + else + { if (normalize) dir = NormalizePath(dir, true); - // The only way AppDomainSetup is exposed in coreclr is through the AppDomainManager - // and the AppDomainManager is a SecurityCritical type. Also, all callers of callstacks - // leading from VerifyDir are SecurityCritical. So we can remove the Demand because - // we have validated that all callers are SecurityCritical + // The only way AppDomainSetup is exposed in coreclr is through the AppDomainManager + // and the AppDomainManager is a SecurityCritical type. Also, all callers of callstacks + // leading from VerifyDir are SecurityCritical. So we can remove the Demand because + // we have validated that all callers are SecurityCritical #if !FEATURE_CORECLR if (IsFilePath(dir)) - new FileIOPermission( FileIOPermissionAccess.PathDiscovery, dir ).Demand(); + { + // If we've already normalized we don't need to do it again, and can avoid hitting + // quirks in FileIOPermission. + new FileIOPermission( + access: FileIOPermissionAccess.PathDiscovery, + pathList: new string[] { dir }, + checkForDuplicates: false, + needFullPath: false).Demand(); + } #endif // !FEATURE_CORECLR } } diff --git a/mscorlib/system/appdomain.cs b/mscorlib/system/appdomain.cs index 0d7719d22..2a704e830 100644 --- a/mscorlib/system/appdomain.cs +++ b/mscorlib/system/appdomain.cs @@ -2712,7 +2712,7 @@ public String DynamicDirectory get { String dyndir = GetDynamicDir(); if (dyndir != null) - new FileIOPermission( FileIOPermissionAccess.PathDiscovery, dyndir ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, dyndir); return dyndir; } @@ -3726,7 +3726,7 @@ private static Object Setup(Object arg) AppDomainInitializerInfo initializerInfo = (AppDomainInitializerInfo)args[5]; string sandboxName = (string)args[6]; string[] propertyNames = (string[])args[7]; // can contain null elements - string[] propertyValues = (string[])args[8]; // can contain null elements + string[] propertyValues = (string[])args[8]; // can contain null elements // extract evidence Evidence providedSecurityInfo = null; Evidence creatorsSecurityInfo = null; @@ -3748,18 +3748,17 @@ private static Object Setup(Object arg) if (Path.IsRelative(propertyValues[i])) throw new ArgumentException( Environment.GetResourceString( "Argument_AbsolutePathRequired" ) ); - newSetup.ApplicationBase=Path.NormalizePath(propertyValues[i],true); - + newSetup.ApplicationBase = NormalizePath(propertyValues[i], fullCheck: true); } #if FEATURE_CAS_POLICY else if(propertyNames[i]=="LOCATION_URI" && providedSecurityInfo==null) { providedSecurityInfo=new Evidence(); providedSecurityInfo.AddHostEvidence(new Url(propertyValues[i])); - ad.SetDataHelper(propertyNames[i],propertyValues[i],null); + ad.SetDataHelper(propertyNames[i],propertyValues[i],null); } #endif // FEATURE_CAS_POLICY -#if FEATURE_LOADER_OPTIMIZATION +#if FEATURE_LOADER_OPTIMIZATION else if(propertyNames[i]=="LOADER_OPTIMIZATION") { @@ -3775,8 +3774,8 @@ private static Object Setup(Object arg) default: throw new ArgumentException(Environment.GetResourceString("Argument_UnrecognizedLoaderOptimization"), "LOADER_OPTIMIZATION"); } } -#endif // FEATURE_LOADER_OPTIMIZATION -#if FEATURE_CORECLR +#endif // FEATURE_LOADER_OPTIMIZATION +#if FEATURE_CORECLR else if(propertyNames[i]=="NATIVE_DLL_SEARCH_DIRECTORIES") { @@ -3804,7 +3803,8 @@ private static Object Setup(Object arg) if (Path.IsRelative(path)) throw new ArgumentException( Environment.GetResourceString( "Argument_AbsolutePathRequired" ) ); - string appPath=Path.NormalizePath(path,true); + string appPath = NormalizePath(path, fullCheck: true); + normalisedAppPathList.Append(appPath); normalisedAppPathList.Append(Path.PathSeparator); } @@ -3813,7 +3813,7 @@ private static Object Setup(Object arg) { normalisedAppPathList.Remove(normalisedAppPathList.Length - 1, 1); } - ad.SetDataHelper(propertyNames[i],normalisedAppPathList.ToString(),null); // not supported by fusion, so set explicitly + ad.SetDataHelper(propertyNames[i],normalisedAppPathList.ToString(),null); // not supported by fusion, so set explicitly } else if(propertyNames[i]=="PLATFORM_RESOURCE_ROOTS") @@ -3831,7 +3831,8 @@ private static Object Setup(Object arg) if (Path.IsRelative(path)) throw new ArgumentException( Environment.GetResourceString( "Argument_AbsolutePathRequired" ) ); - string appPath=Path.NormalizePath(path,true); + string appPath = NormalizePath(path, fullCheck: true); + normalisedAppPathList.Append(appPath); normalisedAppPathList.Append(Path.PathSeparator); } @@ -3840,7 +3841,7 @@ private static Object Setup(Object arg) { normalisedAppPathList.Remove(normalisedAppPathList.Length - 1, 1); } - ad.SetDataHelper(propertyNames[i],normalisedAppPathList.ToString(),null); // not supported by fusion, so set explicitly + ad.SetDataHelper(propertyNames[i],normalisedAppPathList.ToString(),null); // not supported by fusion, so set explicitly } else if(propertyNames[i]=="APP_PATHS") @@ -3858,7 +3859,8 @@ private static Object Setup(Object arg) if (Path.IsRelative(path)) throw new ArgumentException( Environment.GetResourceString( "Argument_AbsolutePathRequired" ) ); - string appPath=Path.NormalizePath(path,true); + string appPath = NormalizePath(path, fullCheck: true); + normalisedAppPathList.Append(appPath); normalisedAppPathList.Append(Path.PathSeparator); } @@ -3867,7 +3869,7 @@ private static Object Setup(Object arg) { normalisedAppPathList.Remove(normalisedAppPathList.Length - 1, 1); } - ad.SetDataHelper(propertyNames[i],normalisedAppPathList.ToString(),null); // not supported by fusion, so set explicitly + ad.SetDataHelper(propertyNames[i],normalisedAppPathList.ToString(),null); // not supported by fusion, so set explicitly } else if(propertyNames[i]=="APP_NI_PATHS") @@ -3885,7 +3887,8 @@ private static Object Setup(Object arg) if (Path.IsRelative(path)) throw new ArgumentException( Environment.GetResourceString( "Argument_AbsolutePathRequired" ) ); - string appPath=Path.NormalizePath(path,true); + string appPath = NormalizePath(path, fullCheck: true); + normalisedAppPathList.Append(appPath); normalisedAppPathList.Append(Path.PathSeparator); } @@ -3894,12 +3897,12 @@ private static Object Setup(Object arg) { normalisedAppPathList.Remove(normalisedAppPathList.Length - 1, 1); } - ad.SetDataHelper(propertyNames[i],normalisedAppPathList.ToString(),null); // not supported by fusion, so set explicitly + ad.SetDataHelper(propertyNames[i],normalisedAppPathList.ToString(),null); // not supported by fusion, so set explicitly } else if(propertyNames[i]!= null) { - ad.SetDataHelper(propertyNames[i],propertyValues[i],null); // just propagate + ad.SetDataHelper(propertyNames[i],propertyValues[i],null); // just propagate } #endif @@ -3993,6 +3996,19 @@ private static Object Setup(Object arg) #endif // FEATURE_CLICKONCE } + [SecuritySafeCritical] + internal static string NormalizePath(string path, bool fullCheck) + { + // We have to directly hit LegacyNormalizePath to avoid loading quirks for + // the AppDomain. (Once we have runtime support for long paths we can + // use the new normalization in path, but we still need to go in directly + // to avoid quirks.) + return Path.LegacyNormalizePath( + path: path, + fullCheck: fullCheck, + maxPathLength: PathInternal.MaxShortPath, + expandShortPaths: true); + } #if FEATURE_APTCA // Called from DomainAssembly in Conditional APTCA cases @@ -4654,7 +4670,7 @@ public Int32 Id [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.None)] [MethodImplAttribute(MethodImplOptions.InternalCall)] - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] internal extern Int32 GetId(); internal const Int32 DefaultADID = 1; @@ -4675,6 +4691,7 @@ private static AppDomainSetup InternalCreateDomainSetup(String imageLocation) Contract.Assert(i != -1, "invalid image location"); AppDomainSetup info = new AppDomainSetup(); + info.ApplicationBase = imageLocation.Substring(0, i+1); StringBuilder config = new StringBuilder(imageLocation.Substring(i+1)); @@ -4685,7 +4702,7 @@ private static AppDomainSetup InternalCreateDomainSetup(String imageLocation) } // Used by the validator for testing but not executing an assembly -#if FEATURE_REMOTING +#if FEATURE_REMOTING [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private static AppDomain InternalCreateDomain(String imageLocation) diff --git a/mscorlib/system/bitconverter.cs b/mscorlib/system/bitconverter.cs index a23d38ead..6d9f98fc9 100644 --- a/mscorlib/system/bitconverter.cs +++ b/mscorlib/system/bitconverter.cs @@ -360,7 +360,7 @@ private static char GetHexValue(int i) { // Converts an array of bytes into a String. public static String ToString (byte[] value, int startIndex, int length) { if (value == null) { - throw new ArgumentNullException("byteArray"); + throw new ArgumentNullException("value"); } if (startIndex < 0 || startIndex >= value.Length && startIndex > 0) { // Don't throw for a 0 length array. diff --git a/mscorlib/system/deployment/cmsutils.cs b/mscorlib/system/deployment/cmsutils.cs index 1c0fb3137..247c7dfe2 100644 --- a/mscorlib/system/deployment/cmsutils.cs +++ b/mscorlib/system/deployment/cmsutils.cs @@ -79,18 +79,18 @@ internal static string GetEntryPointFullPath (ActivationArguments activationArgu [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] - internal static string GetEntryPointFullPath (ActivationContext activationContext) { + internal static string GetEntryPointFullPath (ActivationContext activationContext) + { string file, parameters; GetEntryPoint(activationContext, out file, out parameters); - if (!String.IsNullOrEmpty(file)) { + if (!string.IsNullOrEmpty(file)) + { string directoryName = activationContext.ApplicationDirectory; - if (directoryName == null || directoryName.Length == 0) { + if (directoryName == null || directoryName.Length == 0) + { // If we were passed a relative path, assume the app base is the current working directory - StringBuilder sb = new StringBuilder(Path.MAX_PATH + 1); - if (Win32Native.GetCurrentDirectory(sb.Capacity, sb) == 0) - System.IO.__Error.WinIOError(); - directoryName = sb.ToString(); + directoryName = Directory.UnsafeGetCurrentDirectory(); } file = Path.Combine(directoryName, file); diff --git a/mscorlib/system/diagnostics/eventing/TraceLogging/SimpleTypeInfos.cs b/mscorlib/system/diagnostics/eventing/TraceLogging/SimpleTypeInfos.cs index cd70bfb23..08844d397 100644 --- a/mscorlib/system/diagnostics/eventing/TraceLogging/SimpleTypeInfos.cs +++ b/mscorlib/system/diagnostics/eventing/TraceLogging/SimpleTypeInfos.cs @@ -833,6 +833,15 @@ public override void WriteData(TraceLoggingDataCollector collector, ref String v { collector.AddBinary(value); } + + public override object GetData(object value) + { + object val = base.GetData(value); + if (null == val) + val = ""; + + return val; + } } /// diff --git a/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingEventSource.cs b/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingEventSource.cs index d620ff963..ae38d4fde 100644 --- a/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingEventSource.cs +++ b/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingEventSource.cs @@ -460,6 +460,7 @@ private unsafe void WriteMultiMergeInner( } this.WriteEventRaw( + eventName, ref descriptor, activityID, childActivityID, @@ -564,6 +565,7 @@ internal unsafe void WriteMultiMerge( } this.WriteEventRaw( + eventName, ref descriptor, activityID, childActivityID, @@ -646,6 +648,7 @@ private unsafe void WriteImpl( eventTypes.typeInfo.WriteData(TraceLoggingDataCollector.Instance, ref data); this.WriteEventRaw( + eventName, ref descriptor, pActivityId, pRelatedActivityId, @@ -665,7 +668,7 @@ private unsafe void WriteImpl( if (ex is EventSourceException) throw; else - ThrowEventSourceException(ex); + ThrowEventSourceException(eventName, ex); } finally { @@ -679,7 +682,7 @@ private unsafe void WriteImpl( if (ex is EventSourceException) throw; else - ThrowEventSourceException(ex); + ThrowEventSourceException(eventName, ex); } } @@ -703,7 +706,7 @@ private unsafe void WriteToAllListeners(string eventName, ref EventDescriptor ev eventCallbackArgs.PayloadNames = new ReadOnlyCollection((IList)payload.Keys); } - DisptachToAllListeners(-1, pActivityId, eventCallbackArgs); + DispatchToAllListeners(-1, pActivityId, eventCallbackArgs); } #if !ES_BUILD_PCL diff --git a/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingMetadataCollector.cs b/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingMetadataCollector.cs index 1d1f1e708..ed5ffeca9 100644 --- a/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingMetadataCollector.cs +++ b/mscorlib/system/diagnostics/eventing/TraceLogging/TraceLoggingMetadataCollector.cs @@ -99,7 +99,7 @@ public TraceLoggingMetadataCollector AddGroup(string name) var newGroup = new FieldMetadata( name, TraceLoggingDataType.Struct, - 0, + this.Tags, this.BeginningBufferedArray); this.AddField(newGroup); result = new TraceLoggingMetadataCollector(this, newGroup); diff --git a/mscorlib/system/diagnostics/eventing/activitytracker.cs b/mscorlib/system/diagnostics/eventing/activitytracker.cs index 633183d94..bdfa364fe 100644 --- a/mscorlib/system/diagnostics/eventing/activitytracker.cs +++ b/mscorlib/system/diagnostics/eventing/activitytracker.cs @@ -1,8 +1,6 @@ using System; using System.Diagnostics; using System.Threading; -using System.Threading.Tasks; - #if !ES_BUILD_AGAINST_DOTNET_V35 using Contract = System.Diagnostics.Contracts.Contract; #else @@ -11,15 +9,16 @@ #if ES_BUILD_STANDALONE namespace Microsoft.Diagnostics.Tracing -#else +#else +using System.Threading.Tasks; namespace System.Diagnostics.Tracing #endif { /// - /// Tracks activities. This is meant to be a singledon (accessed by the ActivityTracer.Instance static property) + /// Tracks activities. This is meant to be a singleton (accessed by the ActivityTracer.Instance static property) /// /// Logically this is simply holds the m_current variable that holds the async local that holds the current ActivityInfo - /// An ActivityInfo is represents a actvity (which knows its creator and thus knows its path). + /// An ActivityInfo is represents a activity (which knows its creator and thus knows its path). /// /// Most of the magic is in the async local (it gets copied to new tasks) /// @@ -39,7 +38,7 @@ namespace System.Diagnostics.Tracing /// On any normal event log the event with activityTracker.CurrentActivityId /// internal class ActivityTracker - { + { /// /// Called on work item begins. The activity name = providerName + activityName without 'Start' suffix. @@ -55,15 +54,32 @@ internal class ActivityTracker public void OnStart(string providerName, string activityName, int task, ref Guid activityId, ref Guid relatedActivityId, EventActivityOptions options) { if (m_current == null) // We are not enabled - return; + { + // We used to rely on the TPL provider turning us on, but that has the disadvantage that you don't get Start-Stop tracking + // until you use Tasks for the first time (which you may never do). Thus we change it to pull rather tan push for whether + // we are enabled. + if (m_checkedForEnable) + return; + m_checkedForEnable = true; +#if ES_BUILD_STANDALONE + Enable(); // Enable it unconditionally. +#else + if (System.Threading.Tasks.TplEtwProvider.Log.IsEnabled(EventLevel.Informational, System.Threading.Tasks.TplEtwProvider.Keywords.TasksFlowActivityIds)) + Enable(); +#endif + if(m_current == null) + { + return; + } + } Contract.Assert((options & EventActivityOptions.Disable) == 0); var currentActivity = m_current.Value; var fullActivityName = NormalizeActivityName(providerName, activityName, task); - + var etwLog = TplEtwProvider.Log; - if (etwLog.Debug) + if (etwLog.Debug) { etwLog.DebugFacilityMessage("OnStartEnter", fullActivityName); etwLog.DebugFacilityMessage("OnStartEnterActivityState", ActivityInfo.LiveActivities(currentActivity)); @@ -100,17 +116,17 @@ public void OnStart(string providerName, string activityName, int task, ref Guid else id = Interlocked.Increment(ref currentActivity.m_lastChildID); - // Remember the previous ID so we can log it - relatedActivityId = currentActivity != null ? currentActivity.ActivityId : Guid.Empty; + // The previous ID is my 'causer' and becomes my related activity ID + relatedActivityId = EventSource.CurrentThreadActivityId; // Add to the list of started but not stopped activities. - ActivityInfo newActivity = new ActivityInfo(fullActivityName, id, currentActivity, options); + ActivityInfo newActivity = new ActivityInfo(fullActivityName, id, currentActivity, relatedActivityId, options); m_current.Value = newActivity; // Remember the current ID so we can log it activityId = newActivity.ActivityId; - if (etwLog.Debug) + if (etwLog.Debug) { etwLog.DebugFacilityMessage("OnStartRetActivityState", ActivityInfo.LiveActivities(newActivity)); etwLog.DebugFacilityMessage1("OnStartRet", activityId.ToString(), relatedActivityId.ToString()); @@ -119,7 +135,7 @@ public void OnStart(string providerName, string activityName, int task, ref Guid /// /// Called when a work item stops. The activity name = providerName + activityName without 'Stop' suffix. - /// It updates CurrentActivityId to track this fact. The Stop event associated with stop should log the ActivityID associated with the event. + /// It updates m_current variable to track this fact. The Stop event associated with stop should log the ActivityID associated with the event. /// /// If activity tracing is not on, then activityId and relatedActivityId are not set /// @@ -131,13 +147,13 @@ public void OnStop(string providerName, string activityName, int task, ref Guid var fullActivityName = NormalizeActivityName(providerName, activityName, task); var etwLog = TplEtwProvider.Log; - if (etwLog.Debug) + if (etwLog.Debug) { etwLog.DebugFacilityMessage("OnStopEnter", fullActivityName); etwLog.DebugFacilityMessage("OnStopEnterActivityState", ActivityInfo.LiveActivities(m_current.Value)); } - for (; ;) // This is a retry loop. + for (; ; ) // This is a retry loop. { ActivityInfo currentActivity = m_current.Value; ActivityInfo newCurrentActivity = null; // if we have seen any live activities (orphans), at he first one we have seen. @@ -193,7 +209,7 @@ public void OnStop(string providerName, string activityName, int task, ref Guid m_current.Value = newCurrentActivity; - if (etwLog.Debug) + if (etwLog.Debug) { etwLog.DebugFacilityMessage("OnStopRetActivityState", ActivityInfo.LiveActivities(newCurrentActivity)); etwLog.DebugFacilityMessage("OnStopRet", activityId.ToString()); @@ -210,12 +226,12 @@ public void OnStop(string providerName, string activityName, int task, ref Guid [System.Security.SecuritySafeCritical] public void Enable() { - if (m_current == null) + if (m_current == null) { m_current = new AsyncLocal(ActivityChanging); } } - + /// /// An activity tracker is a singleton, this is how you get the one and only instance. /// @@ -263,24 +279,25 @@ private string NormalizeActivityName(string providerName, string activityName, i // ******************************************************************************* /// - /// An ActivityInfo repesents a particular activity. It is almost read-only the only + /// An ActivityInfo represents a particular activity. It is almost read-only. The only /// fields that change after creation are /// m_lastChildID - used to generate unique IDs for the children activities and for the most part can be ignored. /// m_stopped - indicates that this activity is dead - /// This read-only ness is important because an activity's m_creator chain forms the + /// This read-only-ness is important because an activity's m_creator chain forms the /// 'Path of creation' for the activity (which is also its unique ID) but is also used as /// the 'list of live parents' which indicate of those ancestors, which are alive (if they /// are not marked dead they are alive). /// private class ActivityInfo { - public ActivityInfo(string name, long uniqueId, ActivityInfo creator, EventActivityOptions options) + public ActivityInfo(string name, long uniqueId, ActivityInfo creator, Guid activityIDToRestore, EventActivityOptions options) { m_name = name; m_eventOptions = options; m_creator = creator; m_uniqueId = uniqueId; m_level = creator != null ? creator.m_level + 1 : 0; + m_activityIdToRestore = activityIDToRestore; // Create a nice GUID that encodes the chain of activities that started this one. CreateActivityPathGuid(out m_guid, out m_activityPathGuidOffset); @@ -294,19 +311,19 @@ public Guid ActivityId } } - public static string Path(ActivityInfo activityInfo) + public static string Path(ActivityInfo activityInfo) { if (activityInfo == null) - return(""); + return (""); return Path(activityInfo.m_creator) + "/" + activityInfo.m_uniqueId; } - public override string ToString() + public override string ToString() { string dead = ""; if (m_stopped != 0) - dead = ",DEAD"; - return m_name + "(" + Path(this) + dead + ")"; + dead = ",DEAD"; + return m_name + "(" + Path(this) + dead + ")"; } public static string LiveActivities(ActivityInfo list) @@ -331,10 +348,10 @@ public bool CanBeOrphan() /// (rooted in an activity that predates activity tracking. /// /// We wish to encode this path in the Guid to the extent that we can. Many of the paths have - /// many small numbers in them and we take advatage of this in the encoding to output as long + /// many small numbers in them and we take advantage of this in the encoding to output as long /// a path in the GUID as possible. /// - /// Because of the possiblility of GUID collistion, we only use 96 of the 128 bits of the GUID + /// Because of the possibility of GUID collision, we only use 96 of the 128 bits of the GUID /// for encoding the path. The last 32 bits are a simple checksum (and random number) that /// identifies this as using the convention defined here. /// @@ -347,7 +364,7 @@ public bool CanBeOrphan() [System.Security.SecuritySafeCritical] private unsafe void CreateActivityPathGuid(out Guid idRet, out int activityPathGuidOffset) { - fixed (Guid* outPtr = &idRet) + fixed (Guid* outPtr = &idRet) { int activityPathGuidOffsetStart = 0; if (m_creator != null) @@ -355,13 +372,18 @@ private unsafe void CreateActivityPathGuid(out Guid idRet, out int activityPathG activityPathGuidOffsetStart = m_creator.m_activityPathGuidOffset; idRet = m_creator.m_guid; } - else + else { - // We start with the appdomain number to make this unique among appdomains. - activityPathGuidOffsetStart = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint) System.Threading.Thread.GetDomainID()); + // + int appDomainID = 0; +#if !ES_BUILD_PCL + appDomainID = System.Threading.Thread.GetDomainID(); +#endif + // We start with the appdomain number to make this unique among appdomains. + activityPathGuidOffsetStart = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint)appDomainID); } - activityPathGuidOffset = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint) m_uniqueId); + activityPathGuidOffset = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint)m_uniqueId); // If the path does not fit, Make a GUID by incrementing rather than as a path, keeping as much of the path as possible @@ -372,19 +394,19 @@ private unsafe void CreateActivityPathGuid(out Guid idRet, out int activityPathG /// /// If we can't fit the activity Path into the GUID we come here. What we do is simply - /// generate a 4 byte number (s_nextOverflowId). Then look for an anscesor that has + /// generate a 4 byte number (s_nextOverflowId). Then look for an ancestor that has /// sufficient space for this ID. By doing this, we preserve the fact that this activity /// is a child (of unknown depth) from that ancestor. /// [System.Security.SecurityCritical] private unsafe void CreateOverflowGuid(Guid* outPtr) { - // Seach backwards for an ancestor that has sufficient space to put the ID. - for(ActivityInfo ancestor = m_creator; ancestor != null; ancestor = ancestor.m_creator) + // Search backwards for an ancestor that has sufficient space to put the ID. + for (ActivityInfo ancestor = m_creator; ancestor != null; ancestor = ancestor.m_creator) { if (ancestor.m_activityPathGuidOffset <= 10) // we need at least 2 bytes. { - uint id = (uint) Interlocked.Increment(ref ancestor.m_lastChildID); // Get a unique ID + uint id = unchecked((uint)Interlocked.Increment(ref ancestor.m_lastChildID)); // Get a unique ID // Try to put the ID into the GUID *outPtr = ancestor.m_guid; int endId = AddIdToGuid(outPtr, ancestor.m_activityPathGuidOffset, id, true); @@ -397,8 +419,8 @@ private unsafe void CreateOverflowGuid(Guid* outPtr) } /// - /// The encoding for a list of numbers used to make Activity Guids. Basically - /// we operate on nibbles (which are nice becase they show up as hex digits). The + /// The encoding for a list of numbers used to make Activity GUIDs. Basically + /// we operate on nibbles (which are nice because they show up as hex digits). The /// list is ended with a end nibble (0) and depending on the nibble value (Below) /// the value is either encoded into nibble itself or it can spill over into the /// bytes that follow. @@ -409,18 +431,18 @@ enum NumberListCodes : byte LastImmediateValue = 0xA, PrefixCode = 0xB, // all the 'long' encodings go here. If the next nibble is MultiByte1-4 - // than this is a 'overflow' id. Unlike the hierarchitcal IDs these are - // allocated densly but don't tell you anything about nesting. we use - // these when we run out of space in the GUID to store the path. + // than this is a 'overflow' id. Unlike the hierarchical IDs these are + // allocated densely but don't tell you anything about nesting. we use + // these when we run out of space in the GUID to store the path. MultiByte1 = 0xC, // 1 byte follows. If this Nibble is in the high bits, it the high bits of the number are stored in the low nibble. // commented out because the code does not explicitly reference the names (but they are logically defined). - // MultiByte2 = 0xD, // 2 bytes follow (we don't bother with the nibble optimzation) - // MultiByte3 = 0xE, // 3 bytes follow (we don't bother with the nibble optimzation) - // MultiByte4 = 0xF, // 4 bytes follow (we don't bother with the nibble optimzation) + // MultiByte2 = 0xD, // 2 bytes follow (we don't bother with the nibble optimization) + // MultiByte3 = 0xE, // 3 bytes follow (we don't bother with the nibble optimization) + // MultiByte4 = 0xF, // 4 bytes follow (we don't bother with the nibble optimization) } - /// Add the acivity id 'id' to the output Guid 'outPtr' starting at the offset 'whereToAddId' + /// Add the activity id 'id' to the output Guid 'outPtr' starting at the offset 'whereToAddId' /// Thus if this number is 6 that is where 'id' will be added. This will return 13 (12 /// is the maximum number of bytes that fit in a GUID) if the path did not fit. /// If 'overflow' is true, then the number is encoded as an 'overflow number (which has a @@ -450,9 +472,9 @@ private static unsafe int AddIdToGuid(Guid* outPtr, int whereToAddId, uint id, b { if (endPtr <= ptr + 2) // I need at least 2 bytes return 13; - + // Write out the prefix code nibble and the length nibble - WriteNibble(ref ptr, endPtr, (uint) NumberListCodes.PrefixCode); + WriteNibble(ref ptr, endPtr, (uint)NumberListCodes.PrefixCode); } // The rest is the same for overflow and non-overflow case WriteNibble(ref ptr, endPtr, (uint)NumberListCodes.MultiByte1 + (len - 1)); @@ -460,7 +482,7 @@ private static unsafe int AddIdToGuid(Guid* outPtr, int whereToAddId, uint id, b // Do we have an odd nibble? If so flush it or use it for the 12 byte case. if (ptr < endPtr && *ptr != 0) { - // If the value < 4096 we can use the nibble we are otherwise just outputing as padding. + // If the value < 4096 we can use the nibble we are otherwise just outputting as padding. if (id < 4096) { // Indicate this is a 1 byte multicode with 4 high order bits in the lower nibble. @@ -471,7 +493,7 @@ private static unsafe int AddIdToGuid(Guid* outPtr, int whereToAddId, uint id, b } // Write out the bytes. - while(0 < len) + while (0 < len) { if (endPtr <= ptr) { @@ -514,14 +536,15 @@ private static unsafe void WriteNibble(ref byte* ptr, byte* endPtr, uint value) #endregion // CreateGuidForActivityPath readonly internal string m_name; // The name used in the 'start' and 'stop' APIs to help match up - readonly long m_uniqueId; // a small number that makes this activity unique among its siblings - internal readonly Guid m_guid; // Activity Guid, it is bascially an encoding of the Path() (see CreateActivityPathGuid) + readonly long m_uniqueId; // a small number that makes this activity unique among its siblings + internal readonly Guid m_guid; // Activity Guid, it is basically an encoding of the Path() (see CreateActivityPathGuid) internal readonly int m_activityPathGuidOffset; // Keeps track of where in m_guid the causality path stops (used to generated child GUIDs) internal readonly int m_level; // current depth of the Path() of the activity (used to keep recursion under control) readonly internal EventActivityOptions m_eventOptions; // Options passed to start. internal long m_lastChildID; // used to create a unique ID for my children activities internal int m_stopped; // This work item has stopped readonly internal ActivityInfo m_creator; // My parent (creator). Forms the Path() for the activity. + readonly internal Guid m_activityIdToRestore; // The Guid to restore after a stop. #endregion } @@ -530,26 +553,50 @@ private static unsafe void WriteNibble(ref byte* ptr, byte* endPtr, uint value) // with m_current.ActivityID void ActivityChanging(AsyncLocalValueChangedArgs args) { - if (args.PreviousValue == args.CurrentValue) - return; + ActivityInfo cur = args.CurrentValue; + ActivityInfo prev = args.PreviousValue; - if (args.CurrentValue != null) + // Are we popping off a value? (we have a prev, and it creator is cur) + // Then check if we should use the GUID at the time of the start event + if (prev != null && prev.m_creator == cur) { - // Allow subsequent activities inside this thread to automatically get the current activity ID. - EventSource.SetCurrentThreadActivityId(args.CurrentValue.ActivityId); + // If the saved activity ID is not the same as the creator activity + // that takes precedence (it means someone explicitly did a SetActivityID) + // Set it to that and get out + if (cur == null || prev.m_activityIdToRestore != cur.ActivityId) + { + EventSource.SetCurrentThreadActivityId(prev.m_activityIdToRestore); + return; + } } - else - EventSource.SetCurrentThreadActivityId(Guid.Empty); + + // OK we did not have an explicit SetActivityID set. Then we should be + // setting the activity to current ActivityInfo. However that activity + // might be dead, in which case we should skip it, so we never set + // the ID to dead things. + while(cur != null) + { + // We found a live activity (typically the first time), set it to that. + if (cur.m_stopped == 0) + { + EventSource.SetCurrentThreadActivityId(cur.ActivityId); + return; + } + cur = cur.m_creator; + } + // we can get here if there is no information on our activity stack (everything is dead) + // currently we do nothing, as that seems better than setting to Guid.Emtpy. } /// - /// Async local variables have the propery that the are automatically copied whenever a task is created and used + /// Async local variables have the properly that the are automatically copied whenever a task is created and used /// while that task is running. Thus m_current 'flows' to any task that is caused by the current thread that /// last set it. /// /// This variable points a a linked list that represents all Activities that have started but have not stopped. /// AsyncLocal m_current; + bool m_checkedForEnable; // Singleton private static ActivityTracker s_activityTrackerInstance = new ActivityTracker(); @@ -557,8 +604,74 @@ void ActivityChanging(AsyncLocalValueChangedArgs args) // Used to create unique IDs at the top level. Not used for nested Ids (each activity has its own id generator) static long m_nextId = 0; private const ushort MAX_ACTIVITY_DEPTH = 100; // Limit maximum depth of activities to be tracked at 100. - // This will avoid leaking memory in case of activities that are never stopped. + // This will avoid leaking memory in case of activities that are never stopped. #endregion } + +#if ES_BUILD_STANDALONE + /******************************** SUPPORT *****************************/ + /// + /// This is supplied by the framework. It is has the semantics that the value is copied to any new Tasks that is created + /// by the current task. Thus all causally related code gets this value. Note that reads and writes to this VARIABLE + /// (not what it points it) to this does not need to be protected by locks because it is inherently thread local (you always + /// only get your thread local copy which means that you never have ----s. + /// + /// + [EventSource(Name="Microsoft.Tasks.Nuget")] + internal class TplEtwProvider : EventSource + { + public class Keywords + { + public const EventKeywords Debug = (EventKeywords) 1; + } + + public static TplEtwProvider Log = new TplEtwProvider(); + public bool Debug { get { return IsEnabled(EventLevel.Verbose, Keywords.Debug); } } + + public void DebugFacilityMessage(string Facility, string Message) { WriteEvent(1, Facility, Message); } + public void DebugFacilityMessage1(string Facility, string Message, string Arg) { WriteEvent(2, Facility, Message, Arg); } + public void SetActivityId(Guid Id) { WriteEvent(3, Id); } + } +#endif + +#if ES_BUILD_AGAINST_DOTNET_V35 || ES_BUILD_PCL || NO_ASYNC_LOCAL + + internal sealed class AsyncLocalValueChangedArgs + { + public AsyncLocalValueChangedArgs() + { + } + + public T PreviousValue { get { return default(T); } } + public T CurrentValue { get { return default(T); } } + + } + + internal sealed class AsyncLocal + { + public AsyncLocal() + { + } + + public AsyncLocal(Action> valueChangedHandler) + { + + } + + public T Value + { + get + { + object obj = null; // + return (obj == null) ? default(T) : (T)obj; + } + set + { + // + } + } + } +#endif + } diff --git a/mscorlib/system/diagnostics/eventing/eventprovider.cs b/mscorlib/system/diagnostics/eventing/eventprovider.cs index 8a3fec6e6..939d15ee5 100644 --- a/mscorlib/system/diagnostics/eventing/eventprovider.cs +++ b/mscorlib/system/diagnostics/eventing/eventprovider.cs @@ -91,7 +91,7 @@ internal SessionInfo(int sessionIdBit_, int etwSessionId_) private static WriteEventErrorCode s_returnCode; // The last return code private const int s_basicTypeAllocationBufferSize = 16; - private const int s_etwMaxNumberArguments = 32; + private const int s_etwMaxNumberArguments = 128; private const int s_etwAPIMaxRefObjCount = 8; private const int s_maxEventDataDescriptors = 128; private const int s_traceEventMaximumSize = 65482; diff --git a/mscorlib/system/diagnostics/eventing/eventsource.cs b/mscorlib/system/diagnostics/eventing/eventsource.cs index a1b518512..9ae566937 100644 --- a/mscorlib/system/diagnostics/eventing/eventsource.cs +++ b/mscorlib/system/diagnostics/eventing/eventsource.cs @@ -1,5 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved -// Copyright (c) Microsoft Corporation. All rights reserved +// Copyright (c) Microsoft Corporation. All rights reserved // This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in. // It is available from http://www.codeplex.com/hyperAddin #define FEATURE_MANAGED_ETW @@ -13,7 +12,7 @@ // #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS #endif -/* DESIGN NOTES DESIGN NOTES DESIGN NOTES DESIGN NOTES */ +/* DESIGN NOTES DESIGN NOTES DESIGN NOTES DESIGN NOTES */ // DESIGN NOTES // Over the years EventSource has become more complex and so it is important to understand // the basic structure of the code to insure that it does not grow more complex. @@ -169,6 +168,7 @@ using System; +using System.Runtime.CompilerServices; #if FEATURE_ACTIVITYSAMPLING using System.Collections.Concurrent; #endif @@ -531,6 +531,7 @@ public static void SetCurrentThreadActivityId(Guid activityId) #if FEATURE_ACTIVITYSAMPLING Guid newId = activityId; #endif // FEATURE_ACTIVITYSAMPLING + // We ignore errors to keep with the convention that EventSources do not throw errors. // Note we can't access m_throwOnWrites because this is a static method. if (UnsafeNativeMethods.ManifestEtw.EventActivityIdControl( @@ -594,7 +595,7 @@ public static void SetCurrentThreadActivityId(Guid activityId, out Guid oldActiv /// public static Guid CurrentThreadActivityId { - [System.Security.SecurityCritical] + [System.Security.SecuritySafeCritical] get { // We ignore errors to keep with the convention that EventSources do not throw @@ -680,6 +681,30 @@ public string GetTrait(string key) /// public override string ToString() { return Environment.GetResourceString("EventSource_ToString", Name, Guid); } + /// + /// Fires when a Command (e.g. Enable) comes from a an EventListener. + /// + public event EventHandler EventCommandExecuted + { + add + { + m_eventCommandExecuted += value; + + // If we have an EventHandler attached to the EventSource before the first command arrives + // It should get a chance to handle the deferred commands. + EventCommandEventArgs deferredCommands = m_deferredCommands; + while (deferredCommands != null) + { + value(this, deferredCommands); + deferredCommands = deferredCommands.nextCommand; + } + } + remove + { + m_eventCommandExecuted -= value; + } + } + #region protected /// /// This is the constructor that most users will use to create their eventSource. It takes @@ -1152,7 +1177,7 @@ protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* rel { Contract.Assert(m_eventData != null); // You must have initialized this if you enabled the source. if (relatedActivityId != null) - ValidateEventOpcodeForTransfer(ref m_eventData[eventId]); + ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name); #if FEATURE_MANAGED_ETW if (m_eventData[eventId].EnabledForETW) @@ -1202,7 +1227,7 @@ protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* rel // mask set to 0x0f so, when all ETW sessions want the event we don't need to // synthesize a new one if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data)) - ThrowEventSourceException(); + ThrowEventSourceException(m_eventData[eventId].Name); } else { @@ -1222,7 +1247,7 @@ protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* rel unchecked((long)etwSessions.ToEventKeywords() | origKwd)); if (!m_provider.WriteEvent(ref desc, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data)) - ThrowEventSourceException(); + ThrowEventSourceException(m_eventData[eventId].Name); } } else @@ -1252,7 +1277,7 @@ protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* rel if (!SelfDescribingEvents) { if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data)) - ThrowEventSourceException(); + ThrowEventSourceException(m_eventData[eventId].Name); } else { @@ -1286,7 +1311,7 @@ protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* rel if (ex is EventSourceException) throw; else - ThrowEventSourceException(ex); + ThrowEventSourceException(m_eventData[eventId].Name, ex); } } } @@ -1402,6 +1427,7 @@ internal void WriteStringToListener(EventListener listener, string msg, SessionM #endif [SecurityCritical] private unsafe void WriteEventRaw( + string eventName, ref EventDescriptor eventDescriptor, Guid* activityID, Guid* relatedActivityID, @@ -1410,12 +1436,12 @@ private unsafe void WriteEventRaw( { if (m_provider == null) { - ThrowEventSourceException(); + ThrowEventSourceException(eventName); } else { if (!m_provider.WriteEventRaw(ref eventDescriptor, activityID, relatedActivityID, dataCount, data)) - ThrowEventSourceException(); + ThrowEventSourceException(eventName); } } @@ -1516,10 +1542,13 @@ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, str { // If there are any deferred commands, we can do them now. // This is the most likely place for exceptions to happen. - while (m_deferredCommands != null) + // Note that we are NOT resetting m_deferredCommands to NULL here, + // We are giving for EventHandler that will be attached later + EventCommandEventArgs deferredCommands = m_deferredCommands; + while (deferredCommands != null) { - DoCommand(m_deferredCommands); // This can never throw, it catches them and reports the errors. - m_deferredCommands = m_deferredCommands.nextCommand; + DoCommand(deferredCommands); // This can never throw, it catches them and reports the errors. + deferredCommands = deferredCommands.nextCommand; } } } @@ -1817,7 +1846,7 @@ private unsafe object DecodeObject(int eventId, int parameterId, ref EventSource dataPointer = data->DataPointer; data++; for (int i = 0; i < cbSize; ++i) - blob[i] = *((byte*)dataPointer); + blob[i] = *((byte*)dataPointer + i); return blob; } else if (dataType == typeof(byte*)) @@ -1863,8 +1892,24 @@ private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object { Contract.Assert(m_eventData != null); // You must have initialized this if you enabled the source. if (childActivityID != null) - ValidateEventOpcodeForTransfer(ref m_eventData[eventId]); - + { + ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name); + + // If you use WriteEventWithRelatedActivityID you MUST declare the first argument to be a GUID + // with the name 'relatedActivityID, and NOT pass this argument to the WriteEvent method. + // During manifest creation we modify the ParameterInfo[] that we store to strip out any + // first parameter that is of type Guid and named "relatedActivityId." Thus, if you call + // WriteEventWithRelatedActivityID from a method that doesn't name its first parameter correctly + // we can end up in a state where the ParameterInfo[] doesn't have its first parameter stripped, + // and this leads to a mismatch between the number of arguments and the number of ParameterInfos, + // which would cause a cryptic IndexOutOfRangeException later if we don't catch it here. + if (!m_eventData[eventId].HasRelatedActivityID) + { + throw new ArgumentException(Environment.GetResourceString("EventSource_NoRelatedActivityId")); + } + } + + LogEventArgsMismatches(m_eventData[eventId].Parameters, args); #if FEATURE_MANAGED_ETW if (m_eventData[eventId].EnabledForETW) { @@ -1909,7 +1954,7 @@ private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object // mask set to 0x0f so, when all ETW sessions want the event we don't need to // synthesize a new one if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args)) - ThrowEventSourceException(); + ThrowEventSourceException(m_eventData[eventId].Name); } else { @@ -1926,7 +1971,7 @@ private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object unchecked((long)(ulong)etwSessions | origKwd)); if (!m_provider.WriteEvent(ref desc, pActivityId, childActivityID, args)) - ThrowEventSourceException(); + ThrowEventSourceException(m_eventData[eventId].Name); } } else @@ -1956,7 +2001,7 @@ private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object if (!SelfDescribingEvents) { if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args)) - ThrowEventSourceException(); + ThrowEventSourceException(m_eventData[eventId].Name); } else { @@ -2003,7 +2048,7 @@ private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object if (ex is EventSourceException) throw; else - ThrowEventSourceException(ex); + ThrowEventSourceException(m_eventData[eventId].Name, ex); } } } @@ -2027,11 +2072,71 @@ unsafe private object[] SerializeEventArgs(int eventId, object[] args) return eventData; } + /// + /// We expect that the arguments to the Event method and the arguments to WriteEvent match. This function + /// checks that they in fact match and logs a warning to the debugger if they don't. + /// + /// + /// + private void LogEventArgsMismatches(ParameterInfo[] infos, object[] args) + { + // It would be nice to have this on PCL builds, but it would be pointless since there isn't support for + // writing to the debugger log on PCL. + bool typesMatch = args.Length == infos.Length; + + int i = 0; + while (typesMatch && i < args.Length) + { + Type pType = infos[i].ParameterType; + + // Checking to see if the Parameter types (from the Event method) match the supplied argument types. + // Fail if one of two things hold : either the argument type is not equal to the parameter type, or the + // argument is null and the parameter type is non-nullable. + if ((args[i] != null && (args[i].GetType() != pType)) + || (args[i] == null && (!(pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>)))) + ) + { + typesMatch = false; + break; + } + + ++i; + } + + if (!typesMatch) + { + System.Diagnostics.Debugger.Log(0, null, Environment.GetResourceString("EventSource_VarArgsParameterMismatch") + "\r\n"); + } + } + + private int GetParamLengthIncludingByteArray(ParameterInfo[] parameters) + { + int sum = 0; + foreach(ParameterInfo info in parameters) + { + if(info.ParameterType == typeof(byte[])) + { + sum += 2; + } + else + { + sum++; + } + } + + return sum; + } + [SecurityCritical] unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data) { + // We represent a byte[] as a integer denoting the length and then a blob of bytes in the data pointer. This causes a spurious + // warning because eventDataCount is off by one for the byte[] case since a byte[] has 2 items associated it. So we want to check + // that the number of parameters is correct against the byte[] case, but also we the args array would be one too long if + // we just used the modifiedParamCount here -- so we need both. int paramCount = m_eventData[eventId].Parameters.Length; - if (eventDataCount != paramCount) + int modifiedParamCount = GetParamLengthIncludingByteArray(m_eventData[eventId].Parameters); + if (eventDataCount != modifiedParamCount) { ReportOutOfBandMessage(Environment.GetResourceString("EventSource_EventParametersMismatch", eventId, eventDataCount, paramCount), true); paramCount = Math.Min(paramCount, eventDataCount); @@ -2057,11 +2162,11 @@ unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, para eventCallbackArgs.Message = m_eventData[eventId].Message; eventCallbackArgs.Payload = new ReadOnlyCollection(args); - DisptachToAllListeners(eventId, childActivityID, eventCallbackArgs); + DispatchToAllListeners(eventId, childActivityID, eventCallbackArgs); } [SecurityCritical] - private unsafe void DisptachToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs) + private unsafe void DispatchToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs) { Exception lastThrownException = null; for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next) @@ -2304,9 +2409,9 @@ private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeyword } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - private void ThrowEventSourceException(Exception innerEx = null) + private void ThrowEventSourceException(string eventName, Exception innerEx = null) { - // If we fail during ouf of band logging we may end up trying + // If we fail during out of band logging we may end up trying // to throw another EventSourceException, thus hitting a StackOverflowException. // Avoid StackOverflow by making sure we do not recursively call this method. if (m_EventSourceExceptionRecurenceCount > 0) @@ -2315,30 +2420,36 @@ private void ThrowEventSourceException(Exception innerEx = null) { m_EventSourceExceptionRecurenceCount++; + string errorPrefix = "EventSourceException"; + if(eventName != null) + { + errorPrefix += " while processing event \"" + eventName + "\""; + } + // switch (EventProvider.GetLastWriteEventError()) { case EventProvider.WriteEventErrorCode.EventTooBig: - ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_EventTooBig"), true); + ReportOutOfBandMessage(errorPrefix + ": " + Environment.GetResourceString("EventSource_EventTooBig"), true); if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_EventTooBig"), innerEx); break; case EventProvider.WriteEventErrorCode.NoFreeBuffers: - ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_NoFreeBuffers"), true); + ReportOutOfBandMessage(errorPrefix + ": " + Environment.GetResourceString("EventSource_NoFreeBuffers"), true); if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_NoFreeBuffers"), innerEx); break; case EventProvider.WriteEventErrorCode.NullInput: - ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_NullInput"), true); + ReportOutOfBandMessage(errorPrefix + ": " + Environment.GetResourceString("EventSource_NullInput"), true); if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_NullInput"), innerEx); break; case EventProvider.WriteEventErrorCode.TooManyArgs: - ReportOutOfBandMessage("EventSourceException: " + Environment.GetResourceString("EventSource_TooManyArgs"), true); + ReportOutOfBandMessage(errorPrefix + ": " + Environment.GetResourceString("EventSource_TooManyArgs"), true); if (ThrowOnEventWriteErrors) throw new EventSourceException(Environment.GetResourceString("EventSource_TooManyArgs"), innerEx); break; default: if (innerEx != null) - ReportOutOfBandMessage("EventSourceException: " + innerEx.GetType() + ":" + innerEx.Message, true); + ReportOutOfBandMessage(errorPrefix + ": " + innerEx.GetType() + ":" + innerEx.Message, true); else - ReportOutOfBandMessage("EventSourceException", true); + ReportOutOfBandMessage(errorPrefix, true); if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx); break; } @@ -2349,18 +2460,20 @@ private void ThrowEventSourceException(Exception innerEx = null) } } - private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData) + private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData, string eventName) { if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send && - (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive) + (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive && + (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Start) + { - ThrowEventSourceException(); + ThrowEventSourceException(eventName); } } internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string eventName) { - if (opcode == EventOpcode.Info) + if (opcode == EventOpcode.Info && eventName != null) { if (eventName.EndsWith(s_ActivityStartSuffix)) { @@ -2408,6 +2521,8 @@ internal struct EventMetadata public EventTags Tags; public bool EnabledForAnyListener; // true if any dispatcher has this event turned on public bool EnabledForETW; // is this event on for the OS ETW data dispatcher? + + public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId' #if !FEATURE_ACTIVITYSAMPLING #pragma warning disable 0649 #endif @@ -2457,8 +2572,13 @@ internal void SendCommand(EventListener listener, int perEventSourceSessionId, i var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword); lock (EventListener.EventListenersLock) { - if (m_completelyInited) // We are fully initialized, do the command + if (m_completelyInited) + { + // After the first command arrive after construction, we are ready to get rid of the deferred commands + this.m_deferredCommands = null; + // We are fully initialized, do the command DoCommand(commandArgs); + } else { // We can't do the command, simply remember it and we do it when we are fully constructed. @@ -2603,9 +2723,12 @@ internal void DoCommand(EventCommandEventArgs commandArgs) Contract.Assert(m_eventData != null); m_eventSourceEnabled = true; } - + this.OnEventCommand(commandArgs); - + var eventCommandCallback = this.m_eventCommandExecuted; + if (eventCommandCallback != null) + eventCommandCallback(this, commandArgs); + #if FEATURE_ACTIVITYSAMPLING if (commandArgs.listener == null && !bSessionEnable && commandArgs.perEventSourceSessionId != -1) { @@ -2688,8 +2811,11 @@ internal void DoCommand(EventCommandEventArgs commandArgs) // Contract.Assert(enable == true); // Contract.Assert(level == EventLevel.LogAlways); // Contract.Assert(matchAnyKeyword == EventKeywords.None); - + this.OnEventCommand(commandArgs); + var eventCommandCallback = m_eventCommandExecuted; + if (eventCommandCallback != null) + eventCommandCallback(this, commandArgs); } #if FEATURE_ACTIVITYSAMPLING @@ -2976,7 +3102,7 @@ private unsafe bool SendManifest(byte[] rawManifest) } success = false; if (ThrowOnEventWriteErrors) - ThrowEventSourceException(); + ThrowEventSourceException("SendManifest"); break; } } @@ -3213,7 +3339,7 @@ private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string manifest.AddKeyword("Session0", (long)0x8000 << 32); } - if (eventSourceType.Name != "EventSource") + if (eventSourceType != typeof(EventSource)) { for (int i = 0; i < methods.Length; i++) { @@ -3222,6 +3348,20 @@ private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string // Get the EventDescriptor (from the Custom attributes) EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute), flags); + + // Visual Studio online bug #222067 - we can't add a dependency in System.Web on EventSource features that + // didn't exist in 4.5. We have to manually set the disable flag here since the ActivityOptions + // falls in to that category. + // + // The check for <= 3 is to only disable Activity tracking for the RequestStarted and RequestCompleted + // events. + if (eventAttribute != null + && source != null + && eventAttribute.EventId <= 3 + && source.Guid.Equals(AspNetEventSourceGuid)) + { + eventAttribute.ActivityOptions |= EventActivityOptions.Disable; + } // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for // the only reason of limiting the number of methods considered to be events. This broke a common @@ -3273,15 +3413,17 @@ private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventId++; string eventName = method.Name; - if (!eventAttribute.IsOpcodeSet) + if (eventAttribute.Opcode == EventOpcode.Info) // We are still using the default opcode. { // By default pick a task ID derived from the EventID, starting with the highest task number and working back bool noTask = (eventAttribute.Task == EventTask.None); - if (eventAttribute.Task == EventTask.None) + if (noTask) eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId); - // pick a default opcode (either Info or start or stop if the name ends with that suffix. - eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName); + // Unless we explicitly set the opcode to Info (to override the auto-generate of Start or Stop opcodes, + // pick a default opcode based on the event name (either Info or start or stop if the name ends with that suffix). + if (!eventAttribute.IsOpcodeSet) + eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName); // Make the stop opcode have the same task as the start opcode. if (noTask) @@ -3299,7 +3441,7 @@ private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string } else if (eventAttribute.Opcode == EventOpcode.Stop) { - // Find the start associated with this stop event. We require start to be immediately before the stop + // Find the start associated with this stop event. We requre start to be immediately before the stop int startEventId = eventAttribute.EventId - 1; if (eventData != null && startEventId < eventData.Length) { @@ -3325,7 +3467,7 @@ private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string } } - RemoveFirstArgIfRelatedActivityId(ref args); + bool hasRelatedActivityID = RemoveFirstArgIfRelatedActivityId(ref args); if (!(source != null && source.SelfDescribingEvents)) { manifest.StartEvent(eventName, eventAttribute); @@ -3339,7 +3481,7 @@ private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string if (source != null || (flags & EventManifestOptions.Strict) != 0) { // Do checking for user errors (optional, but not a big deal so we do it). - DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest); + DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest, flags); #if FEATURE_MANAGED_ETW_CHANNELS // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only @@ -3357,7 +3499,7 @@ private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string // overwrite inline message with the localized message if (msg != null) eventAttribute.Message = msg; - AddEventDescriptor(ref eventData, eventName, eventAttribute, args); + AddEventDescriptor(ref eventData, eventName, eventAttribute, args, hasRelatedActivityID); } } } @@ -3379,7 +3521,7 @@ private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string { bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0 #if FEATURE_MANAGED_ETW_CHANNELS - || manifest.GetChannelData().Length > 0 + || manifest.GetChannelData().Length > 0 #endif ; @@ -3422,7 +3564,7 @@ private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string return bNeedsManifest ? res : null; } - private static void RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args) + private static bool RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args) { // If the first parameter is (case insensitive) 'relatedActivityId' then skip it. if (args.Length > 0 && args[0].ParameterType == typeof(Guid) && @@ -3431,7 +3573,11 @@ private static void RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args) var newargs = new ParameterInfo[args.Length - 1]; Array.Copy(args, 1, newargs, 0, args.Length - 1); args = newargs; + + return true; } + + return false; } // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField' @@ -3475,7 +3621,8 @@ private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo stat // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it // it is populated if we need to look up message resources private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName, - EventAttribute eventAttribute, ParameterInfo[] eventParameters) + EventAttribute eventAttribute, ParameterInfo[] eventParameters, + bool hasRelatedActivityID) { if (eventData == null || eventData.Length <= eventAttribute.EventId) { @@ -3488,7 +3635,7 @@ private static void AddEventDescriptor(ref EventMetadata[] eventData, string eve eventAttribute.EventId, eventAttribute.Version, #if FEATURE_MANAGED_ETW_CHANNELS - (byte)eventAttribute.Channel, + (byte)eventAttribute.Channel, #else (byte)0, #endif @@ -3502,6 +3649,7 @@ private static void AddEventDescriptor(ref EventMetadata[] eventData, string eve eventData[eventAttribute.EventId].Parameters = eventParameters; eventData[eventAttribute.EventId].Message = eventAttribute.Message; eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions; + eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID; } // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct @@ -3541,7 +3689,7 @@ internal void AddListener(EventListener listener) // index for two distinct events etc. Throws exceptions when it finds something wrong. private static void DebugCheckEvent(ref Dictionary eventsByName, EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute, - ManifestBuilder manifest) + ManifestBuilder manifest, EventManifestOptions options) { int evtId = eventAttribute.EventId; string evtName = method.Name; @@ -3568,14 +3716,30 @@ private static void DebugCheckEvent(ref Dictionary eventsByName, { manifest.ManifestError(Environment.GetResourceString("EventSource_TaskOpcodePairReused", evtName, evtId, eventData[idx].Name, idx)); + + // If we are not strict stop on first error. We have had problems with really large providers taking forever. because of many errors. + if ((options & EventManifestOptions.Strict) == 0) + break; } } // for non-default event opcodes the user must define a task! - if (eventAttribute.Opcode != EventOpcode.Info && - (eventAttribute.Task == EventTask.None || eventAttribute.Task == (EventTask)(0xFFFE - evtId))) + if (eventAttribute.Opcode != EventOpcode.Info) { - manifest.ManifestError(Environment.GetResourceString("EventSource_EventMustHaveTaskIfNonDefaultOpcode", evtName, evtId)); + bool failure = false; + if (eventAttribute.Task == EventTask.None) + failure = true; + else + { + // If you have the auto-assigned Task, then you did not explicitly set one. + // This is OK for Start events because we have special logic to assign the task to a prefix derived from the event name + // But all other cases we want to catch the omission. + var autoAssignedTask = (EventTask)(0xFFFE - evtId); + if ((eventAttribute.Opcode != EventOpcode.Start && eventAttribute.Opcode != EventOpcode.Stop) && eventAttribute.Task == autoAssignedTask) + failure = true; + } + if (failure) + manifest.ManifestError(Environment.GetResourceString("EventSource_EventMustHaveTaskIfNonDefaultOpcode", evtName, evtId)); } // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how: @@ -3591,7 +3755,7 @@ private static void DebugCheckEvent(ref Dictionary eventsByName, eventsByName = new Dictionary(); if (eventsByName.ContainsKey(evtName)) - manifest.ManifestError(Environment.GetResourceString("EventSource_EventNameReused", evtName)); + manifest.ManifestError(Environment.GetResourceString("EventSource_EventNameReused", evtName), true); eventsByName[evtName] = evtName; } @@ -3759,13 +3923,13 @@ internal void ReportOutOfBandMessage(string msg, bool flush) #endif // Send it to all listeners. - if (m_outOfBandMessageCount < 254) // Note this is only if size byte + if (m_outOfBandMessageCount < 16-1) // Note this is only if size byte m_outOfBandMessageCount++; else { - if (m_outOfBandMessageCount == 255) + if (m_outOfBandMessageCount == 16) return; - m_outOfBandMessageCount = 255; // Mark that we hit the limit. Notify them that this is the case. + m_outOfBandMessageCount = 16; // Mark that we hit the limit. Notify them that this is the case. msg = "Reached message limit. End of EventSource error messages."; } @@ -3870,6 +4034,8 @@ private void ReportActivitySamplingInfo(EventListener listener, SessionMask sess internal volatile EventMetadata[] m_eventData; // None per-event data private volatile byte[] m_rawManifest; // Bytes to send out representing the event schema + private EventHandler m_eventCommandExecuted; + private EventSourceSettings m_config; // configuration information // Enabling bits @@ -3919,6 +4085,10 @@ private void ReportActivitySamplingInfo(EventListener listener, SessionMask sess 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB, }; + // Visual Studio Online 222067 - This is only needed for a compatibility hack. We need to check to see + // if an EventSource is the AspNetEventSource to override the ActivityTracking for it. + private static readonly Guid AspNetEventSourceGuid = new Guid("ee799f41-cfa5-550b-bf2c-344747c1c668"); + #endregion } @@ -3990,52 +4160,60 @@ public enum EventSourceSettings /// created. /// /// - public abstract class EventListener : IDisposable + public class EventListener : IDisposable { + private static readonly object s_EventSourceCreatedLock = new object(); + + private event EventHandler _EventSourceCreated; + /// - /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn - /// them on). + /// This event is raised whenever a new eventSource is 'attached' to the dispatcher. + /// This can happen for all existing EventSources when the EventListener is created + /// as well as for any EventSources that come into existence after the EventListener + /// has been created. + /// + /// These 'catch up' events are called during the construction of the EventListener. + /// Subclasses need to be prepared for that. + /// + /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback' + /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued. /// - protected EventListener() - { - lock (EventListenersLock) + public event EventHandler EventSourceCreated + { + add { - // Disallow creating EventListener reentrancy. - if (s_CreatingListener) - throw new InvalidOperationException(Environment.GetResourceString("EventSource_ListenerCreatedInsideCallback")); - - try + lock (s_EventSourceCreatedLock) { - s_CreatingListener = true; - - // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that - // Those added sources see this listener. - this.m_Next = s_Listeners; - s_Listeners = this; - - // Find all existing eventSources call OnEventSourceCreated to 'catchup' - // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback) - // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves - // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source - // is created. - WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray(); - - for (int i = 0; i < eventSourcesSnapshot.Length; i++) - { - WeakReference eventSourceRef = eventSourcesSnapshot[i]; - EventSource eventSource = eventSourceRef.Target as EventSource; - if (eventSource != null) - eventSource.AddListener(this); // This will cause the OnEventSourceCreated callback to fire. - } + CallBackForExistingEventSources(false, value); - Validate(); + this._EventSourceCreated = (EventHandler)Delegate.Combine(_EventSourceCreated, value); } - finally + } + remove + { + lock (s_EventSourceCreatedLock) { - s_CreatingListener = false; + this._EventSourceCreated = (EventHandler)Delegate.Remove(_EventSourceCreated, value); } } } + + /// + /// This event is raised whenever an event has been written by a EventSource for which + /// the EventListener has enabled events. + /// + public event EventHandler EventWritten; + + /// + /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn + /// them on). + /// + public EventListener() + { + // This will cause the OnEventSourceCreated callback to fire. + CallBackForExistingEventSources(true, (obj, args) => args.EventSource.AddListener(this) ); + } + /// /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly @@ -4164,12 +4342,31 @@ public void DisableEvents(EventSource eventSource) /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued. /// /// - internal protected virtual void OnEventSourceCreated(EventSource eventSource) { } + internal protected virtual void OnEventSourceCreated(EventSource eventSource) + { + EventHandler callBack = this._EventSourceCreated; + if(callBack != null) + { + EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs(); + args.EventSource = eventSource; + callBack(this, args); + } + } + /// /// This method is called whenever an event has been written by a EventSource for which /// the EventListener has enabled events. /// - internal protected abstract void OnEventWritten(EventWrittenEventArgs eventData); + /// + internal protected virtual void OnEventWritten(EventWrittenEventArgs eventData) + { + EventHandler callBack = this.EventWritten; + if (callBack != null) + { + callBack(this, eventData); + } + } + /// /// EventSourceIndex is small non-negative integer (suitable for indexing in an array) /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find @@ -4251,11 +4448,14 @@ internal static void AddEventSource(EventSource newEventSource) // See bug 724140 for more private static void DisposeOnShutdown(object sender, EventArgs e) { - foreach (var esRef in s_EventSources) + lock(EventListenersLock) { - EventSource es = esRef.Target as EventSource; - if (es != null) - es.Dispose(); + foreach (var esRef in s_EventSources) + { + EventSource es = esRef.Target as EventSource; + if (es != null) + es.Dispose(); + } } } @@ -4267,6 +4467,9 @@ private static void DisposeOnShutdown(object sender, EventArgs e) /// private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove) { +#if !ES_BUILD_STANDALONE + Contract.Assert(Monitor.IsEntered(EventListener.EventListenersLock)); +#endif // Foreach existing EventSource in the appdomain foreach (WeakReference eventSourceRef in s_EventSources) { @@ -4365,6 +4568,55 @@ internal static object EventListenersLock return s_EventSources; } } + + private void CallBackForExistingEventSources(bool addToListenersList, EventHandler callback) + { + lock (EventListenersLock) + { + // Disallow creating EventListener reentrancy. + if (s_CreatingListener) + throw new InvalidOperationException(Environment.GetResourceString("EventSource_ListenerCreatedInsideCallback")); + + try + { + s_CreatingListener = true; + + if (addToListenersList) + { + // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that + // Those added sources see this listener. + this.m_Next = s_Listeners; + s_Listeners = this; + } + + // Find all existing eventSources call OnEventSourceCreated to 'catchup' + // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback) + // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves + // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source + // is created. + WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray(); + + for (int i = 0; i < eventSourcesSnapshot.Length; i++) + { + WeakReference eventSourceRef = eventSourcesSnapshot[i]; + EventSource eventSource = eventSourceRef.Target as EventSource; + if (eventSource != null) + { + EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs(); + args.EventSource = eventSource; + callback(this, args); + } + } + + Validate(); + } + finally + { + s_CreatingListener = false; + } + } + + } // Instance fields internal volatile EventListener m_Next; // These form a linked list in s_Listeners @@ -4469,6 +4721,21 @@ internal EventCommandEventArgs(EventCommand command, IDictionary #endregion } + /// + /// EventSourceCreatedEventArgs is passed to + /// + public class EventSourceCreatedEventArgs : EventArgs + { + /// + /// The EventSource that is attaching to the listener. + /// + public EventSource EventSource + { + get; + internal set; + } + } + /// /// EventWrittenEventArgs is passed to the user-provided override for /// when an event is fired. @@ -5291,7 +5558,7 @@ public void Dispose() } } - #region private + #region private /// /// Creates a new ActivityFilter that is triggered by 'eventId' from 'source' ever @@ -5460,7 +5727,7 @@ private static ConcurrentDictionary GetActiveActivities( ActivityFilter m_next; // We create a linked list of these Action m_myActivityDelegate; - #endregion + #endregion }; @@ -6212,8 +6479,8 @@ private string CreateManifestString() cultures.Add(CultureInfo.CurrentUICulture); } #if ES_BUILD_STANDALONE - var sortedStrings = new List(stringTab.Keys); - sortedStrings.Sort(); + var sortedStrings = new List(stringTab.Keys); + sortedStrings.Sort(); #else // DD 947936 var sortedStrings = new string[stringTab.Keys.Count]; @@ -6264,10 +6531,13 @@ private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\""); string prevValue; - if (stringTab.TryGetValue(key, out prevValue)) + if (stringTab.TryGetValue(key, out prevValue) && !prevValue.Equals(value)) + { ManifestError(Environment.GetResourceString("EventSource_DuplicateStringKey", key), true); - else - stringTab.Add(key, value); + return; + } + + stringTab[key] = value; } internal string GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat) { @@ -6331,7 +6601,7 @@ private string GetChannelName(EventChannel channel, string eventName, string eve // rest get names Channel. This allows users to modify the Manifest if they want more advanced features. if (channelTab == null) channelTab = new Dictionary(4); - + string channelName = channel.ToString(); // For well know channels this is a nice name, otherwise a number if (EventChannel.Debug < channel) channelName = "Channel" + channelName; // Add a 'Channel' prefix for numbers. diff --git a/mscorlib/system/environment.cs b/mscorlib/system/environment.cs index 5e9b986bd..d5feefac3 100644 --- a/mscorlib/system/environment.cs +++ b/mscorlib/system/environment.cs @@ -416,7 +416,7 @@ public static String SystemDirectory { #if !FEATURE_CORECLR // Do security check - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, path).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, path); #endif return path; diff --git a/mscorlib/system/globalization/taiwancalendar.cs b/mscorlib/system/globalization/taiwancalendar.cs index 45b0322f8..e2f2f3055 100644 --- a/mscorlib/system/globalization/taiwancalendar.cs +++ b/mscorlib/system/globalization/taiwancalendar.cs @@ -21,7 +21,7 @@ namespace System.Globalization { ** Gregorian 1912/01/01 9999/12/31 ** Taiwan 01/01/01 8088/12/31 ============================================================================*/ - + [System.Runtime.InteropServices.ComVisible(true)] [Serializable] public class TaiwanCalendar: Calendar { diff --git a/mscorlib/system/io/__error.cs b/mscorlib/system/io/__error.cs index 48677e89d..f4a47d4a6 100644 --- a/mscorlib/system/io/__error.cs +++ b/mscorlib/system/io/__error.cs @@ -79,28 +79,21 @@ internal static void EndWriteCalledTwice() { [System.Security.SecurityCritical] // auto-generated internal static String GetDisplayablePath(String path, bool isInvalidPath) { - if (String.IsNullOrEmpty(path)) return String.Empty; - // Is it a fully qualified path? - bool isFullyQualified = false; if (path.Length < 2) return path; - if (Path.IsDirectorySeparator(path[0]) && Path.IsDirectorySeparator(path[1])) - isFullyQualified = true; - else if (path[1] == Path.VolumeSeparatorChar) { - isFullyQualified = true; - } - if (!isFullyQualified && !isInvalidPath) + // Return the path as is if we're relative (not fully qualified) and not a bad path + if (PathInternal.IsPartiallyQualified(path) && !isInvalidPath) return path; bool safeToReturn = false; try { if (!isInvalidPath) { #if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { path }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, path, false, false); #endif safeToReturn = true; } diff --git a/mscorlib/system/io/binaryreader.cs b/mscorlib/system/io/binaryreader.cs index 29d8edb83..c33ed1522 100644 --- a/mscorlib/system/io/binaryreader.cs +++ b/mscorlib/system/io/binaryreader.cs @@ -374,10 +374,23 @@ private int InternalReadChars(char[] buffer, int index, int count) { } Contract.Assert(byteBuffer != null, "expected byteBuffer to be non-null"); - unsafe { - fixed (byte* pBytes = byteBuffer) - fixed (char* pChars = buffer) { - charsRead = m_decoder.GetChars(pBytes + position, numBytes, pChars + index, charsRemaining, false); + + checked { + + if (position < 0 || numBytes < 0 || position + numBytes > byteBuffer.Length) { + throw new ArgumentOutOfRangeException("byteCount"); + } + + if (index < 0 || charsRemaining < 0 || index + charsRemaining > buffer.Length) { + throw new ArgumentOutOfRangeException("charsRemaining"); + } + + unsafe { + fixed (byte* pBytes = byteBuffer) { + fixed (char* pChars = buffer) { + charsRead = m_decoder.GetChars(pBytes + position, numBytes, pChars + index, charsRemaining, false); + } + } } } diff --git a/mscorlib/system/io/binarywriter.cs b/mscorlib/system/io/binarywriter.cs index ffbbc49b5..d9406a6f3 100644 --- a/mscorlib/system/io/binarywriter.cs +++ b/mscorlib/system/io/binarywriter.cs @@ -196,7 +196,7 @@ public unsafe virtual void Write(char ch) { Contract.Assert(_encoding.GetMaxByteCount(1) <= 16, "_encoding.GetMaxByteCount(1) <= 16)"); int numBytes = 0; fixed(byte * pBytes = _buffer) { - numBytes = _encoder.GetBytes(&ch, 1, pBytes, 16, true); + numBytes = _encoder.GetBytes(&ch, 1, pBytes, _buffer.Length, true); } OutStream.Write(_buffer, 0, numBytes); } @@ -363,10 +363,10 @@ public unsafe virtual void Write(String value) if (_largeByteBuffer == null) { _largeByteBuffer = new byte[LargeByteBufferSize]; - _maxChars = LargeByteBufferSize / _encoding.GetMaxByteCount(1); + _maxChars = _largeByteBuffer.Length / _encoding.GetMaxByteCount(1); } - if (len <= LargeByteBufferSize) { + if (len <= _largeByteBuffer.Length) { //Contract.Assert(len == _encoding.GetBytes(chars, 0, chars.Length, _largeByteBuffer, 0), "encoding's GetByteCount & GetBytes gave different answers! encoding type: "+_encoding.GetType().Name); _encoding.GetBytes(value, 0, value.Length, _largeByteBuffer, 0); OutStream.Write(_largeByteBuffer, 0, len); @@ -385,14 +385,21 @@ public unsafe virtual void Write(String value) // Figure out how many chars to process this round. int charCount = (numLeft > _maxChars) ? _maxChars : numLeft; int byteLen; - fixed(char* pChars = value) { - fixed(byte* pBytes = _largeByteBuffer) { - byteLen = _encoder.GetBytes(pChars + charStart, charCount, pBytes, LargeByteBufferSize, charCount == numLeft); + + checked { + if (charStart < 0 || charCount < 0 || charStart + charCount > value.Length) { + throw new ArgumentOutOfRangeException("charCount"); + } + + fixed(char* pChars = value) { + fixed(byte* pBytes = _largeByteBuffer) { + byteLen = _encoder.GetBytes(pChars + charStart, charCount, pBytes, _largeByteBuffer.Length, charCount == numLeft); + } } } #if _DEBUG totalBytes += byteLen; - Contract.Assert (totalBytes <= len && byteLen <= LargeByteBufferSize, "BinaryWriter::Write(String) - More bytes encoded than expected!"); + Contract.Assert (totalBytes <= len && byteLen <= _largeByteBuffer.Length, "BinaryWriter::Write(String) - More bytes encoded than expected!"); #endif OutStream.Write(_largeByteBuffer, 0, byteLen); charStart += charCount; diff --git a/mscorlib/system/io/directory.cs b/mscorlib/system/io/directory.cs index da6f5ab57..943e8ff52 100644 --- a/mscorlib/system/io/directory.cs +++ b/mscorlib/system/io/directory.cs @@ -248,17 +248,25 @@ internal unsafe static void InternalCreateDirectory(String fullPath, String path int count = stackDir.Count; - if (stackDir.Count != 0) + if (stackDir.Count != 0 +#if FEATURE_CAS_POLICY + // All demands in full trust domains are no-ops, so skip + // + // The full path went through validity checks by being passed through FileIOPermissions already. + // As a sub string of the full path can't fail the checks if the full path passes. + && !CodeAccessSecurityEngine.QuickCheckForAllDemands() +#endif + ) { - String [] securityList = new String[stackDir.Count]; + String[] securityList = new String[stackDir.Count]; stackDir.CopyTo(securityList, 0); for (int j = 0 ; j < securityList.Length; j++) securityList[j] += "\\."; // leaf will never have a slash at the end // Security check for all directories not present only. -#if !FEATURE_PAL && FEATURE_MACL +#if !FEATURE_PAL && FEATURE_MACL AccessControlActions control = (dirSecurity == null) ? AccessControlActions.None : AccessControlActions.Change; - new FileIOPermission(FileIOPermissionAccess.Write, control, securityList, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, control, securityList, false, false); #else #if FEATURE_CORECLR if (checkHost) @@ -270,7 +278,7 @@ internal unsafe static void InternalCreateDirectory(String fullPath, String path } } #else - new FileIOPermission(FileIOPermissionAccess.Write, securityList, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, securityList, false, false ); #endif #endif //!FEATURE_PAL && FEATURE_MACL } @@ -298,8 +306,9 @@ internal unsafe static void InternalCreateDirectory(String fullPath, String path while (stackDir.Count > 0) { String name = stackDir[stackDir.Count - 1]; stackDir.RemoveAt(stackDir.Count - 1); - if (name.Length >= Path.MAX_DIRECTORY_PATH) + if (PathInternal.IsDirectoryTooLong(name)) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); + r = Win32Native.CreateDirectory(name, secAttrs); if (!r && (firstError == 0)) { int currentError = Marshal.GetLastWin32Error(); @@ -326,7 +335,7 @@ internal unsafe static void InternalCreateDirectory(String fullPath, String path state.EnsureState(); } #else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, GetDemandDir(name, true)).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, GetDemandDir(name, true)); #endif // FEATURE_CORECLR errorString = name; } @@ -1085,49 +1094,88 @@ internal static String UnsafeGetCurrentDirectory() return InternalGetCurrentDirectory(false); } + [System.Security.SecuritySafeCritical] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private static string InternalGetCurrentDirectory(bool checkHost) + { + string currentDirectory = AppContextSwitches.UseLegacyPathHandling ? LegacyGetCurrentDirectory() : NewGetCurrentDirectory(); + string demandPath = GetDemandDir(currentDirectory, true); + +#if FEATURE_CORECLR + if (checkHost) + { + FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPath); + state.EnsureState(); + } +#else + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPath, false, false); +#endif + return currentDirectory; + } + [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] - private static String InternalGetCurrentDirectory(bool checkHost) + private static string LegacyGetCurrentDirectory() { - StringBuilder sb = StringBuilderCache.Acquire(Path.MAX_PATH + 1); + StringBuilder sb = StringBuilderCache.Acquire(PathInternal.MaxShortPath + 1); if (Win32Native.GetCurrentDirectory(sb.Capacity, sb) == 0) __Error.WinIOError(); - String currentDirectory = sb.ToString(); + string currentDirectory = sb.ToString(); // Note that if we have somehow put our command prompt into short // file name mode (ie, by running edlin or a DOS grep, etc), then // this will return a short file name. - if (currentDirectory.IndexOf('~') >= 0) { + if (currentDirectory.IndexOf('~') >= 0) + { int r = Win32Native.GetLongPathName(currentDirectory, sb, sb.Capacity); - if (r == 0 || r >= Path.MAX_PATH) { + if (r == 0 || r >= PathInternal.MaxShortPath) + { int errorCode = Marshal.GetLastWin32Error(); - if (r >= Path.MAX_PATH) + if (r >= PathInternal.MaxShortPath) errorCode = Win32Native.ERROR_FILENAME_EXCED_RANGE; if (errorCode != Win32Native.ERROR_FILE_NOT_FOUND && errorCode != Win32Native.ERROR_PATH_NOT_FOUND && errorCode != Win32Native.ERROR_INVALID_FUNCTION && // by design - enough said. errorCode != Win32Native.ERROR_ACCESS_DENIED) - __Error.WinIOError(errorCode, String.Empty); + __Error.WinIOError(errorCode, string.Empty); } currentDirectory = sb.ToString(); } StringBuilderCache.Release(sb); - String demandPath = GetDemandDir(currentDirectory, true); + return currentDirectory; + } - -#if FEATURE_CORECLR - if (checkHost) + [System.Security.SecurityCritical] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private static string NewGetCurrentDirectory() + { + using (StringBuffer buffer = new StringBuffer(PathInternal.MaxShortPath)) { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPath); - state.EnsureState(); + uint result = 0; + while ((result = Win32Native.GetCurrentDirectoryW(buffer.CharCapacity, buffer.GetHandle())) > buffer.CharCapacity) + { + // Reported size is greater than the buffer size. Increase the capacity. + // The size returned includes the null only if more space is needed (this case). + buffer.EnsureCharCapacity(result); + } + + if (result == 0) + { + __Error.WinIOError(); + } + + buffer.Length = result; + if (buffer.Contains('~')) + { + return LongPathHelper.GetLongPathName(buffer); + } + + return buffer.ToString(); } -#else - new FileIOPermission( FileIOPermissionAccess.PathDiscovery, new String[] { demandPath }, false, false ).Demand(); -#endif - return currentDirectory; } - #if FEATURE_CORECLR [System.Security.SecurityCritical] // auto-generated #else @@ -1136,15 +1184,16 @@ private static String InternalGetCurrentDirectory(bool checkHost) [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] public static void SetCurrentDirectory(String path) - { + { if (path==null) throw new ArgumentNullException("value"); if (path.Length==0) throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty")); Contract.EndContractBlock(); - if (path.Length >= Path.MAX_PATH) + + if (PathInternal.IsPathTooLong(path)) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - + // This will have some large effects on the rest of the runtime // and other appdomains in this process. Demand unmanaged code. #pragma warning disable 618 @@ -1174,19 +1223,19 @@ public static void Move(String sourceDirName,String destDirName) { [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] - internal static void UnsafeMove(String sourceDirName,String destDirName) { + internal static void UnsafeMove(String sourceDirName, String destDirName) { InternalMove(sourceDirName, destDirName, false); } [System.Security.SecurityCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] - private static void InternalMove(String sourceDirName,String destDirName,bool checkHost) { + private static void InternalMove(String sourceDirName, String destDirName, bool checkHost) { if (sourceDirName==null) throw new ArgumentNullException("sourceDirName"); if (sourceDirName.Length==0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "sourceDirName"); - + if (destDirName==null) throw new ArgumentNullException("destDirName"); if (destDirName.Length==0) @@ -1195,16 +1244,16 @@ private static void InternalMove(String sourceDirName,String destDirName,bool ch String fullsourceDirName = Path.GetFullPathInternal(sourceDirName); String sourcePath = GetDemandDir(fullsourceDirName, false); - - if (sourcePath.Length >= Path.MAX_DIRECTORY_PATH) + + if (PathInternal.IsDirectoryTooLong(sourcePath)) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); String fulldestDirName = Path.GetFullPathInternal(destDirName); String destPath = GetDemandDir(fulldestDirName, false); - if (destPath.Length >= Path.MAX_DIRECTORY_PATH) + if (PathInternal.IsDirectoryTooLong(sourcePath)) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - + #if FEATURE_CORECLR if (checkHost) { FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Write | FileSecurityStateAccess.Read, sourceDirName, sourcePath); @@ -1288,7 +1337,7 @@ internal static void Delete(String fullPath, String userPath, bool recursive, bo } #else // Make sure we have write permission to this directory - new FileIOPermission(FileIOPermissionAccess.Write, new String[] { demandPath }, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, demandPath, false, false); #endif // Do not recursively delete through reparse points. Perhaps in a diff --git a/mscorlib/system/io/directoryinfo.cs b/mscorlib/system/io/directoryinfo.cs index 5d88346dd..4a5d733a5 100644 --- a/mscorlib/system/io/directoryinfo.cs +++ b/mscorlib/system/io/directoryinfo.cs @@ -102,7 +102,7 @@ private void Init(String path, bool checkHost) state.EnsureState(); } #else - new FileIOPermission(FileIOPermissionAccess.Read, demandDir, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandDir, false, false); #endif FullPath = fullPath; @@ -130,7 +130,7 @@ private DirectoryInfo(SerializationInfo info, StreamingContext context) : base(i { #if !FEATURE_CORECLR demandDir = new String[] {Directory.GetDemandDir(FullPath, true)}; - new FileIOPermission(FileIOPermissionAccess.Read, demandDir, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandDir, false, false ); #endif DisplayPath = GetDisplayName(OriginalPath, FullPath); } @@ -169,7 +169,7 @@ public DirectoryInfo Parent { FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery | FileSecurityStateAccess.Read, String.Empty, dir.demandDir[0]); state.EnsureState(); #else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, dir.demandDir, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, dir.demandDir, false, false); #endif return dir; } @@ -234,7 +234,7 @@ private DirectoryInfo CreateSubdirectoryHelper(String path, Object directorySecu FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, OriginalPath, demandDirForCreation); state.EnsureState(); #else - new FileIOPermission(FileIOPermissionAccess.Write, new String[] { demandDirForCreation }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, demandDirForCreation, false, false); #endif Directory.InternalCreateDirectory(fullPath, path, directorySecurity); @@ -609,7 +609,7 @@ public DirectoryInfo Root { FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPath); sourceState.EnsureState(); #else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { demandPath }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPath, false, false); #endif return new DirectoryInfo(rootPath); } @@ -629,7 +629,7 @@ public void MoveTo(String destDirName) { FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Write | FileSecurityStateAccess.Read, DisplayPath, Directory.GetDemandDir(FullPath, true)); sourceState.EnsureState(); #else - new FileIOPermission(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, demandDir, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, demandDir, false, false); #endif String fullDestDirName = Path.GetFullPathInternal(destDirName); String demandPath; @@ -648,9 +648,9 @@ public void MoveTo(String destDirName) { FileSecurityState destState = new FileSecurityState(FileSecurityStateAccess.Write, destDirName, demandPath); destState.EnsureState(); #else - new FileIOPermission(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, demandPath).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, demandPath); #endif - + String fullSourcePath; if (FullPath.EndsWith(Path.DirectorySeparatorChar)) fullSourcePath = FullPath; diff --git a/mscorlib/system/io/file.cs b/mscorlib/system/io/file.cs index d8a7146e0..8a36be7ee 100644 --- a/mscorlib/system/io/file.cs +++ b/mscorlib/system/io/file.cs @@ -1322,10 +1322,23 @@ private static void InternalReplace(String sourceFileName, String destinationFil destState.EnsureState(); backupState.EnsureState(); #else - FileIOPermission perm = new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, new String[] { fullSrcPath, fullDestPath}); - if (destinationBackupFileName != null) - perm.AddPathList(FileIOPermissionAccess.Write, fullBackupPath); - perm.Demand(); +#if FEATURE_CAS_POLICY + // All demands in full trust domains are no-ops, just do additional checks + if (CodeAccessSecurityEngine.QuickCheckForAllDemands()) + { + FileIOPermission.EmulateFileIOPermissionChecks(fullSrcPath); + FileIOPermission.EmulateFileIOPermissionChecks(fullDestPath); + if (fullBackupPath != null) + FileIOPermission.EmulateFileIOPermissionChecks(fullBackupPath); + } + else +#endif + { + FileIOPermission perm = new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, new String[] { fullSrcPath, fullDestPath }); + if (fullBackupPath != null) + perm.AddPathList(FileIOPermissionAccess.Write, fullBackupPath); + perm.Demand(); + } #endif int flags = Win32Native.REPLACEFILE_WRITE_THROUGH; diff --git a/mscorlib/system/io/fileinfo.cs b/mscorlib/system/io/fileinfo.cs index 21cca9f51..1148997f4 100644 --- a/mscorlib/system/io/fileinfo.cs +++ b/mscorlib/system/io/fileinfo.cs @@ -129,7 +129,7 @@ private String GetDisplayPath(String originalPath) private FileInfo(SerializationInfo info, StreamingContext context) : base(info, context) { #if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.Read, new String[] { FullPath }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, FullPath, false, false); #endif _name = Path.GetFileName(OriginalPath); DisplayPath = GetDisplayPath(OriginalPath); @@ -181,7 +181,7 @@ public String DirectoryName FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, DisplayPath, FullPath); state.EnsureState(); #else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { directoryName }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, directoryName, false, false); #endif } return directoryName; @@ -331,7 +331,7 @@ public override void Delete() state.EnsureState(); #else // For security check, path should be resolved to an absolute path. - new FileIOPermission(FileIOPermissionAccess.Write, new String[] { FullPath }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, FullPath, false, false); #endif bool r = Win32Native.DeleteFile(FullPath); @@ -458,7 +458,7 @@ public void MoveTo(String destFileName) { sourceState.EnsureState(); destState.EnsureState(); #else - new FileIOPermission(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, new String[] { FullPath }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, FullPath, false, false); FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, fullDestFileName, false, false); #endif diff --git a/mscorlib/system/io/filestream.cs b/mscorlib/system/io/filestream.cs index 38b867352..7e47b8bed 100644 --- a/mscorlib/system/io/filestream.cs +++ b/mscorlib/system/io/filestream.cs @@ -708,29 +708,26 @@ private void Init(String path, FileMode mode, FileAccess access, int rights, boo // Get absolute path - Security needs this to prevent something // like trying to create a file in c:\tmp with the name // "..\WinNT\System32\ntoskrnl.exe". Store it for user convenience. - int maxPath = useLongPath ? Path.MaxLongPath : Path.MaxPath; - String filePath = Path.NormalizePath(path, true, maxPath); + int maxPath = useLongPath + ? PathInternal.MaxLongPath + : AppContextSwitches.BlockLongPaths ? PathInternal.MaxShortPath : PathInternal.MaxLongPath; + + string filePath = Path.NormalizePath(path, true, maxPath); _fileName = filePath; // Prevent access to your disk drives as raw block devices. - if (filePath.StartsWith("\\\\.\\", StringComparison.Ordinal)) + // + // We'll allow if in full trust and not in legacy mode. You can also get device access via \\?\ and \??\ so there isn't + // much point in making users jump through these hoops. Blocking is pointless in full trust as well. + if ( +#if FEATURE_CAS_POLICY + (!CodeAccessSecurityEngine.QuickCheckForAllDemands() || AppContextSwitches.UseLegacyPathHandling) + && +#endif + filePath.StartsWith("\\\\.\\", StringComparison.Ordinal)) throw new ArgumentException(Environment.GetResourceString("Arg_DevicesNotSupported")); - // In 4.0, we always construct a FileIOPermission object below. - // If filePath contained a ':', we would throw a NotSupportedException in - // System.Security.Util.StringExpressionSet.CanonicalizePath. - // If filePath contained other illegal characters, we would throw an ArgumentException in - // FileIOPermission.CheckIllegalCharacters. - // In 4.5 we on longer construct the FileIOPermission object in full trust. - // To preserve the 4.0 behavior we do an explicit check for ':' here and also call Path.CheckInvalidPathChars. - // Note that we need to call CheckInvalidPathChars before checking for ':' because that is what FileIOPermission does. - - Path.CheckInvalidPathChars(filePath, true); - - if (filePath.IndexOf( ':', 2 ) != -1) - throw new NotSupportedException( Environment.GetResourceString( "Argument_PathFormatNotSupported" ) ); - bool read = false; #if FEATURE_MACL @@ -747,7 +744,12 @@ private void Init(String path, FileMode mode, FileAccess access, int rights, boo // All demands in full trust domains are no-ops, so skip #if FEATURE_CAS_POLICY - if (!CodeAccessSecurityEngine.QuickCheckForAllDemands()) + if (CodeAccessSecurityEngine.QuickCheckForAllDemands()) + { + // Need to throw the same exceptions that are thrown if we actually called QuickDemand() below. + FileIOPermission.EmulateFileIOPermissionChecks(filePath); + } + else #endif // FEATURE_CAS_POLICY { // Build up security permissions required, as well as validate we @@ -796,7 +798,7 @@ private void Init(String path, FileMode mode, FileAccess access, int rights, boo } AccessControlActions control = specifiedAcl ? AccessControlActions.Change : AccessControlActions.None; - new FileIOPermission(secAccess, control, new String[] { filePath }, false, false).Demand(); + FileIOPermission.QuickDemand(secAccess, control, new String[] { filePath }, false, false); #else #if FEATURE_CORECLR if (checkHost) { @@ -804,7 +806,7 @@ private void Init(String path, FileMode mode, FileAccess access, int rights, boo state.EnsureState(); } #else - new FileIOPermission(secAccess, new String[] { filePath }, false, false).Demand(); + FileIOPermission.QuickDemand(secAccess, filePath, false, false); #endif // FEATURE_CORECLR #endif } @@ -867,7 +869,7 @@ private void Init(String path, FileMode mode, FileAccess access, int rights, boo { try { #if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { _fileName }, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, _fileName, false, false); #endif canGiveFullPath = true; } @@ -1202,7 +1204,7 @@ public String Name { FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, _fileName); sourceState.EnsureState(); #else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { _fileName }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, _fileName, false, false); #endif return _fileName; } diff --git a/mscorlib/system/io/filesystemenumerable.cs b/mscorlib/system/io/filesystemenumerable.cs index ec69c33af..5acaf13e8 100644 --- a/mscorlib/system/io/filesystemenumerable.cs +++ b/mscorlib/system/io/filesystemenumerable.cs @@ -234,7 +234,7 @@ internal FileSystemEnumerableIterator(String path, String originalUserPath, Stri state2.EnsureState(); } #else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false); #endif // normalize search criteria @@ -340,7 +340,7 @@ private FileSystemEnumerableIterator(String fullPath, String normalizedSearchPat state2.EnsureState(); } #else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false); #endif searchData = new Directory.SearchData(normalizedSearchPath, userPath, searchOption); CommonInit(); @@ -591,8 +591,7 @@ internal void DoDemand(String fullPathToDemand) } #else String demandDir = Directory.GetDemandDir(fullPathToDemand, true); - String[] demandPaths = new String[] { demandDir }; - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandDir, false, false); #endif } @@ -706,8 +705,7 @@ internal override FileInfo CreateObject(SearchResult result) FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, name); state.EnsureState(); #else - String[] names = new String[] { name }; - new FileIOPermission(FileIOPermissionAccess.Read, names, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, name, false, false); #endif FileInfo fi = new FileInfo(name, false); fi.InitializeFrom(result.FindData); @@ -733,8 +731,7 @@ internal override DirectoryInfo CreateObject(SearchResult result) FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, permissionName); state.EnsureState(); #else - String[] permissionNames = new String[] { permissionName }; - new FileIOPermission(FileIOPermissionAccess.Read, permissionNames, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, permissionName, false, false); #endif DirectoryInfo di = new DirectoryInfo(name, false); di.InitializeFrom(result.FindData); @@ -770,8 +767,7 @@ internal override FileSystemInfo CreateObject(SearchResult result) FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, permissionName); state.EnsureState(); #else - String[] permissionNames = new String[] { permissionName }; - new FileIOPermission(FileIOPermissionAccess.Read, permissionNames, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, permissionName, false, false); #endif DirectoryInfo di = new DirectoryInfo(name, false); di.InitializeFrom(result.FindData); @@ -786,8 +782,7 @@ internal override FileSystemInfo CreateObject(SearchResult result) FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, name); state.EnsureState(); #else - String[] names = new String[] { name }; - new FileIOPermission(FileIOPermissionAccess.Read, names, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, name, false, false); #endif FileInfo fi = new FileInfo(name, false); fi.InitializeFrom(result.FindData); diff --git a/mscorlib/system/io/filesysteminfo.cs b/mscorlib/system/io/filesysteminfo.cs index 6428bc0c8..557ab1be8 100644 --- a/mscorlib/system/io/filesysteminfo.cs +++ b/mscorlib/system/io/filesysteminfo.cs @@ -101,7 +101,7 @@ public virtual String FullName { FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandDir); sourceState.EnsureState(); #else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandDir).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandDir); #endif return FullPath; } @@ -118,7 +118,7 @@ internal virtual String UnsafeGetFullName else demandDir = FullPath; #if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandDir).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandDir); #endif return FullPath; } @@ -324,7 +324,7 @@ public FileAttributes Attributes { #endif set { #if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.Write, FullPath).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, FullPath); #endif bool r = Win32Native.SetFileAttributes(FullPath, (int) value); if (!r) { @@ -349,7 +349,7 @@ public FileAttributes Attributes { public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { #if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, FullPath).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, FullPath); #endif info.AddValue("OriginalPath", OriginalPath, typeof(String)); diff --git a/mscorlib/system/io/longpath.cs b/mscorlib/system/io/longpath.cs index 2240ba9e1..7b863b95c 100644 --- a/mscorlib/system/io/longpath.cs +++ b/mscorlib/system/io/longpath.cs @@ -195,9 +195,9 @@ internal static void Copy(String sourceFileName, String destFileName, bool overw Contract.Requires(destFileName.Length > 0); String fullSourceFileName = LongPath.NormalizePath(sourceFileName); - new FileIOPermission(FileIOPermissionAccess.Read, new String[] { fullSourceFileName }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullSourceFileName, false, false); String fullDestFileName = LongPath.NormalizePath(destFileName); - new FileIOPermission(FileIOPermissionAccess.Write, new String[] { fullDestFileName }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, fullDestFileName, false, false); InternalCopy(fullSourceFileName, fullDestFileName, sourceFileName, destFileName, overwrite); } @@ -258,7 +258,7 @@ internal static void Delete(String path) { String fullPath = LongPath.NormalizePath(path); // For security check, path should be resolved to an absolute path. - new FileIOPermission(FileIOPermissionAccess.Write, new String[] { fullPath }, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, fullPath, false, false); String tempPath = Path.AddLongPathPrefix(fullPath); bool r = Win32Native.DeleteFile(tempPath); @@ -297,8 +297,8 @@ internal static bool Exists(String path) { if (path.Length > 0 && Path.IsDirectorySeparator(path[path.Length - 1])) { return false; } - - new FileIOPermission(FileIOPermissionAccess.Read, new String[] { path }, false, false ).Demand(); + + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, path, false, false ); return InternalExists(path); } @@ -326,7 +326,7 @@ internal static DateTimeOffset GetCreationTime(String path) Contract.Requires(path != null); String fullPath = LongPath.NormalizePath(path); - new FileIOPermission(FileIOPermissionAccess.Read, new String[] { fullPath }, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullPath, false, false); String tempPath = Path.AddLongPathPrefix(fullPath); @@ -348,7 +348,7 @@ internal static DateTimeOffset GetLastAccessTime(String path) Contract.Requires(path != null); String fullPath = LongPath.NormalizePath(path); - new FileIOPermission(FileIOPermissionAccess.Read, new String[] { fullPath }, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullPath, false, false); String tempPath = Path.AddLongPathPrefix(fullPath); Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); @@ -369,7 +369,7 @@ internal static DateTimeOffset GetLastWriteTime(String path) Contract.Requires(path != null); String fullPath = LongPath.NormalizePath(path); - new FileIOPermission(FileIOPermissionAccess.Read, new String[] { fullPath }, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullPath, false, false); String tempPath = Path.AddLongPathPrefix(fullPath); Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); @@ -400,9 +400,9 @@ internal static void Move(String sourceFileName, String destFileName) { Contract.Requires(destFileName.Length > 0); String fullSourceFileName = LongPath.NormalizePath(sourceFileName); - new FileIOPermission(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, new String[] { fullSourceFileName }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, fullSourceFileName, false, false); String fullDestFileName = LongPath.NormalizePath(destFileName); - new FileIOPermission(FileIOPermissionAccess.Write, new String[] { fullDestFileName }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, fullDestFileName, false, false); if (!LongPathFile.InternalExists(fullSourceFileName)) __Error.WinIOError(Win32Native.ERROR_FILE_NOT_FOUND, fullSourceFileName); @@ -423,7 +423,7 @@ internal static long GetLength(String path) Contract.Requires(path != null); String fullPath = LongPath.NormalizePath(path); - new FileIOPermission(FileIOPermissionAccess.Read, new String[] { fullPath }, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullPath, false, false); String tempPath = Path.AddLongPathPrefix(fullPath); Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); @@ -435,7 +435,6 @@ internal static long GetLength(String path) __Error.WinIOError(Win32Native.ERROR_FILE_NOT_FOUND, path); return ((long)data.fileSizeHigh) << 32 | ((long)data.fileSizeLow & 0xFFFFFFFFL); - } // Defined in WinError.h @@ -460,7 +459,7 @@ internal static void CreateDirectory(String path) // We attempt to create directories only after all the security checks have passed. This is avoid doing // a demand at every level. String demandDir = GetDemandDir(fullPath, true); - new FileIOPermission(FileIOPermissionAccess.Read, new String[] { demandDir }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandDir, false, false); InternalCreateDirectory(fullPath, path, null); } @@ -514,7 +513,15 @@ private unsafe static void InternalCreateDirectory(String fullPath, String path, int count = stackDir.Count; - if (stackDir.Count != 0) + if (stackDir.Count != 0 +#if FEATURE_CAS_POLICY + // All demands in full trust domains are no-ops, so skip + // + // The full path went through validity checks by being passed through FileIOPermissions already. + // As a sub string of the full path can't fail the checks if the full path passes. + && !CodeAccessSecurityEngine.QuickCheckForAllDemands() +#endif + ) { String[] securityList = new String[stackDir.Count]; stackDir.CopyTo(securityList, 0); @@ -524,9 +531,9 @@ private unsafe static void InternalCreateDirectory(String fullPath, String path, // Security check for all directories not present only. #if !FEATURE_PAL && FEATURE_MACL AccessControlActions control = (dirSecurity == null) ? AccessControlActions.None : AccessControlActions.Change; - new FileIOPermission(FileIOPermissionAccess.Write, control, securityList, false, false ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, control, securityList, false, false); #else - new FileIOPermission(FileIOPermissionAccess.Write, securityList, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, securityList, false, false); #endif } @@ -554,9 +561,11 @@ private unsafe static void InternalCreateDirectory(String fullPath, String path, { String name = stackDir[stackDir.Count - 1]; stackDir.RemoveAt(stackDir.Count - 1); + if (name.Length >= Path.MaxLongPath) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - r = Win32Native.CreateDirectory(Path.AddLongPathPrefix(name), secAttrs); + + r = Win32Native.CreateDirectory(PathInternal.EnsureExtendedPrefix(name), secAttrs); if (!r && (firstError == 0)) { int currentError = Marshal.GetLastWin32Error(); @@ -579,7 +588,7 @@ private unsafe static void InternalCreateDirectory(String fullPath, String path, // Give the user a nice error message, but don't leak path information. try { - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { GetDemandDir(name, true) }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, GetDemandDir(name, true), false, false); errorString = name; } catch (SecurityException) { } @@ -631,8 +640,8 @@ internal static void Move(String sourceDirName, String destDirName) if (destPath.Length >= Path.MaxLongPath) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - new FileIOPermission(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, new String[] { sourcePath }, false, false).Demand(); - new FileIOPermission(FileIOPermissionAccess.Write, new String[] { destPath }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, sourcePath, false, false); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, destPath, false, false); if (String.Compare(sourcePath, destPath, StringComparison.OrdinalIgnoreCase) == 0) throw new IOException(Environment.GetResourceString("IO.IO_SourceDestMustBeDifferent")); @@ -643,8 +652,8 @@ internal static void Move(String sourceDirName, String destDirName) throw new IOException(Environment.GetResourceString("IO.IO_SourceDestMustHaveSameRoot")); - String tempSourceDirName = Path.AddLongPathPrefix(sourceDirName); - String tempDestDirName = Path.AddLongPathPrefix(destDirName); + String tempSourceDirName = PathInternal.EnsureExtendedPrefix(sourceDirName); + String tempDestDirName = PathInternal.EnsureExtendedPrefix(destDirName); if (!Win32Native.MoveFile(tempSourceDirName, tempDestDirName)) { @@ -683,7 +692,7 @@ private static void InternalDelete(String fullPath, String userPath, bool recurs demandPath = GetDemandDir(fullPath, !recursive); // Make sure we have write permission to this directory - new FileIOPermission(FileIOPermissionAccess.Write, new String[] { demandPath }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, demandPath, false, false); String longPath = Path.AddLongPathPrefix(fullPath); // Do not recursively delete through reparse points. Perhaps in a @@ -904,7 +913,7 @@ internal static bool Exists(String path) String fullPath = LongPath.NormalizePath(path); String demandPath = GetDemandDir(fullPath, true); - new FileIOPermission(FileIOPermissionAccess.Read, new String[] { demandPath }, false, false).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandPath, false, false); return InternalExists(fullPath); } diff --git a/mscorlib/system/io/path.cs b/mscorlib/system/io/path.cs index b4cb0ab7e..3927d52da 100644 --- a/mscorlib/system/io/path.cs +++ b/mscorlib/system/io/path.cs @@ -55,19 +55,19 @@ public static class Path // public static readonly char DirectorySeparatorChar = '\\'; internal const string DirectorySeparatorCharAsString = "\\"; - + // Platform specific alternate directory separator character. // This is backslash ('\') on Unix, and slash ('/') on Windows // and MacOS. // public static readonly char AltDirectorySeparatorChar = '/'; - + // Platform specific volume separator character. This is colon (':') // on Windows and MacOS, and slash ('/') on Unix. This is mostly - // useful for parsing paths like "c:\windows" or "MacVolume:System Folder". + // useful for parsing paths like "c:\windows" or "MacVolume:System Folder". // public static readonly char VolumeSeparatorChar = ':'; - + // Platform specific invalid list of characters in a path. // See the "Naming a File" MSDN conceptual docs for more details on // what is valid in a file name (which is slightly different from what @@ -77,10 +77,10 @@ public static class Path public static readonly char[] InvalidPathChars = { '\"', '<', '>', '|', '\0', (Char)1, (Char)2, (Char)3, (Char)4, (Char)5, (Char)6, (Char)7, (Char)8, (Char)9, (Char)10, (Char)11, (Char)12, (Char)13, (Char)14, (Char)15, (Char)16, (Char)17, (Char)18, (Char)19, (Char)20, (Char)21, (Char)22, (Char)23, (Char)24, (Char)25, (Char)26, (Char)27, (Char)28, (Char)29, (Char)30, (Char)31 }; // Trim trailing white spaces, tabs etc but don't be aggressive in removing everything that has UnicodeCategory of trailing space. - // String.WhitespaceChars will trim aggressively than what the underlying FS does (for ex, NTFS, FAT). - internal static readonly char[] TrimEndChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD, (char) 0x20, (char) 0x85, (char) 0xA0}; - - private static readonly char[] RealInvalidPathChars = { '\"', '<', '>', '|', '\0', (Char)1, (Char)2, (Char)3, (Char)4, (Char)5, (Char)6, (Char)7, (Char)8, (Char)9, (Char)10, (Char)11, (Char)12, (Char)13, (Char)14, (Char)15, (Char)16, (Char)17, (Char)18, (Char)19, (Char)20, (Char)21, (Char)22, (Char)23, (Char)24, (Char)25, (Char)26, (Char)27, (Char)28, (Char)29, (Char)30, (Char)31 }; + // String.WhitespaceChars will trim aggressively than what the underlying FS does (for ex, NTFS, FAT). + internal static readonly char[] TrimEndChars = LongPathHelper.s_trimEndChars; + + private static readonly char[] RealInvalidPathChars = PathInternal.InvalidPathChars; // This is used by HasIllegalCharacters private static readonly char[] InvalidPathCharsWithAdditionalChecks = { '\"', '<', '>', '|', '\0', (Char)1, (Char)2, (Char)3, (Char)4, (Char)5, (Char)6, (Char)7, (Char)8, (Char)9, (Char)10, (Char)11, (Char)12, (Char)13, (Char)14, (Char)15, (Char)16, (Char)17, (Char)18, (Char)19, (Char)20, (Char)21, (Char)22, (Char)23, (Char)24, (Char)25, (Char)26, (Char)27, (Char)28, (Char)29, (Char)30, (Char)31, '*', '?' }; @@ -89,16 +89,15 @@ public static class Path public static readonly char PathSeparator = ';'; - // Make this public sometime. // The max total path is 260, and the max individual component length is 255. // For example, D:\<256 char file name> isn't legal, even though it's under 260 chars. - internal static readonly int MaxPath = 260; - private static readonly int MaxDirectoryLength = 255; + internal static readonly int MaxPath = PathInternal.MaxShortPath; + private static readonly int MaxDirectoryLength = PathInternal.MaxComponentLength; // Windows API definitions - internal const int MAX_PATH = 260; // From WinDef.h - internal const int MAX_DIRECTORY_PATH = 248; // cannot create directories greater than 248 characters + internal const int MAX_PATH = PathInternal.MaxShortPath; // From WinDef.h + internal const int MAX_DIRECTORY_PATH = PathInternal.MaxShortDirectoryPath; // cannot create directories greater than 248 characters // Changes the extension of a file path. The path parameter // specifies a file path, and the extension parameter @@ -136,7 +135,6 @@ public static String ChangeExtension(String path, String extension) { return null; } - // Returns the directory path of a file path. This method effectively // removes the last element of the given file path, i.e. it returns a // string consisting of all characters up to but not including the last @@ -146,7 +144,17 @@ public static String ChangeExtension(String path, String extension) { // [ResourceExposure(ResourceScope.None)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] - public static String GetDirectoryName(String path) { + public static string GetDirectoryName(string path) + { + return InternalGetDirectoryName(path); + } + + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] + [System.Security.SecuritySafeCritical] + + private static string InternalGetDirectoryName(string path) + { if (path != null) { CheckInvalidPathChars(path); @@ -154,16 +162,29 @@ public static String GetDirectoryName(String path) { if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { #endif - string normalizedPath = NormalizePath(path, false); + // Expanding short paths is dangerous in this case as the results will change with the current directory. + // + // Suppose you have a path called "PICTUR~1\Foo". Now suppose you have two folders on disk "C:\Mine\Pictures Of Me" + // and "C:\Yours\Pictures of You". If the current directory is neither you'll get back "PICTUR~1". If it is "C:\Mine" + // get back "Pictures Of Me". "C:\Yours" would give back "Pictures of You". + // + // Because of this and as it isn't documented that short paths are expanded we will not expand short names unless + // we're in legacy mode. + string normalizedPath = NormalizePath(path, fullCheck: false, expandShortPaths: AppContextSwitches.UseLegacyPathHandling); // If there are no permissions for PathDiscovery to this path, we should NOT expand the short paths // as this would leak information about paths to which the user would not have access to. - if (path.Length > 0) + if (path.Length > 0 +#if FEATURE_CAS_POLICY + // Only do the extra logic if we're not in full trust + && !CodeAccessSecurityEngine.QuickCheckForAllDemands() +#endif + ) { try { // If we were passed in a path with \\?\ we need to remove it as FileIOPermission does not like it. - string tempPath = Path.RemoveLongPathPrefix(path); + string tempPath = RemoveLongPathPrefix(path); // FileIOPermission cannot handle paths that contain ? or * // So we only pass to FileIOPermission the text up to them. @@ -176,14 +197,14 @@ public static String GetDirectoryName(String path) { // While we don't use the result of this call we are using it as a consistent way of // doing the security checks. if (pos > 0) - Path.GetFullPath(tempPath.Substring(0, pos)); + GetFullPath(tempPath.Substring(0, pos)); } catch (SecurityException) { // If the user did not have permissions to the path, make sure that we don't leak expanded short paths // Only re-normalize if the original path had a ~ in it. if (path.IndexOf("~", StringComparison.Ordinal) != -1) { - normalizedPath = NormalizePath(path, /*fullCheck*/ false, /*expandShortPaths*/ false); + normalizedPath = NormalizePath(path, fullCheck: false, expandShortPaths: false); } } catch (PathTooLongException) { } @@ -203,13 +224,13 @@ public static String GetDirectoryName(String path) { if (i > root) { i = path.Length; if (i == root) return null; - while (i > root && path[--i] != DirectorySeparatorChar && path[i] != AltDirectorySeparatorChar); + while (i > root && path[--i] != DirectorySeparatorChar && path[i] != AltDirectorySeparatorChar); String dir = path.Substring(0, i); #if FEATURE_LEGACYNETCF - if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { + if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) { if (dir.Length >= MAX_PATH - 1) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - } + } #endif return dir; } @@ -220,9 +241,22 @@ public static String GetDirectoryName(String path) { // Gets the length of the root DirectoryInfo or whatever DirectoryInfo markers // are specified for the first part of the DirectoryInfo name. // - internal static int GetRootLength(String path) { + internal static int GetRootLength(string path) + { CheckInvalidPathChars(path); - + + if (AppContextSwitches.UseLegacyPathHandling) + { + return LegacyGetRootLength(path); + } + else + { + return PathInternal.GetRootLength(path); + } + } + + private static int LegacyGetRootLength(string path) + { int i = 0; int length = path.Length; @@ -247,7 +281,6 @@ internal static bool IsDirectorySeparator(char c) { return (c==DirectorySeparatorChar || c == AltDirectorySeparatorChar); } - public static char[] GetInvalidPathChars() { return (char[]) RealInvalidPathChars.Clone(); @@ -330,7 +363,7 @@ internal static String GetFullPathInternal(String path) { throw new ArgumentNullException("path"); Contract.EndContractBlock(); - String newPath = NormalizePath(path, true); + string newPath = NormalizePath(path, fullCheck: true); return newPath; } @@ -338,8 +371,9 @@ internal static String GetFullPathInternal(String path) { [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] - internal unsafe static String NormalizePath(String path, bool fullCheck) { - return NormalizePath(path, fullCheck, MaxPath); + internal unsafe static String NormalizePath(String path, bool fullCheck) + { + return NormalizePath(path, fullCheck, AppContextSwitches.BlockLongPaths ? PathInternal.MaxShortPath : PathInternal.MaxLongPath); } [System.Security.SecuritySafeCritical] // auto-generated @@ -347,31 +381,127 @@ internal unsafe static String NormalizePath(String path, bool fullCheck) { [ResourceConsumption(ResourceScope.Machine)] internal unsafe static String NormalizePath(String path, bool fullCheck, bool expandShortPaths) { - return NormalizePath(path, fullCheck, MaxPath, expandShortPaths); + return NormalizePath(path, fullCheck, MaxPath, expandShortPaths: expandShortPaths); } [System.Security.SecuritySafeCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] internal unsafe static String NormalizePath(String path, bool fullCheck, int maxPathLength) { - return NormalizePath(path, fullCheck, maxPathLength, true); + return NormalizePath(path, fullCheck, maxPathLength, expandShortPaths: true); } - [System.Security.SecurityCritical] // auto-generated + [System.Security.SecuritySafeCritical] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] - internal unsafe static String NormalizePath(String path, bool fullCheck, int maxPathLength, bool expandShortPaths) { + internal static string NormalizePath(string path, bool fullCheck, int maxPathLength, bool expandShortPaths) + { + if (AppContextSwitches.UseLegacyPathHandling) + { + return LegacyNormalizePath(path, fullCheck, maxPathLength, expandShortPaths); + } + else + { + if (PathInternal.IsExtended(path)) + { + // We can't really know what is valid for all cases of extended paths. + // + // - object names can include other characters as well (':', '/', etc.) + // - even file objects have different rules (pipe names can contain most characters) + // + // As such we will do no further analysis of extended paths to avoid blocking known and unknown + // scenarios as well as minimizing compat breaks should we block now and need to unblock later. + return path; + } + + string normalizedPath = null; + if (fullCheck == false) + { + // Disabled fullCheck is only called by GetDirectoryName and GetPathRoot. + // Avoid adding addtional callers and try going direct to lighter weight NormalizeDirectorySeparators. + normalizedPath = NewNormalizePathLimitedChecks(path, maxPathLength, expandShortPaths); + } + else + { + normalizedPath = NewNormalizePath(path, maxPathLength, expandShortPaths: true); + } + + if (string.IsNullOrWhiteSpace(normalizedPath)) + throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); + return normalizedPath; + } + } + + [System.Security.SecuritySafeCritical] + private static string NewNormalizePathLimitedChecks(string path, int maxPathLength, bool expandShortPaths) + { + string normalized = PathInternal.NormalizeDirectorySeparators(path); + + if (PathInternal.IsPathTooLong(normalized) || PathInternal.AreSegmentsTooLong(normalized)) + throw new PathTooLongException(); + + if (!PathInternal.IsDevice(normalized) && PathInternal.HasInvalidVolumeSeparator(path)) + throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); + + if (expandShortPaths && normalized.IndexOf('~') != -1) + { + try + { + return LongPathHelper.GetLongPathName(normalized); + } + catch + { + // Don't care if we can't get the long path- might not exist, etc. + } + } + + return normalized; + } + + /// + /// Normalize the path and check for bad characters or other invalid syntax. + /// + [System.Security.SecuritySafeCritical] + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + private static string NewNormalizePath(string path, int maxPathLength, bool expandShortPaths) + { Contract.Requires(path != null, "path can't be null"); - // If we're doing a full path check, trim whitespace and look for - // illegal path characters. - if (fullCheck) { + + // Embedded null characters are the only invalid character case we want to check up front. + // This is because the nulls will signal the end of the string to Win32 and therefore have + // unpredictable results. Other invalid characters we give a chance to be normalized out. + if (path.IndexOf('\0') != -1) + throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars")); + + // Note that colon and wildcard checks happen in FileIOPermissions + + // Technically this doesn't matter but we used to throw for this case + if (string.IsNullOrWhiteSpace(path)) + throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); + + // We don't want to check invalid characters for device format- see comments for extended above + return LongPathHelper.Normalize(path, (uint)maxPathLength, checkInvalidCharacters: !PathInternal.IsDevice(path), expandShortPaths: expandShortPaths); + } + + [System.Security.SecurityCritical] // auto-generated + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + internal unsafe static string LegacyNormalizePath(string path, bool fullCheck, int maxPathLength, bool expandShortPaths) + { + Contract.Requires(path != null, "path can't be null"); + + // If we're doing a full path check, trim whitespace and look for illegal path characters. + if (fullCheck) + { // Trim whitespace off the end of the string. // Win32 normalization trims only U+0020. path = path.TrimEnd(TrimEndChars); // Look for illegal path characters. - CheckInvalidPathChars(path); + if (PathInternal.AnyPathHasIllegalCharacters(path)) + throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars")); } int index = 0; @@ -777,50 +907,75 @@ internal unsafe static String NormalizePath(String path, bool fullCheck, int max return returnVal; } - internal const int MaxLongPath = 32000; - private const string LongPathPrefix = @"\\?\"; - private const string UNCPathPrefix = @"\\"; - private const string UNCLongPathPrefixToInsert = @"?\UNC\"; - private const string UNCLongPathPrefix = @"\\?\UNC\"; + internal const int MaxLongPath = PathInternal.MaxLongPath; - internal unsafe static bool HasLongPathPrefix(String path) + private const string LongPathPrefix = PathInternal.ExtendedPathPrefix; + private const string UNCPathPrefix = PathInternal.UncPathPrefix; + private const string UNCLongPathPrefixToInsert = PathInternal.UncExtendedPrefixToInsert; + private const string UNCLongPathPrefix = PathInternal.UncExtendedPathPrefix; + + internal static bool HasLongPathPrefix(string path) { - return path.StartsWith(LongPathPrefix, StringComparison.Ordinal); + if (AppContextSwitches.UseLegacyPathHandling) + return path.StartsWith(LongPathPrefix, StringComparison.Ordinal); + else + return PathInternal.IsExtended(path); } - internal unsafe static String AddLongPathPrefix(String path) + internal static string AddLongPathPrefix(string path) { - if (path.StartsWith(LongPathPrefix, StringComparison.Ordinal)) - return path; + if (AppContextSwitches.UseLegacyPathHandling) + { + if (path.StartsWith(LongPathPrefix, StringComparison.Ordinal)) + return path; - if (path.StartsWith(UNCPathPrefix, StringComparison.Ordinal)) - return path.Insert(2, UNCLongPathPrefixToInsert); // Given \\server\share in longpath becomes \\?\UNC\server\share => UNCLongPathPrefix + path.SubString(2); => The actual command simply reduces the operation cost. + if (path.StartsWith(UNCPathPrefix, StringComparison.Ordinal)) + return path.Insert(2, UNCLongPathPrefixToInsert); // Given \\server\share in longpath becomes \\?\UNC\server\share => UNCLongPathPrefix + path.SubString(2); => The actual command simply reduces the operation cost. - return LongPathPrefix + path; + return LongPathPrefix + path; + } + else + { + return PathInternal.EnsureExtendedPrefix(path); + } } - internal unsafe static String RemoveLongPathPrefix(String path) + internal static string RemoveLongPathPrefix(string path) { - if (!path.StartsWith(LongPathPrefix, StringComparison.Ordinal)) - return path; + if (AppContextSwitches.UseLegacyPathHandling) + { + if (!path.StartsWith(LongPathPrefix, StringComparison.Ordinal)) + return path; - if (path.StartsWith(UNCLongPathPrefix, StringComparison.OrdinalIgnoreCase)) - return path.Remove(2, 6); // Given \\?\UNC\server\share we return \\server\share => @'\\' + path.SubString(UNCLongPathPrefix.Length) => The actual command simply reduces the operation cost. + if (path.StartsWith(UNCLongPathPrefix, StringComparison.OrdinalIgnoreCase)) + return path.Remove(2, 6); // Given \\?\UNC\server\share we return \\server\share => @'\\' + path.SubString(UNCLongPathPrefix.Length) => The actual command simply reduces the operation cost. - return path.Substring(4); + return path.Substring(4); + } + else + { + return PathInternal.RemoveExtendedPrefix(path); + } } - internal unsafe static StringBuilder RemoveLongPathPrefix(StringBuilder pathSB) + internal static StringBuilder RemoveLongPathPrefix(StringBuilder pathSB) { - string path = pathSB.ToString(); - if (!path.StartsWith(LongPathPrefix, StringComparison.Ordinal)) - return pathSB; + if (AppContextSwitches.UseLegacyPathHandling) + { + if (!PathInternal.StartsWithOrdinal(pathSB, LongPathPrefix)) + return pathSB; - if (path.StartsWith(UNCLongPathPrefix, StringComparison.OrdinalIgnoreCase)) - return pathSB.Remove(2, 6); // Given \\?\UNC\server\share we return \\server\share => @'\\' + path.SubString(UNCLongPathPrefix.Length) => The actual command simply reduces the operation cost. + // Given \\?\UNC\server\share we return \\server\share => @'\\' + path.SubString(UNCLongPathPrefix.Length) => The actual command simply reduces the operation cost. + if (PathInternal.StartsWithOrdinal(pathSB, UNCLongPathPrefix, ignoreCase: true)) + return pathSB.Remove(2, 6); - return pathSB.Remove(0, 4); + return pathSB.Remove(0, 4); + } + else + { + return PathInternal.RemoveExtendedPrefix(pathSB); + } } // Returns the name and extension parts of the given path. The resulting @@ -860,8 +1015,6 @@ public static String GetFileNameWithoutExtension(String path) { return null; } - - // Returns the root portion of the given path. The resulting string // consists of those rightmost characters of the path that constitute the // root of the path. Possible patterns for the resulting string are: An @@ -876,7 +1029,10 @@ public static String GetFileNameWithoutExtension(String path) { [ResourceConsumption(ResourceScope.Machine)] public static String GetPathRoot(String path) { if (path == null) return null; - path = NormalizePath(path, false); + + // Expanding short paths has no impact on the path root- there is no such thing as an + // 8.3 volume or server/share name. + path = NormalizePath(path, fullCheck: false, expandShortPaths: false); return path.Substring(0, GetRootLength(path)); } @@ -903,15 +1059,9 @@ public static String GetTempPath() internal static bool IsRelative(string path) { Contract.Assert(path != null, "path can't be null"); - if ((path.Length >= 3 && path[1] == VolumeSeparatorChar && path[2] == DirectorySeparatorChar && - ((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z'))) || - (path.Length >= 2 && path[0] == '\\' && path[1] == '\\')) - return false; - else - return true; - + return PathInternal.IsPartiallyQualified(path); } - + // Returns a cryptographically strong random 8.3 string that can be // used as either a folder name or a file name. public static String GetRandomFileName() @@ -978,7 +1128,7 @@ private static String InternalGetTempFileName(bool checkHost) state.EnsureState(); } #else - new FileIOPermission(FileIOPermissionAccess.Write, path).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, path); #endif StringBuilder sb = new StringBuilder(MAX_PATH); uint r = Win32Native.GetTempFileName(path, "tmp", 0, sb); @@ -1214,31 +1364,17 @@ internal static void CheckSearchPattern(String searchPattern) searchPattern = searchPattern.Substring(index + 2); } - - } - - internal static bool HasIllegalCharacters(String path, bool checkAdditional) - { - Contract.Requires(path != null); - - if (checkAdditional) - { - return path.IndexOfAny(InvalidPathCharsWithAdditionalChecks) >= 0; - } - - return path.IndexOfAny(RealInvalidPathChars) >= 0; } - internal static void CheckInvalidPathChars(String path, bool checkAdditional = false) + internal static void CheckInvalidPathChars(string path, bool checkAdditional = false) { if (path == null) throw new ArgumentNullException("path"); - if (Path.HasIllegalCharacters(path, checkAdditional)) + if (PathInternal.HasIllegalCharacters(path, checkAdditional)) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars")); } - internal static String InternalCombine(String path1, String path2) { if (path1==null || path2==null) throw new ArgumentNullException((path1==null) ? "path1" : "path2"); diff --git a/mscorlib/system/resources/resourcereader.cs b/mscorlib/system/resources/resourcereader.cs index 7f8d20517..ec458846b 100644 --- a/mscorlib/system/resources/resourcereader.cs +++ b/mscorlib/system/resources/resourcereader.cs @@ -1002,10 +1002,15 @@ private void _ReadResources() } } else { - int seekPos = unchecked(4 * _numResources); - if (seekPos < 0) { + // The hexadecimal E translates to binary 1110 + // So, with this & condition we are checking that none of the highest 3 bits are + // set before multiplying, as that would cause an overflow. + if ((_numResources & 0xE0000000) != 0){ + throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted")); } + + int seekPos = unchecked(4 * _numResources); unsafe { _nameHashesPtr = (int*)_ums.PositionPointer; // Skip over the array of nameHashes. @@ -1034,10 +1039,14 @@ private void _ReadResources() } } else { - int seekPos = unchecked(4 * _numResources); - if (seekPos < 0) { + // The hexadecimal E translates to binary 1110 + // So, with this & condition we are checking that none of the highest 3 bits are + // set before multiplying, as that would cause an overflow. + if ((_numResources & 0xE0000000) != 0){ throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted")); } + + int seekPos = unchecked(4 * _numResources); unsafe { _namePositionsPtr = (int*)_ums.PositionPointer; // Skip over the array of namePositions. diff --git a/mscorlib/system/runtime/interopservices/windowsruntime/nativemethods.cs b/mscorlib/system/runtime/interopservices/windowsruntime/nativemethods.cs index f9cec7869..893340d67 100644 --- a/mscorlib/system/runtime/interopservices/windowsruntime/nativemethods.cs +++ b/mscorlib/system/runtime/interopservices/windowsruntime/nativemethods.cs @@ -28,22 +28,26 @@ internal static class UnsafeNativeMethods [DllImport("api-ms-win-core-winrt-error-l1-1-1.dll", PreserveSig = false)] [SecurityCritical] [SuppressUnmanagedCodeSecurity] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] internal static extern IRestrictedErrorInfo GetRestrictedErrorInfo(); [DllImport("api-ms-win-core-winrt-error-l1-1-1.dll")] [SecurityCritical] [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] internal static extern bool RoOriginateLanguageException(int error, [MarshalAs(UnmanagedType.HString)]string message, IntPtr languageException); [DllImport("api-ms-win-core-winrt-error-l1-1-1.dll", PreserveSig = false)] [SecurityCritical] [SuppressUnmanagedCodeSecurity] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] internal static extern void RoReportUnhandledError(IRestrictedErrorInfo error); [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] [SecurityCritical] [SuppressUnmanagedCodeSecurity] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] internal static unsafe extern int WindowsCreateString([MarshalAs(UnmanagedType.LPWStr)] string sourceString, int length, [Out] IntPtr *hstring); @@ -51,6 +55,7 @@ internal static unsafe extern int WindowsCreateString([MarshalAs(UnmanagedType.L [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] [SecurityCritical] [SuppressUnmanagedCodeSecurity] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] internal static unsafe extern int WindowsCreateStringReference(char *sourceString, int length, [Out] HSTRING_HEADER *hstringHeader, @@ -59,11 +64,13 @@ internal static unsafe extern int WindowsCreateStringReference(char *sourceStrin [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] [SecurityCritical] [SuppressUnmanagedCodeSecurity] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] internal static extern int WindowsDeleteString(IntPtr hstring); [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] [SecurityCritical] [SuppressUnmanagedCodeSecurity] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] internal static unsafe extern char* WindowsGetStringRawBuffer(IntPtr hstring, [Out] uint *length); } } diff --git a/mscorlib/system/security/accesscontrol/filesecurity.cs b/mscorlib/system/security/accesscontrol/filesecurity.cs index 59c44820f..fad7beb38 100644 --- a/mscorlib/system/security/accesscontrol/filesecurity.cs +++ b/mscorlib/system/security/accesscontrol/filesecurity.cs @@ -435,9 +435,9 @@ internal AccessControlSections GetAccessControlSectionsFromChanges() [SecurityPermission(SecurityAction.Assert, UnmanagedCode=true)] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] - internal void Persist( String fullPath ) + internal void Persist(string fullPath) { - new FileIOPermission( FileIOPermissionAccess.NoAccess, AccessControlActions.Change, fullPath ).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.NoAccess, AccessControlActions.Change, fullPath); WriteLock(); @@ -455,12 +455,12 @@ internal void Persist( String fullPath ) [System.Security.SecuritySafeCritical] // auto-generated [SecurityPermission(SecurityAction.Assert, UnmanagedCode=true)] - internal void Persist( SafeFileHandle handle, String fullPath ) + internal void Persist(SafeFileHandle handle, string fullPath) { - if ( fullPath != null ) - new FileIOPermission( FileIOPermissionAccess.NoAccess, AccessControlActions.Change, fullPath ).Demand(); + if (fullPath != null) + FileIOPermission.QuickDemand(FileIOPermissionAccess.NoAccess, AccessControlActions.Change, fullPath); else - new FileIOPermission( PermissionState.Unrestricted ).Demand(); + FileIOPermission.QuickDemand(PermissionState.Unrestricted); WriteLock(); @@ -627,11 +627,9 @@ public override Type AuditRuleType public sealed class FileSecurity : FileSystemSecurity { - #region Constructors - [System.Security.SecuritySafeCritical] // auto-generated public FileSecurity() - : base( false ) + : base(false) { } @@ -639,11 +637,11 @@ public FileSecurity() [SecurityPermission(SecurityAction.Assert, UnmanagedCode=true)] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] - public FileSecurity( String fileName, AccessControlSections includeSections ) + public FileSecurity(string fileName, AccessControlSections includeSections) : base(false, fileName, includeSections, false) { - String fullPath = Path.GetFullPathInternal(fileName); - new FileIOPermission(FileIOPermissionAccess.NoAccess, AccessControlActions.View, fullPath).Demand(); + string fullPath = Path.GetFullPathInternal(fileName); + FileIOPermission.QuickDemand(FileIOPermissionAccess.NoAccess, AccessControlActions.View, fullPath, checkForDuplicates: false, needFullPath: false); } // Warning! Be exceedingly careful with this constructor. Do not make @@ -654,26 +652,21 @@ public FileSecurity( String fileName, AccessControlSections includeSections ) [SecurityPermission(SecurityAction.Assert, UnmanagedCode=true)] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] - internal FileSecurity( SafeFileHandle handle, String fullPath, AccessControlSections includeSections ) - : base( false, handle, includeSections, false ) + internal FileSecurity(SafeFileHandle handle, string fullPath, AccessControlSections includeSections) + : base(false, handle, includeSections, false) { if (fullPath != null) - new FileIOPermission(FileIOPermissionAccess.NoAccess, AccessControlActions.View, fullPath).Demand(); + FileIOPermission.QuickDemand(FileIOPermissionAccess.NoAccess, AccessControlActions.View, fullPath); else - new FileIOPermission(PermissionState.Unrestricted).Demand(); + FileIOPermission.QuickDemand(PermissionState.Unrestricted); } - - #endregion } - public sealed class DirectorySecurity : FileSystemSecurity { - #region Constructors - [System.Security.SecuritySafeCritical] // auto-generated public DirectorySecurity() - : base( true ) + : base(true) { } @@ -681,14 +674,12 @@ public DirectorySecurity() [SecurityPermission(SecurityAction.Assert, UnmanagedCode=true)] [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] - public DirectorySecurity( String name, AccessControlSections includeSections ) - : base( true, name, includeSections, true ) + public DirectorySecurity(string name, AccessControlSections includeSections) + : base(true, name, includeSections, true) { - String fullPath = Path.GetFullPathInternal(name); - new FileIOPermission(FileIOPermissionAccess.NoAccess, AccessControlActions.View, fullPath).Demand(); + string fullPath = Path.GetFullPathInternal(name); + FileIOPermission.QuickDemand(FileIOPermissionAccess.NoAccess, AccessControlActions.View, fullPath, checkForDuplicates: false, needFullPath: false); } - - #endregion } } diff --git a/mscorlib/system/security/claims/ClaimsIdentity.cs b/mscorlib/system/security/claims/ClaimsIdentity.cs index 5b8627cd9..5f3a25e79 100644 --- a/mscorlib/system/security/claims/ClaimsIdentity.cs +++ b/mscorlib/system/security/claims/ClaimsIdentity.cs @@ -315,7 +315,14 @@ internal ClaimsIdentity(IIdentity identity, IEnumerable claims, string au // if(!IsCircular(claimsIdentity.Actor)) { - m_actor = claimsIdentity.Actor; + if (!AppContextSwitches.SetActorAsReferenceWhenCopyingClaimsIdentity) + { + m_actor = claimsIdentity.Actor.Clone(); + } + else + { + m_actor = claimsIdentity.Actor; + } } else { @@ -334,6 +341,10 @@ internal ClaimsIdentity(IIdentity identity, IEnumerable claims, string au else SafeAddClaims(claimsIdentity.m_instanceClaims); + if (claimsIdentity.m_userSerializationData != null) + { + m_userSerializationData = claimsIdentity.m_userSerializationData.Clone() as byte[]; + } } else { @@ -594,7 +605,14 @@ public virtual ClaimsIdentity Clone() // the Actor property and so not really needed here. But checking just for sanity sake if(!IsCircular(this.Actor)) { - newIdentity.Actor = this.Actor; + if (!AppContextSwitches.SetActorAsReferenceWhenCopyingClaimsIdentity) + { + newIdentity.Actor = this.Actor.Clone(); + } + else + { + newIdentity.Actor = this.Actor; + } } else { @@ -945,6 +963,7 @@ private void OnDeserializingMethod(StreamingContext context) return; m_instanceClaims = new List(); + m_externalClaims = new Collection>(); } diff --git a/mscorlib/system/security/cryptography/cryptoconfig.cs b/mscorlib/system/security/cryptography/cryptoconfig.cs index 7d3eb777e..667369d2d 100644 --- a/mscorlib/system/security/cryptography/cryptoconfig.cs +++ b/mscorlib/system/security/cryptography/cryptoconfig.cs @@ -122,6 +122,7 @@ private static Dictionary DefaultOidHT { #endif //FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO #if FEATURE_CRYPTO ht.Add("System.Security.Cryptography.SHA1CryptoServiceProvider", Constants.OID_OIWSEC_SHA1); + ht.Add("System.Security.Cryptography.SHA1Cng", Constants.OID_OIWSEC_SHA1); #endif //FEATURE_CRYPTO #if FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO ht.Add("System.Security.Cryptography.SHA1Managed", Constants.OID_OIWSEC_SHA1); @@ -174,13 +175,6 @@ private static Dictionary DefaultNameHT { #if FEATURE_CRYPTO Type SHA1CryptoServiceProviderType = typeof(System.Security.Cryptography.SHA1CryptoServiceProvider); Type MD5CryptoServiceProviderType = typeof(System.Security.Cryptography.MD5CryptoServiceProvider); -#endif //FEATURE_CRYPTO -#if FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO - Type SHA256ManagedType = typeof(SHA256Managed); -#endif //FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO -#if FEATURE_CRYPTO - Type SHA384ManagedType = typeof(SHA384Managed); - Type SHA512ManagedType = typeof(SHA512Managed); Type RIPEMD160ManagedType = typeof(System.Security.Cryptography.RIPEMD160Managed); Type HMACMD5Type = typeof(System.Security.Cryptography.HMACMD5); Type HMACRIPEMD160Type = typeof(System.Security.Cryptography.HMACRIPEMD160); @@ -209,6 +203,9 @@ private static Dictionary DefaultNameHT { #if FEATURE_CRYPTO Type DSASignatureDescriptionType = typeof(System.Security.Cryptography.DSASignatureDescription); Type RSAPKCS1SHA1SignatureDescriptionType = typeof(System.Security.Cryptography.RSAPKCS1SHA1SignatureDescription); + Type RSAPKCS1SHA256SignatureDescriptionType = typeof(System.Security.Cryptography.RSAPKCS1SHA256SignatureDescription); + Type RSAPKCS1SHA384SignatureDescriptionType = typeof(System.Security.Cryptography.RSAPKCS1SHA384SignatureDescription); + Type RSAPKCS1SHA512SignatureDescriptionType = typeof(System.Security.Cryptography.RSAPKCS1SHA512SignatureDescription); #endif //FEATURE_CRYPTO #if FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO Type RNGCryptoServiceProviderType = typeof(System.Security.Cryptography.RNGCryptoServiceProvider); @@ -233,11 +230,26 @@ private static Dictionary DefaultNameHT { string SHA384CryptoSerivceProviderType = "System.Security.Cryptography.SHA384CryptoServiceProvider, " + AssemblyRef.SystemCore; string SHA512CngType = "System.Security.Cryptography.SHA512Cng, " + AssemblyRef.SystemCore; string SHA512CryptoServiceProviderType = "System.Security.Cryptography.SHA512CryptoServiceProvider, " + AssemblyRef.SystemCore; +#endif //FEATURE_CRYPTO + + +#if FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO + bool fipsOnly = AllowOnlyFipsAlgorithms; + object SHA256DefaultType = typeof(SHA256Managed); +#endif //FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO + +#if FEATURE_CRYPTO + if (fipsOnly) + { + SHA256DefaultType = SHA256CngType; + } + object SHA384DefaultType = fipsOnly ? (object)SHA384CngType : (object)typeof(SHA384Managed); + object SHA512DefaultType = fipsOnly ? (object)SHA512CngType : (object)typeof(SHA512Managed); // Cryptography algorithms in System.Security string DpapiDataProtectorType = "System.Security.Cryptography.DpapiDataProtector, " + AssemblyRef.SystemSecurity; - #endif //FEATURE_CRYPTO + #if FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO // Random number generator ht.Add("RandomNumberGenerator", RNGCryptoServiceProviderType); @@ -256,21 +268,21 @@ private static Dictionary DefaultNameHT { ht.Add("System.Security.Cryptography.MD5Cng", MD5CngType); #endif //FEATURE_CRYPTO #if FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO - ht.Add("SHA256", SHA256ManagedType); - ht.Add("SHA-256", SHA256ManagedType); - ht.Add("System.Security.Cryptography.SHA256", SHA256ManagedType); + ht.Add("SHA256", SHA256DefaultType); + ht.Add("SHA-256", SHA256DefaultType); + ht.Add("System.Security.Cryptography.SHA256", SHA256DefaultType); #endif //FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO #if FEATURE_CRYPTO ht.Add("System.Security.Cryptography.SHA256Cng", SHA256CngType); ht.Add("System.Security.Cryptography.SHA256CryptoServiceProvider", SHA256CryptoServiceProviderType); - ht.Add("SHA384", SHA384ManagedType); - ht.Add("SHA-384", SHA384ManagedType); - ht.Add("System.Security.Cryptography.SHA384", SHA384ManagedType); + ht.Add("SHA384", SHA384DefaultType); + ht.Add("SHA-384", SHA384DefaultType); + ht.Add("System.Security.Cryptography.SHA384", SHA384DefaultType); ht.Add("System.Security.Cryptography.SHA384Cng", SHA384CngType); ht.Add("System.Security.Cryptography.SHA384CryptoServiceProvider", SHA384CryptoSerivceProviderType); - ht.Add("SHA512", SHA512ManagedType); - ht.Add("SHA-512", SHA512ManagedType); - ht.Add("System.Security.Cryptography.SHA512", SHA512ManagedType); + ht.Add("SHA512", SHA512DefaultType); + ht.Add("SHA-512", SHA512DefaultType); + ht.Add("System.Security.Cryptography.SHA512", SHA512DefaultType); ht.Add("System.Security.Cryptography.SHA512Cng", SHA512CngType); ht.Add("System.Security.Cryptography.SHA512CryptoServiceProvider", SHA512CryptoServiceProviderType); ht.Add("RIPEMD160", RIPEMD160ManagedType); @@ -357,16 +369,19 @@ private static Dictionary DefaultNameHT { ht.Add("System.Security.Cryptography.DSASignatureDescription", DSASignatureDescriptionType); ht.Add("http://www.w3.org/2000/09/xmldsig#rsa-sha1", RSAPKCS1SHA1SignatureDescriptionType); ht.Add("System.Security.Cryptography.RSASignatureDescription", RSAPKCS1SHA1SignatureDescriptionType); + ht.Add("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", RSAPKCS1SHA256SignatureDescriptionType); + ht.Add("http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", RSAPKCS1SHA384SignatureDescriptionType); + ht.Add("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", RSAPKCS1SHA512SignatureDescriptionType); // Xml Dsig/Enc Hash algorithms ht.Add("http://www.w3.org/2000/09/xmldsig#sha1", SHA1CryptoServiceProviderType); // Add the other hash algorithms introduced with XML Encryption #endif //FEATURE_CRYPTO #if FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO - ht.Add("http://www.w3.org/2001/04/xmlenc#sha256", SHA256ManagedType); + ht.Add("http://www.w3.org/2001/04/xmlenc#sha256", SHA256DefaultType); #endif //FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO #if FEATURE_CRYPTO && !FEATURE_CORECLR - ht.Add("http://www.w3.org/2001/04/xmlenc#sha512", SHA512ManagedType); + ht.Add("http://www.w3.org/2001/04/xmlenc#sha512", SHA512DefaultType); ht.Add("http://www.w3.org/2001/04/xmlenc#ripemd160", RIPEMD160ManagedType); // Xml Encryption symmetric keys @@ -421,7 +436,7 @@ private static Dictionary DefaultNameHT { // Xml Dsig-more Uri's as defined in http://www.ietf.org/rfc/rfc4051.txt ht.Add("http://www.w3.org/2001/04/xmldsig-more#md5", MD5CryptoServiceProviderType); - ht.Add("http://www.w3.org/2001/04/xmldsig-more#sha384", SHA384ManagedType); + ht.Add("http://www.w3.org/2001/04/xmldsig-more#sha384", SHA384DefaultType); ht.Add("http://www.w3.org/2001/04/xmldsig-more#hmac-md5", HMACMD5Type); ht.Add("http://www.w3.org/2001/04/xmldsig-more#hmac-ripemd160", HMACRIPEMD160Type); #endif //FEATURE_CRYPTO diff --git a/mscorlib/system/security/cryptography/dsa.cs b/mscorlib/system/security/cryptography/dsa.cs index 7f4b4a31a..a71cf7976 100644 --- a/mscorlib/system/security/cryptography/dsa.cs +++ b/mscorlib/system/security/cryptography/dsa.cs @@ -15,6 +15,7 @@ namespace System.Security.Cryptography { using System.Runtime.Serialization; using System.Security.Util; using System.Globalization; + using System.IO; using System.Diagnostics.Contracts; // DSAParameters is serializable so that one could pass the public parameters @@ -59,10 +60,78 @@ protected DSA() { } return (DSA) CryptoConfig.CreateFromName(algName); } + // DSA does not encode the algorithm identifier into the signature blob, therefore CreateSignature and + // VerifySignature do not need the HashAlgorithmName value, only SignData and VerifyData do. abstract public byte[] CreateSignature(byte[] rgbHash); abstract public bool VerifySignature(byte[] rgbHash, byte[] rgbSignature); + protected virtual byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) + { + throw DerivedClassMustOverride(); + } + + protected virtual byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) + { + throw DerivedClassMustOverride(); + } + + public byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm) + { + if (data == null) { throw new ArgumentNullException("data"); } + + return SignData(data, 0, data.Length, hashAlgorithm); + } + + public virtual byte[] SignData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) + { + if (data == null) { throw new ArgumentNullException("data"); } + if (offset < 0 || offset > data.Length) { throw new ArgumentOutOfRangeException("offset"); } + if (count < 0 || count > data.Length - offset) { throw new ArgumentOutOfRangeException("count"); } + if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); } + + byte[] hash = HashData(data, offset, count, hashAlgorithm); + return CreateSignature(hash); + } + + public virtual byte[] SignData(Stream data, HashAlgorithmName hashAlgorithm) + { + if (data == null) { throw new ArgumentNullException("data"); } + if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); } + + byte[] hash = HashData(data, hashAlgorithm); + return CreateSignature(hash); + } + + public bool VerifyData(byte[] data, byte[] signature, HashAlgorithmName hashAlgorithm) + { + if (data == null) { throw new ArgumentNullException("data"); } + + return VerifyData(data, 0, data.Length, signature, hashAlgorithm); + } + + public virtual bool VerifyData(byte[] data, int offset, int count, byte[] signature, HashAlgorithmName hashAlgorithm) + { + if (data == null) { throw new ArgumentNullException("data"); } + if (offset < 0 || offset > data.Length) { throw new ArgumentOutOfRangeException("offset"); } + if (count < 0 || count > data.Length - offset) { throw new ArgumentOutOfRangeException("count"); } + if (signature == null) { throw new ArgumentNullException("signature"); } + if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); } + + byte[] hash = HashData(data, offset, count, hashAlgorithm); + return VerifySignature(hash, signature); + } + + public virtual bool VerifyData(Stream data, byte[] signature, HashAlgorithmName hashAlgorithm) + { + if (data == null) { throw new ArgumentNullException("data"); } + if (signature == null) { throw new ArgumentNullException("signature"); } + if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); } + + byte[] hash = HashData(data, hashAlgorithm); + return VerifySignature(hash, signature); + } + // We can provide a default implementation of FromXmlString because we require // every DSA implementation to implement ImportParameters // All we have to do here is parse the XML. @@ -183,5 +252,15 @@ public override String ToXmlString(bool includePrivateParameters) { abstract public DSAParameters ExportParameters(bool includePrivateParameters); abstract public void ImportParameters(DSAParameters parameters); + + private static Exception DerivedClassMustOverride() + { + return new NotImplementedException(Environment.GetResourceString("NotSupported_SubclassOverride")); + } + + internal static Exception HashAlgorithmNameNullOrEmpty() + { + return new ArgumentException(Environment.GetResourceString("Cryptography_HashAlgorithmNameNullOrEmpty"), "hashAlgorithm"); + } } } diff --git a/mscorlib/system/security/cryptography/dsacryptoserviceprovider.cs b/mscorlib/system/security/cryptography/dsacryptoserviceprovider.cs index 318b09813..df23a26af 100644 --- a/mscorlib/system/security/cryptography/dsacryptoserviceprovider.cs +++ b/mscorlib/system/security/cryptography/dsacryptoserviceprovider.cs @@ -268,6 +268,36 @@ override public bool VerifySignature(byte[] rgbHash, byte[] rgbSignature) { return VerifyHash(rgbHash, null, rgbSignature); } + protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) + { + // we're sealed and the base should have checked this before calling us + Contract.Assert(data != null); + Contract.Assert(offset >= 0 && offset <= data.Length); + Contract.Assert(count >= 0 && count <= data.Length - offset); + Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name)); + + if (hashAlgorithm != HashAlgorithmName.SHA1) + { + throw new CryptographicException(Environment.GetResourceString("Cryptography_UnknownHashAlgorithm", hashAlgorithm.Name)); + } + + return _sha1.ComputeHash(data, offset, count); + } + + protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) + { + // we're sealed and the base should have checked this before calling us + Contract.Assert(data != null); + Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name)); + + if (hashAlgorithm != HashAlgorithmName.SHA1) + { + throw new CryptographicException(Environment.GetResourceString("Cryptography_UnknownHashAlgorithm", hashAlgorithm.Name)); + } + + return _sha1.ComputeHash(data); + } + [System.Security.SecuritySafeCritical] // auto-generated public byte[] SignHash(byte[] rgbHash, string str) { if (rgbHash == null) diff --git a/mscorlib/system/security/cryptography/rsaoaepkeyexchangedeformatter.cs b/mscorlib/system/security/cryptography/rsaoaepkeyexchangedeformatter.cs index 416b29f50..674cdf23f 100644 --- a/mscorlib/system/security/cryptography/rsaoaepkeyexchangedeformatter.cs +++ b/mscorlib/system/security/cryptography/rsaoaepkeyexchangedeformatter.cs @@ -11,6 +11,7 @@ namespace System.Security.Cryptography { [System.Runtime.InteropServices.ComVisible(true)] public class RSAOAEPKeyExchangeDeformatter : AsymmetricKeyExchangeDeformatter { private RSA _rsaKey; // RSA Key value to do decrypt operation + private bool? _rsaOverridesDecrypt; // // public constructors @@ -42,8 +43,8 @@ public override byte[] DecryptKeyExchange(byte[] rgbData) { if (_rsaKey == null) throw new CryptographicUnexpectedOperationException(Environment.GetResourceString("Cryptography_MissingKey")); - if (_rsaKey is RSACryptoServiceProvider) { - return ((RSACryptoServiceProvider) _rsaKey).Decrypt(rgbData, true); + if (OverridesDecrypt) { + return _rsaKey.Decrypt(rgbData, RSAEncryptionPadding.OaepSHA1); } else { return Utils.RsaOaepDecrypt(_rsaKey, SHA1.Create(), new PKCS1MaskGenerationMethod(), rgbData); } @@ -54,6 +55,16 @@ public override void SetKey(AsymmetricAlgorithm key) { throw new ArgumentNullException("key"); Contract.EndContractBlock(); _rsaKey = (RSA) key; + _rsaOverridesDecrypt = default(bool?); + } + + private bool OverridesDecrypt { + get { + if (!_rsaOverridesDecrypt.HasValue) { + _rsaOverridesDecrypt = Utils.DoesRsaKeyOverride(_rsaKey, "Decrypt", new Type[] { typeof(byte[]), typeof(RSAEncryptionPadding) }); + } + return _rsaOverridesDecrypt.Value; + } } } } diff --git a/mscorlib/system/security/cryptography/rsaoaepkeyexchangeformatter.cs b/mscorlib/system/security/cryptography/rsaoaepkeyexchangeformatter.cs index 68746fe17..a931c154c 100644 --- a/mscorlib/system/security/cryptography/rsaoaepkeyexchangeformatter.cs +++ b/mscorlib/system/security/cryptography/rsaoaepkeyexchangeformatter.cs @@ -12,6 +12,7 @@ namespace System.Security.Cryptography { public class RSAOAEPKeyExchangeFormatter : AsymmetricKeyExchangeFormatter { private byte[] ParameterValue; private RSA _rsaKey; + private bool? _rsaOverridesEncrypt; private RandomNumberGenerator RngValue; // @@ -64,6 +65,7 @@ public override void SetKey(AsymmetricAlgorithm key) { throw new ArgumentNullException("key"); Contract.EndContractBlock(); _rsaKey = (RSA) key; + _rsaOverridesEncrypt = default(bool?); } [System.Security.SecuritySafeCritical] // auto-generated @@ -71,8 +73,8 @@ public override byte[] CreateKeyExchange(byte[] rgbData) { if (_rsaKey == null) throw new CryptographicUnexpectedOperationException(Environment.GetResourceString("Cryptography_MissingKey")); - if (_rsaKey is RSACryptoServiceProvider) { - return ((RSACryptoServiceProvider) _rsaKey).Encrypt(rgbData, true); + if (OverridesEncrypt) { + return _rsaKey.Encrypt(rgbData, RSAEncryptionPadding.OaepSHA1); } else { return Utils.RsaOaepEncrypt(_rsaKey, SHA1.Create(), new PKCS1MaskGenerationMethod(), RandomNumberGenerator.Create(), rgbData); } @@ -81,5 +83,14 @@ public override byte[] CreateKeyExchange(byte[] rgbData) { public override byte[] CreateKeyExchange(byte[] rgbData, Type symAlgType) { return CreateKeyExchange(rgbData); } + + private bool OverridesEncrypt { + get { + if (!_rsaOverridesEncrypt.HasValue) { + _rsaOverridesEncrypt = Utils.DoesRsaKeyOverride(_rsaKey, "Encrypt", new Type[] { typeof(byte[]), typeof(RSAEncryptionPadding) }); + } + return _rsaOverridesEncrypt.Value; + } + } } } diff --git a/mscorlib/system/security/cryptography/rsapkcs1keyexchangedeformatter.cs b/mscorlib/system/security/cryptography/rsapkcs1keyexchangedeformatter.cs index ed412c64d..ccd8570ad 100644 --- a/mscorlib/system/security/cryptography/rsapkcs1keyexchangedeformatter.cs +++ b/mscorlib/system/security/cryptography/rsapkcs1keyexchangedeformatter.cs @@ -11,6 +11,7 @@ namespace System.Security.Cryptography { [System.Runtime.InteropServices.ComVisible(true)] public class RSAPKCS1KeyExchangeDeformatter : AsymmetricKeyExchangeDeformatter { RSA _rsaKey; + bool? _rsaOverridesDecrypt; RandomNumberGenerator RngValue; // Constructors @@ -47,8 +48,8 @@ public override byte[] DecryptKeyExchange(byte[] rgbIn) { throw new CryptographicUnexpectedOperationException(Environment.GetResourceString("Cryptography_MissingKey")); byte[] rgbOut; - if (_rsaKey is RSACryptoServiceProvider) { - rgbOut = ((RSACryptoServiceProvider) _rsaKey).Decrypt(rgbIn, false); + if (OverridesDecrypt) { + rgbOut = _rsaKey.Decrypt(rgbIn, RSAEncryptionPadding.Pkcs1); } else { int i; @@ -83,6 +84,16 @@ public override void SetKey(AsymmetricAlgorithm key) { throw new ArgumentNullException("key"); Contract.EndContractBlock(); _rsaKey = (RSA) key; + _rsaOverridesDecrypt = default(bool?); + } + + private bool OverridesDecrypt { + get { + if (!_rsaOverridesDecrypt.HasValue) { + _rsaOverridesDecrypt = Utils.DoesRsaKeyOverride(_rsaKey, "Decrypt", new Type[] { typeof(byte[]), typeof(RSAEncryptionPadding) }); + } + return _rsaOverridesDecrypt.Value; + } } } } diff --git a/mscorlib/system/security/cryptography/rsapkcs1keyexchangeformatter.cs b/mscorlib/system/security/cryptography/rsapkcs1keyexchangeformatter.cs index c0c094bb3..92beef18b 100644 --- a/mscorlib/system/security/cryptography/rsapkcs1keyexchangeformatter.cs +++ b/mscorlib/system/security/cryptography/rsapkcs1keyexchangeformatter.cs @@ -14,6 +14,7 @@ namespace System.Security.Cryptography { public class RSAPKCS1KeyExchangeFormatter : AsymmetricKeyExchangeFormatter { RandomNumberGenerator RngValue; RSA _rsaKey; + bool? _rsaOverridesEncrypt; // // public constructors @@ -50,6 +51,7 @@ public override void SetKey(AsymmetricAlgorithm key) { throw new ArgumentNullException("key"); Contract.EndContractBlock(); _rsaKey = (RSA) key; + _rsaOverridesEncrypt = default(bool?); } public override byte[] CreateKeyExchange(byte[] rgbData) { @@ -57,8 +59,8 @@ public override byte[] CreateKeyExchange(byte[] rgbData) { throw new CryptographicUnexpectedOperationException(Environment.GetResourceString("Cryptography_MissingKey")); byte[] rgbKeyEx; - if (_rsaKey is RSACryptoServiceProvider) { - rgbKeyEx = ((RSACryptoServiceProvider) _rsaKey).Encrypt(rgbData, false); + if (OverridesEncrypt) { + rgbKeyEx = _rsaKey.Encrypt(rgbData, RSAEncryptionPadding.Pkcs1); } else { int cb = _rsaKey.KeySize/8; @@ -96,5 +98,14 @@ public override byte[] CreateKeyExchange(byte[] rgbData) { public override byte[] CreateKeyExchange(byte[] rgbData, Type symAlgType) { return CreateKeyExchange(rgbData); } + + private bool OverridesEncrypt { + get { + if (!_rsaOverridesEncrypt.HasValue) { + _rsaOverridesEncrypt = Utils.DoesRsaKeyOverride(_rsaKey, "Encrypt", new Type[] { typeof(byte[]), typeof(RSAEncryptionPadding) }); + } + return _rsaOverridesEncrypt.Value; + } + } } } diff --git a/mscorlib/system/security/cryptography/rsapkcs1signaturedeformatter.cs b/mscorlib/system/security/cryptography/rsapkcs1signaturedeformatter.cs index 01363fefb..28ee87386 100644 --- a/mscorlib/system/security/cryptography/rsapkcs1signaturedeformatter.cs +++ b/mscorlib/system/security/cryptography/rsapkcs1signaturedeformatter.cs @@ -27,6 +27,7 @@ public class RSAPKCS1SignatureDeformatter : AsymmetricSignatureDeformatter { private RSA _rsaKey; // RSA Key value to do decrypt operation private String _strOID; // OID value for the HASH algorithm + private bool? _rsaOverridesVerifyHash; // // public constructors @@ -49,6 +50,7 @@ public override void SetKey(AsymmetricAlgorithm key) { throw new ArgumentNullException("key"); Contract.EndContractBlock(); _rsaKey = (RSA) key; + _rsaOverridesVerifyHash = default(bool?); } public override void SetHashAlgorithm(String strName) { @@ -70,15 +72,32 @@ public override bool VerifySignature(byte[] rgbHash, byte[] rgbSignature) { // Two cases here -- if we are talking to the CSP version or if we are talking to some other RSA provider. if (_rsaKey is RSACryptoServiceProvider) { + // This path is kept around for desktop compat: in case someone is using this with a hash algorithm that's known to GetAlgIdFromOid but + // not from OidToHashAlgorithmName. int calgHash = X509Utils.GetAlgIdFromOid(_strOID, OidGroup.HashAlgorithm); return ((RSACryptoServiceProvider)_rsaKey).VerifyHash(rgbHash, calgHash, rgbSignature); } + else if (OverridesVerifyHash) { + HashAlgorithmName hashAlgorithmName = Utils.OidToHashAlgorithmName(_strOID); + return _rsaKey.VerifyHash(rgbHash, rgbSignature, hashAlgorithmName, RSASignaturePadding.Pkcs1); + } else { + // Fallback compat path for 3rd-party RSA classes that don't override VerifyHash() + byte[] pad = Utils.RsaPkcs1Padding(_rsaKey, CryptoConfig.EncodeOID(_strOID), rgbHash); // Apply the public key to the signature data to get back the padded buffer actually signed. // Compare the two buffers to see if they match; ignoring any leading zeros return Utils.CompareBigIntArrays(_rsaKey.EncryptValue(rgbSignature), pad); } } + + private bool OverridesVerifyHash { + get { + if (!_rsaOverridesVerifyHash.HasValue) { + _rsaOverridesVerifyHash = Utils.DoesRsaKeyOverride(_rsaKey, "VerifyHash", new Type[] { typeof(byte[]), typeof(byte[]), typeof(HashAlgorithmName), typeof(RSASignaturePadding) }); + } + return _rsaOverridesVerifyHash.Value; + } + } } } diff --git a/mscorlib/system/security/cryptography/rsapkcs1signatureformatter.cs b/mscorlib/system/security/cryptography/rsapkcs1signatureformatter.cs index 249ea4b07..979124256 100644 --- a/mscorlib/system/security/cryptography/rsapkcs1signatureformatter.cs +++ b/mscorlib/system/security/cryptography/rsapkcs1signatureformatter.cs @@ -19,6 +19,7 @@ namespace System.Security.Cryptography { public class RSAPKCS1SignatureFormatter : AsymmetricSignatureFormatter { private RSA _rsaKey; private String _strOID; + private bool? _rsaOverridesSignHash; // // public constructors @@ -42,6 +43,7 @@ public override void SetKey(AsymmetricAlgorithm key) { throw new ArgumentNullException("key"); Contract.EndContractBlock(); _rsaKey = (RSA) key; + _rsaOverridesSignHash = default(bool?); } public override void SetHashAlgorithm(String strName) { @@ -61,14 +63,31 @@ public override byte[] CreateSignature(byte[] rgbHash) { // Two cases here -- if we are talking to the CSP version or if we are talking to some other RSA provider. if (_rsaKey is RSACryptoServiceProvider) { + // This path is kept around for desktop compat: in case someone is using this with a hash algorithm that's known to GetAlgIdFromOid but + // not from OidToHashAlgorithmName. int calgHash = X509Utils.GetAlgIdFromOid(_strOID, OidGroup.HashAlgorithm); return ((RSACryptoServiceProvider)_rsaKey).SignHash(rgbHash, calgHash); } + else if (OverridesSignHash) { + HashAlgorithmName hashAlgorithmName = Utils.OidToHashAlgorithmName(_strOID); + return _rsaKey.SignHash(rgbHash, hashAlgorithmName, RSASignaturePadding.Pkcs1); + } else { + // Fallback compat path for 3rd-party RSA classes that don't override SignHash() + byte[] pad = Utils.RsaPkcs1Padding(_rsaKey, CryptoConfig.EncodeOID(_strOID), rgbHash); // Create the signature by applying the private key to the padded buffer we just created. return _rsaKey.DecryptValue(pad); } } + + private bool OverridesSignHash { + get { + if (!_rsaOverridesSignHash.HasValue) { + _rsaOverridesSignHash = Utils.DoesRsaKeyOverride(_rsaKey, "SignHash", new Type[] { typeof(byte[]), typeof(HashAlgorithmName), typeof(RSASignaturePadding) }); + } + return _rsaOverridesSignHash.Value; + } + } } } diff --git a/mscorlib/system/security/cryptography/signaturedescription.cs b/mscorlib/system/security/cryptography/signaturedescription.cs index f115e8afc..330a0222f 100644 --- a/mscorlib/system/security/cryptography/signaturedescription.cs +++ b/mscorlib/system/security/cryptography/signaturedescription.cs @@ -83,20 +83,52 @@ public virtual HashAlgorithm CreateDigest() { } } - internal class RSAPKCS1SHA1SignatureDescription : SignatureDescription { - public RSAPKCS1SHA1SignatureDescription() { - KeyAlgorithm = "System.Security.Cryptography.RSACryptoServiceProvider"; - DigestAlgorithm = "System.Security.Cryptography.SHA1CryptoServiceProvider"; + internal abstract class RSAPKCS1SignatureDescription : SignatureDescription { + protected RSAPKCS1SignatureDescription(string hashAlgorithm, string digestAlgorithm) { + KeyAlgorithm = "System.Security.Cryptography.RSA"; + DigestAlgorithm = digestAlgorithm; FormatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureFormatter"; DeformatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureDeformatter"; + _hashAlgorithm = hashAlgorithm; + } + + public sealed override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key) { + AsymmetricSignatureDeformatter item = base.CreateDeformatter(key); + item.SetHashAlgorithm(_hashAlgorithm); + return item; } - public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key) { - AsymmetricSignatureDeformatter item = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName(DeformatterAlgorithm); - item.SetKey(key); - item.SetHashAlgorithm("SHA1"); + public sealed override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key) { + AsymmetricSignatureFormatter item = base.CreateFormatter(key); + item.SetHashAlgorithm(_hashAlgorithm); return item; } + + private string _hashAlgorithm; + } + + internal class RSAPKCS1SHA1SignatureDescription : RSAPKCS1SignatureDescription { + public RSAPKCS1SHA1SignatureDescription() + : base("SHA1", "System.Security.Cryptography.SHA1Cng") { + } + } + + internal class RSAPKCS1SHA256SignatureDescription : RSAPKCS1SignatureDescription { + public RSAPKCS1SHA256SignatureDescription() + : base("SHA256", "System.Security.Cryptography.SHA256Cng") { + } + } + + internal class RSAPKCS1SHA384SignatureDescription : RSAPKCS1SignatureDescription { + public RSAPKCS1SHA384SignatureDescription() + : base("SHA384", "System.Security.Cryptography.SHA384Cng") { + } + } + + internal class RSAPKCS1SHA512SignatureDescription : RSAPKCS1SignatureDescription { + public RSAPKCS1SHA512SignatureDescription() + : base("SHA512", "System.Security.Cryptography.SHA512Cng") { + } } internal class DSASignatureDescription : SignatureDescription { diff --git a/mscorlib/system/security/cryptography/utils.cs b/mscorlib/system/security/cryptography/utils.cs index ffdfc91d5..6f9a9c4e7 100644 --- a/mscorlib/system/security/cryptography/utils.cs +++ b/mscorlib/system/security/cryptography/utils.cs @@ -16,6 +16,7 @@ namespace System.Security.Cryptography { using Microsoft.Win32; using System.IO; + using System.Reflection; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -987,6 +988,73 @@ internal static bool CompareBigIntArrays (byte[] lhs, byte[] rhs) { return true; } + internal static HashAlgorithmName OidToHashAlgorithmName(string oid) + { + switch (oid) + { + case Constants.OID_OIWSEC_SHA1: + return HashAlgorithmName.SHA1; + + case Constants.OID_OIWSEC_SHA256: + return HashAlgorithmName.SHA256; + + case Constants.OID_OIWSEC_SHA384: + return HashAlgorithmName.SHA384; + + case Constants.OID_OIWSEC_SHA512: + return HashAlgorithmName.SHA512; + + default: + throw new NotSupportedException(); + } + } + + // + // Backward-compat hack for third-party RSA-derived classes: + // + // Because the SignHash()/VerifyHash()/Encrypt()/Decrypt() methods are new on RSA, we may + // encounter older third-party RSA-derived classes that don't override them + // (and if they don't override them, these methods will throw since they are effectively abstract methods that had to declared non-abstract + // for backward compat reasons.) + // + internal static bool DoesRsaKeyOverride(RSA rsaKey, string methodName, Type[] parameterTypes) + { + // A fast-path check for the common cases where we know we implemented the overrides. + Type t = rsaKey.GetType(); + if (rsaKey is RSACryptoServiceProvider) + { +#if DEBUG + // On checked builds, do the slow-path check anyway so it gets exercised. + bool foundOverride = DoesRsaKeyOverrideSlowPath(t, methodName, parameterTypes); + BCLDebug.Assert(foundOverride, "RSACryptoServiceProvider expected to override " + methodName); +#endif + return true; + } + + string fullName = t.FullName; + if (fullName == "System.Security.Cryptography.RSACng") + { +#if DEBUG + // On checked builds, do the slow-path check anyway so it gets exercised. + bool foundOverride = DoesRsaKeyOverrideSlowPath(t, methodName, parameterTypes); + BCLDebug.Assert(foundOverride, "RSACng expected to override " + methodName); +#endif + return true; + } + return DoesRsaKeyOverrideSlowPath(t, methodName, parameterTypes); + } + + private static bool DoesRsaKeyOverrideSlowPath(Type t, string methodName, Type[] parameterTypes) + { + MethodInfo method = t.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance, null, parameterTypes, null); + BCLDebug.Assert(method != null, "method != null"); + Type declaringType = method.DeclaringType; + if (declaringType == typeof(RSA)) + return false; + + return true; + } + [System.Security.SecurityCritical] // auto-generated [ResourceExposure(ResourceScope.None)] // Creates a process resource, but it can't be scoped. [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity] diff --git a/mscorlib/system/security/cryptography/x509certificates/x509utils.cs b/mscorlib/system/security/cryptography/x509certificates/x509utils.cs index 75c68753d..e59871861 100644 --- a/mscorlib/system/security/cryptography/x509certificates/x509utils.cs +++ b/mscorlib/system/security/cryptography/x509certificates/x509utils.cs @@ -159,7 +159,7 @@ private static CRYPT_OID_INFO FindOidInfo(OidKeyType keyType, string key, OidGro if (group != OidGroup.AllGroups) { IntPtr allGroupOidInfo = CryptFindOIDInfo(keyType, rawKey, OidGroup.AllGroups); if (allGroupOidInfo != IntPtr.Zero) { - return (CRYPT_OID_INFO)Marshal.PtrToStructure(fullOidInfo, typeof(CRYPT_OID_INFO)); + return (CRYPT_OID_INFO)Marshal.PtrToStructure(allGroupOidInfo, typeof(CRYPT_OID_INFO)); } } diff --git a/mscorlib/system/security/permissions/fileiopermission.cs b/mscorlib/system/security/permissions/fileiopermission.cs index 02ef088b5..db35ac502 100644 --- a/mscorlib/system/security/permissions/fileiopermission.cs +++ b/mscorlib/system/security/permissions/fileiopermission.cs @@ -24,9 +24,9 @@ namespace System.Security.Permissions { using System.Runtime.Versioning; using System.Diagnostics.Contracts; -[Serializable] + [Serializable] [Flags] -[System.Runtime.InteropServices.ComVisible(true)] + [System.Runtime.InteropServices.ComVisible(true)] public enum FileIOPermissionAccess { NoAccess = 0x00, @@ -36,9 +36,8 @@ public enum FileIOPermissionAccess PathDiscovery = 0x08, AllAccess = 0x0F, } - - -[System.Runtime.InteropServices.ComVisible(true)] + + [System.Runtime.InteropServices.ComVisible(true)] [Serializable] sealed public class FileIOPermission : CodeAccessPermission, IUnrestrictedPermission, IBuiltInPermission { @@ -51,7 +50,7 @@ sealed public class FileIOPermission : CodeAccessPermission, IUnrestrictedPermis [OptionalField(VersionAdded = 2)] private FileIOAccess m_changeAcl; private bool m_unrestricted; - + public FileIOPermission(PermissionState state) { if (state == PermissionState.Unrestricted) @@ -67,91 +66,91 @@ public FileIOPermission(PermissionState state) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPermissionState")); } } - + [System.Security.SecuritySafeCritical] // auto-generated - public FileIOPermission( FileIOPermissionAccess access, String path ) + public FileIOPermission(FileIOPermissionAccess access, String path) { - VerifyAccess( access ); - + VerifyAccess(access); + String[] pathList = new String[] { path }; - AddPathList( access, pathList, false, true, false ); + AddPathList(access, pathList, false, true, false); } - + [System.Security.SecuritySafeCritical] // auto-generated - public FileIOPermission( FileIOPermissionAccess access, String[] pathList ) + public FileIOPermission(FileIOPermissionAccess access, String[] pathList) { - VerifyAccess( access ); - - AddPathList( access, pathList, false, true, false ); + VerifyAccess(access); + + AddPathList(access, pathList, false, true, false); } #if FEATURE_MACL [System.Security.SecuritySafeCritical] // auto-generated - public FileIOPermission( FileIOPermissionAccess access, AccessControlActions control, String path ) + public FileIOPermission(FileIOPermissionAccess access, AccessControlActions control, String path) { - VerifyAccess( access ); - + VerifyAccess(access); + String[] pathList = new String[] { path }; - AddPathList( access, control, pathList, false, true, false ); + AddPathList(access, control, pathList, false, true, false); } - + [System.Security.SecuritySafeCritical] // auto-generated - public FileIOPermission( FileIOPermissionAccess access, AccessControlActions control, String[] pathList ) - : this( access, control, pathList, true, true ) + public FileIOPermission(FileIOPermissionAccess access, AccessControlActions control, String[] pathList) + : this(access, control, pathList, true, true) { } #endif [System.Security.SecurityCritical] // auto-generated - internal FileIOPermission( FileIOPermissionAccess access, String[] pathList, bool checkForDuplicates, bool needFullPath ) + internal FileIOPermission(FileIOPermissionAccess access, String[] pathList, bool checkForDuplicates, bool needFullPath) { - VerifyAccess( access ); - - AddPathList( access, pathList, checkForDuplicates, needFullPath, true ); + VerifyAccess(access); + + AddPathList(access, pathList, checkForDuplicates, needFullPath, true); } #if FEATURE_MACL [System.Security.SecurityCritical] // auto-generated - internal FileIOPermission( FileIOPermissionAccess access, AccessControlActions control, String[] pathList, bool checkForDuplicates, bool needFullPath ) + internal FileIOPermission(FileIOPermissionAccess access, AccessControlActions control, String[] pathList, bool checkForDuplicates, bool needFullPath) { - VerifyAccess( access ); - - AddPathList( access, control, pathList, checkForDuplicates, needFullPath, true ); + VerifyAccess(access); + + AddPathList(access, control, pathList, checkForDuplicates, needFullPath, true); } #endif - public void SetPathList( FileIOPermissionAccess access, String path ) + public void SetPathList(FileIOPermissionAccess access, String path) { String[] pathList; - if(path == null) - pathList = new String[] {}; + if (path == null) + pathList = new String[] { }; else pathList = new String[] { path }; - SetPathList( access, pathList, false ); + SetPathList(access, pathList, false); } - - public void SetPathList( FileIOPermissionAccess access, String[] pathList ) + + public void SetPathList(FileIOPermissionAccess access, String[] pathList) { - SetPathList( access, pathList, true ); + SetPathList(access, pathList, true); } - internal void SetPathList( FileIOPermissionAccess access, - String[] pathList, bool checkForDuplicates ) + internal void SetPathList(FileIOPermissionAccess access, + String[] pathList, bool checkForDuplicates) { - SetPathList( access, AccessControlActions.None, pathList, checkForDuplicates ); + SetPathList(access, AccessControlActions.None, pathList, checkForDuplicates); } [System.Security.SecuritySafeCritical] // auto-generated - internal void SetPathList( FileIOPermissionAccess access, AccessControlActions control, String[] pathList, bool checkForDuplicates ) + internal void SetPathList(FileIOPermissionAccess access, AccessControlActions control, String[] pathList, bool checkForDuplicates) { - VerifyAccess( access ); - + VerifyAccess(access); + if ((access & FileIOPermissionAccess.Read) != 0) m_read = null; - + if ((access & FileIOPermissionAccess.Write) != 0) m_write = null; - + if ((access & FileIOPermissionAccess.Append) != 0) m_append = null; @@ -168,36 +167,36 @@ internal void SetPathList( FileIOPermissionAccess access, AccessControlActions c m_viewAcl = null; m_changeAcl = null; #endif - + m_unrestricted = false; #if FEATURE_MACL - AddPathList( access, control, pathList, checkForDuplicates, true, true ); + AddPathList(access, control, pathList, checkForDuplicates, true, true); #else AddPathList( access, pathList, checkForDuplicates, true, true ); #endif } [System.Security.SecuritySafeCritical] // auto-generated - public void AddPathList( FileIOPermissionAccess access, String path ) + public void AddPathList(FileIOPermissionAccess access, String path) { String[] pathList; - if(path == null) - pathList = new String[] {}; + if (path == null) + pathList = new String[] { }; else pathList = new String[] { path }; - AddPathList( access, pathList, false, true, false ); + AddPathList(access, pathList, false, true, false); } [System.Security.SecuritySafeCritical] // auto-generated - public void AddPathList( FileIOPermissionAccess access, String[] pathList ) + public void AddPathList(FileIOPermissionAccess access, String[] pathList) { - AddPathList( access, pathList, true, true, true ); + AddPathList(access, pathList, true, true, true); } [System.Security.SecurityCritical] // auto-generated - internal void AddPathList( FileIOPermissionAccess access, String[] pathListOrig, bool checkForDuplicates, bool needFullPath, bool copyPathList ) + internal void AddPathList(FileIOPermissionAccess access, String[] pathListOrig, bool checkForDuplicates, bool needFullPath, bool copyPathList) { - AddPathList( access, AccessControlActions.None, pathListOrig, checkForDuplicates, needFullPath, copyPathList ); + AddPathList(access, AccessControlActions.None, pathListOrig, checkForDuplicates, needFullPath, copyPathList); } [System.Security.SecurityCritical] // auto-generated @@ -205,65 +204,73 @@ internal void AddPathList(FileIOPermissionAccess access, AccessControlActions co { if (pathListOrig == null) { - throw new ArgumentNullException( "pathList" ); + throw new ArgumentNullException("pathList"); } if (pathListOrig.Length == 0) { - throw new ArgumentException( Environment.GetResourceString("Argument_EmptyPath" )); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); } Contract.EndContractBlock(); // @ VerifyAccess(access); - + if (m_unrestricted) return; String[] pathList = pathListOrig; - if(copyPathList) + if (copyPathList) { // Make a copy of pathList (in case its value changes after we check for illegal chars) pathList = new String[pathListOrig.Length]; Array.Copy(pathListOrig, pathList, pathListOrig.Length); } - CheckIllegalCharacters( pathList ); + // If we need the full path the standard illegal characters will be checked in StringExpressionSet. + CheckIllegalCharacters(pathList, onlyCheckExtras: needFullPath); + + // StringExpressionSet will do minor normalization, trimming spaces and replacing alternate + // directory separators. It will make an attemt to expand short file names and will check + // for standard colon placement. + // + // If needFullPath is true it will call NormalizePath- which performs short name expansion + // and does the normal validity checks. ArrayList pathArrayList = StringExpressionSet.CreateListFromExpressions(pathList, needFullPath); - + if ((access & FileIOPermissionAccess.Read) != 0) { if (m_read == null) { m_read = new FileIOAccess(); } - m_read.AddExpressions( pathArrayList, checkForDuplicates); + m_read.AddExpressions(pathArrayList, checkForDuplicates); } - + if ((access & FileIOPermissionAccess.Write) != 0) { if (m_write == null) { m_write = new FileIOAccess(); } - m_write.AddExpressions( pathArrayList, checkForDuplicates); + m_write.AddExpressions(pathArrayList, checkForDuplicates); } - + if ((access & FileIOPermissionAccess.Append) != 0) { if (m_append == null) { m_append = new FileIOAccess(); } - m_append.AddExpressions( pathArrayList, checkForDuplicates); + m_append.AddExpressions(pathArrayList, checkForDuplicates); } if ((access & FileIOPermissionAccess.PathDiscovery) != 0) { if (m_pathDiscovery == null) { - m_pathDiscovery = new FileIOAccess( true ); + m_pathDiscovery = new FileIOAccess(true); } - m_pathDiscovery.AddExpressions( pathArrayList, checkForDuplicates); + m_pathDiscovery.AddExpressions(pathArrayList, checkForDuplicates); } #if FEATURE_MACL @@ -273,7 +280,7 @@ internal void AddPathList(FileIOPermissionAccess access, AccessControlActions co { m_viewAcl = new FileIOAccess(); } - m_viewAcl.AddExpressions( pathArrayList, checkForDuplicates); + m_viewAcl.AddExpressions(pathArrayList, checkForDuplicates); } if ((control & AccessControlActions.Change) != 0) @@ -282,18 +289,18 @@ internal void AddPathList(FileIOPermissionAccess access, AccessControlActions co { m_changeAcl = new FileIOAccess(); } - m_changeAcl.AddExpressions( pathArrayList, checkForDuplicates); + m_changeAcl.AddExpressions(pathArrayList, checkForDuplicates); } #endif } - + [SecuritySafeCritical] - public String[] GetPathList( FileIOPermissionAccess access ) + public String[] GetPathList(FileIOPermissionAccess access) { - VerifyAccess( access ); - ExclusiveAccess( access ); - - if (AccessIsSet( access, FileIOPermissionAccess.Read )) + VerifyAccess(access); + ExclusiveAccess(access); + + if (AccessIsSet(access, FileIOPermissionAccess.Read)) { if (m_read == null) { @@ -301,8 +308,8 @@ public String[] GetPathList( FileIOPermissionAccess access ) } return m_read.ToStringArray(); } - - if (AccessIsSet( access, FileIOPermissionAccess.Write )) + + if (AccessIsSet(access, FileIOPermissionAccess.Write)) { if (m_write == null) { @@ -310,8 +317,8 @@ public String[] GetPathList( FileIOPermissionAccess access ) } return m_write.ToStringArray(); } - - if (AccessIsSet( access, FileIOPermissionAccess.Append )) + + if (AccessIsSet(access, FileIOPermissionAccess.Append)) { if (m_append == null) { @@ -319,8 +326,8 @@ public String[] GetPathList( FileIOPermissionAccess access ) } return m_append.ToStringArray(); } - - if (AccessIsSet( access, FileIOPermissionAccess.PathDiscovery )) + + if (AccessIsSet(access, FileIOPermissionAccess.PathDiscovery)) { if (m_pathDiscovery == null) { @@ -330,10 +337,10 @@ public String[] GetPathList( FileIOPermissionAccess access ) } // not reached - + return null; } - + public FileIOPermissionAccess AllLocalFiles { @@ -341,19 +348,19 @@ public FileIOPermissionAccess AllLocalFiles { if (m_unrestricted) return FileIOPermissionAccess.AllAccess; - + FileIOPermissionAccess access = FileIOPermissionAccess.NoAccess; - + if (m_read != null && m_read.AllLocalFiles) { access |= FileIOPermissionAccess.Read; } - + if (m_write != null && m_write.AllLocalFiles) { access |= FileIOPermissionAccess.Write; } - + if (m_append != null && m_append.AllLocalFiles) { access |= FileIOPermissionAccess.Append; @@ -363,17 +370,17 @@ public FileIOPermissionAccess AllLocalFiles { access |= FileIOPermissionAccess.PathDiscovery; } - + return access; } - + set { if ((value & FileIOPermissionAccess.Read) != 0) { if (m_read == null) m_read = new FileIOAccess(); - + m_read.AllLocalFiles = true; } else @@ -381,12 +388,12 @@ public FileIOPermissionAccess AllLocalFiles if (m_read != null) m_read.AllLocalFiles = false; } - + if ((value & FileIOPermissionAccess.Write) != 0) { if (m_write == null) m_write = new FileIOAccess(); - + m_write.AllLocalFiles = true; } else @@ -394,12 +401,12 @@ public FileIOPermissionAccess AllLocalFiles if (m_write != null) m_write.AllLocalFiles = false; } - + if ((value & FileIOPermissionAccess.Append) != 0) { if (m_append == null) m_append = new FileIOAccess(); - + m_append.AllLocalFiles = true; } else @@ -411,8 +418,8 @@ public FileIOPermissionAccess AllLocalFiles if ((value & FileIOPermissionAccess.PathDiscovery) != 0) { if (m_pathDiscovery == null) - m_pathDiscovery = new FileIOAccess( true ); - + m_pathDiscovery = new FileIOAccess(true); + m_pathDiscovery.AllLocalFiles = true; } else @@ -423,31 +430,31 @@ public FileIOPermissionAccess AllLocalFiles } } - + public FileIOPermissionAccess AllFiles { get { if (m_unrestricted) return FileIOPermissionAccess.AllAccess; - + FileIOPermissionAccess access = FileIOPermissionAccess.NoAccess; - + if (m_read != null && m_read.AllFiles) { access |= FileIOPermissionAccess.Read; } - + if (m_write != null && m_write.AllFiles) { access |= FileIOPermissionAccess.Write; } - + if (m_append != null && m_append.AllFiles) { access |= FileIOPermissionAccess.Append; } - + if (m_pathDiscovery != null && m_pathDiscovery.AllFiles) { access |= FileIOPermissionAccess.PathDiscovery; @@ -455,7 +462,7 @@ public FileIOPermissionAccess AllFiles return access; } - + set { if (value == FileIOPermissionAccess.AllAccess) @@ -463,12 +470,12 @@ public FileIOPermissionAccess AllFiles m_unrestricted = true; return; } - + if ((value & FileIOPermissionAccess.Read) != 0) { if (m_read == null) m_read = new FileIOAccess(); - + m_read.AllFiles = true; } else @@ -476,12 +483,12 @@ public FileIOPermissionAccess AllFiles if (m_read != null) m_read.AllFiles = false; } - + if ((value & FileIOPermissionAccess.Write) != 0) { if (m_write == null) m_write = new FileIOAccess(); - + m_write.AllFiles = true; } else @@ -489,12 +496,12 @@ public FileIOPermissionAccess AllFiles if (m_write != null) m_write.AllFiles = false; } - + if ((value & FileIOPermissionAccess.Append) != 0) { if (m_append == null) m_append = new FileIOAccess(); - + m_append.AllFiles = true; } else @@ -506,8 +513,8 @@ public FileIOPermissionAccess AllFiles if ((value & FileIOPermissionAccess.PathDiscovery) != 0) { if (m_pathDiscovery == null) - m_pathDiscovery = new FileIOAccess( true ); - + m_pathDiscovery = new FileIOAccess(true); + m_pathDiscovery.AllFiles = true; } else @@ -517,42 +524,70 @@ public FileIOPermissionAccess AllFiles } } - } - + } + [Pure] - private static void VerifyAccess( FileIOPermissionAccess access ) + private static void VerifyAccess(FileIOPermissionAccess access) { if ((access & ~FileIOPermissionAccess.AllAccess) != 0) throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", (int)access)); } - + [Pure] - private static void ExclusiveAccess( FileIOPermissionAccess access ) + private static void ExclusiveAccess(FileIOPermissionAccess access) { if (access == FileIOPermissionAccess.NoAccess) { - throw new ArgumentException( Environment.GetResourceString("Arg_EnumNotSingleFlag") ); + throw new ArgumentException(Environment.GetResourceString("Arg_EnumNotSingleFlag")); } - - if (((int) access & ((int)access-1)) != 0) + + if (((int)access & ((int)access - 1)) != 0) { - throw new ArgumentException( Environment.GetResourceString("Arg_EnumNotSingleFlag") ); + throw new ArgumentException(Environment.GetResourceString("Arg_EnumNotSingleFlag")); } } - private static void CheckIllegalCharacters( String[] str ) + private static void CheckIllegalCharacters(String[] str, bool onlyCheckExtras) { for (int i = 0; i < str.Length; ++i) { - Path.CheckInvalidPathChars(str[i], true); + // FileIOPermission doesn't allow for normalizing across various volume names. This means "C:\" and + // "\\?\C:\" won't be considered correctly. In addition there are many other aliases for the volume + // besides "C:" such as (in one concrete example) "\\?\Harddisk0Partition2\", "\\?\HarddiskVolume6\", + // "\\?\Volume{d1655348-0000-0000-0000-f01500000000}\", etc. + // + // We'll continue to explicitly block extended syntax here by disallowing wildcards no matter where + // they occur in the string (e.g. \\?\ isn't ok) + if (CheckExtraPathCharacters(str[i])) + throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars")); + + if (!onlyCheckExtras) + Path.CheckInvalidPathChars(str[i]); } } - private static bool AccessIsSet( FileIOPermissionAccess access, FileIOPermissionAccess question ) + /// + /// Check for ?,* and null, ignoring extended syntax. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe static bool CheckExtraPathCharacters(string path) + { + char currentChar; + for (int i = 0; i < path.Length; i++) + { + currentChar = path[i]; + + // We also check for null here as StringExpressionSet will trim it out. (Ensuring we still throw as we always have.) + if (currentChar == '*' || currentChar == '?' || currentChar == '\0') return true; + } + return false; + } + + private static bool AccessIsSet(FileIOPermissionAccess access, FileIOPermissionAccess question) { return (access & question) != 0; } - + private bool IsEmpty() { return (!m_unrestricted && @@ -563,24 +598,24 @@ private bool IsEmpty() (this.m_viewAcl == null || this.m_viewAcl.IsEmpty()) && (this.m_changeAcl == null || this.m_changeAcl.IsEmpty())); } - + //------------------------------------------------------ // // CODEACCESSPERMISSION IMPLEMENTATION // //------------------------------------------------------ - + public bool IsUnrestricted() { return m_unrestricted; } - + //------------------------------------------------------ // // IPERMISSION IMPLEMENTATION // //------------------------------------------------------ - + public override bool IsSubsetOf(IPermission target) { if (target == null) @@ -597,14 +632,14 @@ public override bool IsSubsetOf(IPermission target) else if (this.IsUnrestricted()) return false; else - return ((this.m_read == null || this.m_read.IsSubsetOf( operand.m_read )) && - (this.m_write == null || this.m_write.IsSubsetOf( operand.m_write )) && - (this.m_append == null || this.m_append.IsSubsetOf( operand.m_append )) && - (this.m_pathDiscovery == null || this.m_pathDiscovery.IsSubsetOf( operand.m_pathDiscovery )) && - (this.m_viewAcl == null || this.m_viewAcl.IsSubsetOf( operand.m_viewAcl )) && - (this.m_changeAcl == null || this.m_changeAcl.IsSubsetOf( operand.m_changeAcl ))); + return ((this.m_read == null || this.m_read.IsSubsetOf(operand.m_read)) && + (this.m_write == null || this.m_write.IsSubsetOf(operand.m_write)) && + (this.m_append == null || this.m_append.IsSubsetOf(operand.m_append)) && + (this.m_pathDiscovery == null || this.m_pathDiscovery.IsSubsetOf(operand.m_pathDiscovery)) && + (this.m_viewAcl == null || this.m_viewAcl.IsSubsetOf(operand.m_viewAcl)) && + (this.m_changeAcl == null || this.m_changeAcl.IsSubsetOf(operand.m_changeAcl))); } - + public override IPermission Intersect(IPermission target) { if (target == null) @@ -622,18 +657,18 @@ public override IPermission Intersect(IPermission target) { return target.Copy(); } - + if (operand.IsUnrestricted()) { return this.Copy(); } - - FileIOAccess intersectRead = this.m_read == null ? null : this.m_read.Intersect( operand.m_read ); - FileIOAccess intersectWrite = this.m_write == null ? null : this.m_write.Intersect( operand.m_write ); - FileIOAccess intersectAppend = this.m_append == null ? null : this.m_append.Intersect( operand.m_append ); - FileIOAccess intersectPathDiscovery = this.m_pathDiscovery == null ? null : this.m_pathDiscovery.Intersect( operand.m_pathDiscovery ); - FileIOAccess intersectViewAcl = this.m_viewAcl == null ? null : this.m_viewAcl.Intersect( operand.m_viewAcl ); - FileIOAccess intersectChangeAcl = this.m_changeAcl == null ? null : this.m_changeAcl.Intersect( operand.m_changeAcl ); + + FileIOAccess intersectRead = this.m_read == null ? null : this.m_read.Intersect(operand.m_read); + FileIOAccess intersectWrite = this.m_write == null ? null : this.m_write.Intersect(operand.m_write); + FileIOAccess intersectAppend = this.m_append == null ? null : this.m_append.Intersect(operand.m_append); + FileIOAccess intersectPathDiscovery = this.m_pathDiscovery == null ? null : this.m_pathDiscovery.Intersect(operand.m_pathDiscovery); + FileIOAccess intersectViewAcl = this.m_viewAcl == null ? null : this.m_viewAcl.Intersect(operand.m_viewAcl); + FileIOAccess intersectChangeAcl = this.m_changeAcl == null ? null : this.m_changeAcl.Intersect(operand.m_changeAcl); if ((intersectRead == null || intersectRead.IsEmpty()) && (intersectWrite == null || intersectWrite.IsEmpty()) && @@ -644,7 +679,7 @@ public override IPermission Intersect(IPermission target) { return null; } - + FileIOPermission intersectPermission = new FileIOPermission(PermissionState.None); intersectPermission.m_unrestricted = false; intersectPermission.m_read = intersectRead; @@ -653,10 +688,10 @@ public override IPermission Intersect(IPermission target) intersectPermission.m_pathDiscovery = intersectPathDiscovery; intersectPermission.m_viewAcl = intersectViewAcl; intersectPermission.m_changeAcl = intersectChangeAcl; - + return intersectPermission; } - + public override IPermission Union(IPermission other) { if (other == null) @@ -670,19 +705,19 @@ public override IPermission Union(IPermission other) { throw new ArgumentException(Environment.GetResourceString("Argument_WrongType", this.GetType().FullName)); } - + if (this.IsUnrestricted() || operand.IsUnrestricted()) { - return new FileIOPermission( PermissionState.Unrestricted ); + return new FileIOPermission(PermissionState.Unrestricted); } - - FileIOAccess unionRead = this.m_read == null ? operand.m_read : this.m_read.Union( operand.m_read ); - FileIOAccess unionWrite = this.m_write == null ? operand.m_write : this.m_write.Union( operand.m_write ); - FileIOAccess unionAppend = this.m_append == null ? operand.m_append : this.m_append.Union( operand.m_append ); - FileIOAccess unionPathDiscovery = this.m_pathDiscovery == null ? operand.m_pathDiscovery : this.m_pathDiscovery.Union( operand.m_pathDiscovery ); - FileIOAccess unionViewAcl = this.m_viewAcl == null ? operand.m_viewAcl : this.m_viewAcl.Union( operand.m_viewAcl ); - FileIOAccess unionChangeAcl = this.m_changeAcl == null ? operand.m_changeAcl : this.m_changeAcl.Union( operand.m_changeAcl ); - + + FileIOAccess unionRead = this.m_read == null ? operand.m_read : this.m_read.Union(operand.m_read); + FileIOAccess unionWrite = this.m_write == null ? operand.m_write : this.m_write.Union(operand.m_write); + FileIOAccess unionAppend = this.m_append == null ? operand.m_append : this.m_append.Union(operand.m_append); + FileIOAccess unionPathDiscovery = this.m_pathDiscovery == null ? operand.m_pathDiscovery : this.m_pathDiscovery.Union(operand.m_pathDiscovery); + FileIOAccess unionViewAcl = this.m_viewAcl == null ? operand.m_viewAcl : this.m_viewAcl.Union(operand.m_viewAcl); + FileIOAccess unionChangeAcl = this.m_changeAcl == null ? operand.m_changeAcl : this.m_changeAcl.Union(operand.m_changeAcl); + if ((unionRead == null || unionRead.IsEmpty()) && (unionWrite == null || unionWrite.IsEmpty()) && (unionAppend == null || unionAppend.IsEmpty()) && @@ -692,7 +727,7 @@ public override IPermission Union(IPermission other) { return null; } - + FileIOPermission unionPermission = new FileIOPermission(PermissionState.None); unionPermission.m_unrestricted = false; unionPermission.m_read = unionRead; @@ -702,9 +737,9 @@ public override IPermission Union(IPermission other) unionPermission.m_viewAcl = unionViewAcl; unionPermission.m_changeAcl = unionChangeAcl; - return unionPermission; + return unionPermission; } - + public override IPermission Copy() { FileIOPermission copy = new FileIOPermission(PermissionState.None); @@ -740,97 +775,97 @@ public override IPermission Copy() copy.m_changeAcl = this.m_changeAcl.Copy(); } } - return copy; + return copy; } - + #if FEATURE_CAS_POLICY public override SecurityElement ToXml() { - SecurityElement esd = CodeAccessPermission.CreatePermissionElement( this, "System.Security.Permissions.FileIOPermission" ); + SecurityElement esd = CodeAccessPermission.CreatePermissionElement(this, "System.Security.Permissions.FileIOPermission"); if (!IsUnrestricted()) { if (this.m_read != null && !this.m_read.IsEmpty()) { - esd.AddAttribute( "Read", SecurityElement.Escape( m_read.ToString() ) ); + esd.AddAttribute("Read", SecurityElement.Escape(m_read.ToString())); } if (this.m_write != null && !this.m_write.IsEmpty()) { - esd.AddAttribute( "Write", SecurityElement.Escape( m_write.ToString() ) ); + esd.AddAttribute("Write", SecurityElement.Escape(m_write.ToString())); } if (this.m_append != null && !this.m_append.IsEmpty()) { - esd.AddAttribute( "Append", SecurityElement.Escape( m_append.ToString() ) ); + esd.AddAttribute("Append", SecurityElement.Escape(m_append.ToString())); } if (this.m_pathDiscovery != null && !this.m_pathDiscovery.IsEmpty()) { - esd.AddAttribute( "PathDiscovery", SecurityElement.Escape( m_pathDiscovery.ToString() ) ); + esd.AddAttribute("PathDiscovery", SecurityElement.Escape(m_pathDiscovery.ToString())); } if (this.m_viewAcl != null && !this.m_viewAcl.IsEmpty()) { - esd.AddAttribute( "ViewAcl", SecurityElement.Escape( m_viewAcl.ToString() ) ); + esd.AddAttribute("ViewAcl", SecurityElement.Escape(m_viewAcl.ToString())); } if (this.m_changeAcl != null && !this.m_changeAcl.IsEmpty()) { - esd.AddAttribute( "ChangeAcl", SecurityElement.Escape( m_changeAcl.ToString() ) ); + esd.AddAttribute("ChangeAcl", SecurityElement.Escape(m_changeAcl.ToString())); } } else { - esd.AddAttribute( "Unrestricted", "true" ); + esd.AddAttribute("Unrestricted", "true"); } return esd; } - + [System.Security.SecuritySafeCritical] // auto-generated public override void FromXml(SecurityElement esd) { - CodeAccessPermission.ValidateElement( esd, this ); + CodeAccessPermission.ValidateElement(esd, this); String et; - + if (XMLUtil.IsUnrestricted(esd)) { m_unrestricted = true; return; } - - + + m_unrestricted = false; - - et = esd.Attribute( "Read" ); + + et = esd.Attribute("Read"); if (et != null) { - m_read = new FileIOAccess( et ); + m_read = new FileIOAccess(et); } else { m_read = null; } - - et = esd.Attribute( "Write" ); + + et = esd.Attribute("Write"); if (et != null) { - m_write = new FileIOAccess( et ); + m_write = new FileIOAccess(et); } else { m_write = null; } - - et = esd.Attribute( "Append" ); + + et = esd.Attribute("Append"); if (et != null) { - m_append = new FileIOAccess( et ); + m_append = new FileIOAccess(et); } else { m_append = null; } - et = esd.Attribute( "PathDiscovery" ); + et = esd.Attribute("PathDiscovery"); if (et != null) { - m_pathDiscovery = new FileIOAccess( et ); + m_pathDiscovery = new FileIOAccess(et); m_pathDiscovery.PathDiscovery = true; } else @@ -838,20 +873,20 @@ public override void FromXml(SecurityElement esd) m_pathDiscovery = null; } - et = esd.Attribute( "ViewAcl" ); + et = esd.Attribute("ViewAcl"); if (et != null) { - m_viewAcl = new FileIOAccess( et ); + m_viewAcl = new FileIOAccess(et); } else { m_viewAcl = null; } - et = esd.Attribute( "ChangeAcl" ); + et = esd.Attribute("ChangeAcl"); if (et != null) { - m_changeAcl = new FileIOAccess( et ); + m_changeAcl = new FileIOAccess(et); } else { @@ -875,60 +910,60 @@ internal static int GetTokenIndex() public override bool Equals(Object obj) { FileIOPermission perm = obj as FileIOPermission; - if(perm == null) + if (perm == null) return false; - if(m_unrestricted && perm.m_unrestricted) + if (m_unrestricted && perm.m_unrestricted) return true; - if(m_unrestricted != perm.m_unrestricted) + if (m_unrestricted != perm.m_unrestricted) return false; - if(m_read == null) + if (m_read == null) { - if(perm.m_read != null && !perm.m_read.IsEmpty()) + if (perm.m_read != null && !perm.m_read.IsEmpty()) return false; } - else if(!m_read.Equals(perm.m_read)) + else if (!m_read.Equals(perm.m_read)) return false; - if(m_write == null) + if (m_write == null) { - if(perm.m_write != null && !perm.m_write.IsEmpty()) - return false; + if (perm.m_write != null && !perm.m_write.IsEmpty()) + return false; } - else if(!m_write.Equals(perm.m_write)) + else if (!m_write.Equals(perm.m_write)) return false; - if(m_append == null) + if (m_append == null) { - if(perm.m_append != null && !perm.m_append.IsEmpty()) - return false; + if (perm.m_append != null && !perm.m_append.IsEmpty()) + return false; } - else if(!m_append.Equals(perm.m_append)) + else if (!m_append.Equals(perm.m_append)) return false; - if(m_pathDiscovery == null) + if (m_pathDiscovery == null) { - if(perm.m_pathDiscovery != null && !perm.m_pathDiscovery.IsEmpty()) - return false; + if (perm.m_pathDiscovery != null && !perm.m_pathDiscovery.IsEmpty()) + return false; } - else if(!m_pathDiscovery.Equals(perm.m_pathDiscovery)) + else if (!m_pathDiscovery.Equals(perm.m_pathDiscovery)) return false; - if(m_viewAcl == null) + if (m_viewAcl == null) { - if(perm.m_viewAcl != null && !perm.m_viewAcl.IsEmpty()) - return false; + if (perm.m_viewAcl != null && !perm.m_viewAcl.IsEmpty()) + return false; } - else if(!m_viewAcl.Equals(perm.m_viewAcl)) + else if (!m_viewAcl.Equals(perm.m_viewAcl)) return false; - if(m_changeAcl == null) + if (m_changeAcl == null) { - if(perm.m_changeAcl != null && !perm.m_changeAcl.IsEmpty()) - return false; + if (perm.m_changeAcl != null && !perm.m_changeAcl.IsEmpty()) + return false; } - else if(!m_changeAcl.Equals(perm.m_changeAcl)) + else if (!m_changeAcl.Equals(perm.m_changeAcl)) return false; return true; @@ -951,12 +986,8 @@ public override int GetHashCode() /// IMPORTANT: This method should only be used after calling GetFullPath on the path to verify /// /// - /// - /// - /// - /// [System.Security.SecuritySafeCritical] - internal static void QuickDemand(FileIOPermissionAccess access, string fullPath, bool checkForDuplicates, bool needFullPath) + internal static void QuickDemand(FileIOPermissionAccess access, string fullPath, bool checkForDuplicates = false, bool needFullPath = true) { if (!CodeAccessSecurityEngine.QuickCheckForAllDemands()) { @@ -964,18 +995,109 @@ internal static void QuickDemand(FileIOPermissionAccess access, string fullPath, } else { - //Emulate FileIOPermission checks - Path.CheckInvalidPathChars(fullPath, true); + EmulateFileIOPermissionChecks(fullPath); + } + } + + /// + /// Call this method if you don't need a the FileIOPermission for anything other than calling Demand() once. + /// + /// This method tries to verify full access before allocating a FileIOPermission object. + /// If full access is there, then we still have to emulate the checks that creating the + /// FileIOPermission object would have performed. + /// + /// IMPORTANT: This method should only be used after calling GetFullPath on the path to verify + /// + /// + [System.Security.SecuritySafeCritical] + internal static void QuickDemand(FileIOPermissionAccess access, string[] fullPathList, bool checkForDuplicates = false, bool needFullPath = true) + { + if (!CodeAccessSecurityEngine.QuickCheckForAllDemands()) + { + new FileIOPermission(access, fullPathList, checkForDuplicates, needFullPath).Demand(); + } + else + { + foreach (string fullPath in fullPathList) + { + EmulateFileIOPermissionChecks(fullPath); + } + } + } - if (fullPath.Length > 2 && fullPath.IndexOf(':', 2) != -1) + [System.Security.SecuritySafeCritical] + internal static void QuickDemand(PermissionState state) + { + if (!CodeAccessSecurityEngine.QuickCheckForAllDemands()) + { + new FileIOPermission(state).Demand(); + } + } + +#if FEATURE_MACL + [System.Security.SecuritySafeCritical] + internal static void QuickDemand(FileIOPermissionAccess access, AccessControlActions control, string fullPath, bool checkForDuplicates = false, bool needFullPath = true) + { + if (!CodeAccessSecurityEngine.QuickCheckForAllDemands()) + { + new FileIOPermission(access, control, new string[] { fullPath }, checkForDuplicates, needFullPath).Demand(); + } + else + { + EmulateFileIOPermissionChecks(fullPath); + } + } + + [System.Security.SecuritySafeCritical] + internal static void QuickDemand(FileIOPermissionAccess access, AccessControlActions control, string[] fullPathList, bool checkForDuplicates = true, bool needFullPath = true) + { + if (!CodeAccessSecurityEngine.QuickCheckForAllDemands()) + { + new FileIOPermission(access, control, fullPathList, checkForDuplicates, needFullPath).Demand(); + } + else + { + foreach (string fullPath in fullPathList) { - throw new NotSupportedException(Environment.GetResourceString("Argument_PathFormatNotSupported")); + EmulateFileIOPermissionChecks(fullPath); } } } +#endif + + /// + /// Perform the additional path checks that would normally happen when creating a FileIOPermission object. + /// + /// A path that has already gone through GetFullPath or Normalize + internal static void EmulateFileIOPermissionChecks(string fullPath) + { + // Callers should have already made checks for invalid path format via normalization. This method will only make the + // additional checks needed to throw the same exceptions that would normally throw when using FileIOPermission. + // These checks are done via CheckIllegalCharacters() and StringExpressionSet in AddPathList() above. + // + // We have to check the beginning as some paths may be passed in as path + @"\.", which will be normalized away. + BCLDebug.Assert( + fullPath.StartsWith(Path.NormalizePath(fullPath, fullCheck: false), StringComparison.OrdinalIgnoreCase), + string.Format("path isn't normalized: {0}", fullPath)); + + // Checking for colon / invalid characters on device paths blocks legitimate access to objects such as named pipes. + if (AppContextSwitches.UseLegacyPathHandling || !PathInternal.IsDevice(fullPath)) + { + // GetFullPath already checks normal invalid path characters. We need to just check additional (wildcard) characters here. + // (By calling the standard helper we can allow extended paths \\?\ through when the support is enabled.) + if (PathInternal.HasWildCardCharacters(fullPath)) + { + throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars")); + } + if (PathInternal.HasInvalidVolumeSeparator(fullPath)) + { + throw new NotSupportedException(Environment.GetResourceString("Argument_PathFormatNotSupported")); + } + } + } } - + [Serializable] internal sealed class FileIOAccess { diff --git a/mscorlib/system/security/principal/windowsidentity.cs b/mscorlib/system/security/principal/windowsidentity.cs index 93cb25558..e045de8e2 100644 --- a/mscorlib/system/security/principal/windowsidentity.cs +++ b/mscorlib/system/security/principal/windowsidentity.cs @@ -609,6 +609,8 @@ public virtual IntPtr Token { // Public methods. // [SecuritySafeCritical] + [DynamicSecurityMethodAttribute()] + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable public static void RunImpersonated(SafeAccessTokenHandle safeAccessTokenHandle, Action action) { if (action == null) @@ -627,6 +629,8 @@ public static void RunImpersonated(SafeAccessTokenHandle safeAccessTokenHandle, } [SecuritySafeCritical] + [DynamicSecurityMethodAttribute()] + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable public static T RunImpersonated(SafeAccessTokenHandle safeAccessTokenHandle, Func func) { if (func == null) diff --git a/mscorlib/system/security/util/stringexpressionset.cs b/mscorlib/system/security/util/stringexpressionset.cs index 101d15ee5..698f76f02 100644 --- a/mscorlib/system/security/util/stringexpressionset.cs +++ b/mscorlib/system/security/util/stringexpressionset.cs @@ -219,33 +219,35 @@ internal static ArrayList CreateListFromExpressions(String[] str, bool needFullP throw new ArgumentNullException( "str" ); } Contract.EndContractBlock(); + ArrayList retArrayList = new ArrayList(); for (int index = 0; index < str.Length; ++index) { if (str[index] == null) throw new ArgumentNullException( "str" ); + // Replace alternate directory separators String oneString = StaticProcessWholeString( str[index] ); if (oneString != null && oneString.Length != 0) { - String temp = StaticProcessSingleString( oneString); + // Trim leading and trailing spaces + String temp = StaticProcessSingleString(oneString); - int indexOfNull = temp.IndexOf( '\0' ); + int indexOfNull = temp.IndexOf('\0'); if (indexOfNull != -1) - temp = temp.Substring( 0, indexOfNull ); + temp = temp.Substring(0, indexOfNull); if (temp != null && temp.Length != 0) { - if (Path.IsRelative(temp)) + if (PathInternal.IsPartiallyQualified(temp)) { - throw new ArgumentException( Environment.GetResourceString( "Argument_AbsolutePathRequired" ) ); + throw new ArgumentException(Environment.GetResourceString("Argument_AbsolutePathRequired")); } temp = CanonicalizePath( temp, needFullPath ); - retArrayList.Add( temp ); } } @@ -746,24 +748,14 @@ internal static String CanonicalizePath( String path ) [System.Security.SecurityCritical] // auto-generated [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] - internal static String CanonicalizePath( String path, bool needFullPath ) + internal static string CanonicalizePath(string path, bool needFullPath) { - if (path.IndexOf( '~' ) != -1) - { - string longPath = null; - GetLongPathName(path, JitHelpers.GetStringHandleOnStack(ref longPath)); - path = (longPath != null) ? longPath : path; - } - - if (path.IndexOf( ':', 2 ) != -1) - throw new NotSupportedException( Environment.GetResourceString( "Argument_PathFormatNotSupported" ) ); - if (needFullPath) { - String newPath = System.IO.Path.GetFullPathInternal( path ); - if (path.EndsWith( m_directorySeparator + ".", StringComparison.Ordinal )) + string newPath = Path.GetFullPathInternal(path); + if (path.EndsWith(m_directorySeparator + ".", StringComparison.Ordinal)) { - if (newPath.EndsWith( m_directorySeparator )) + if (newPath.EndsWith(m_directorySeparator)) { newPath += "."; } @@ -771,11 +763,23 @@ internal static String CanonicalizePath( String path, bool needFullPath ) { newPath += m_directorySeparator + "."; } - } - return newPath; + } + path = newPath; } - else - return path; + else if (path.IndexOf('~') != -1) + { + // GetFullPathInternal() will expand 8.3 file names + string longPath = null; + GetLongPathName(path, JitHelpers.GetStringHandleOnStack(ref longPath)); + path = (longPath != null) ? longPath : path; + } + + // This blocks usage of alternate data streams and some extended syntax paths (\\?\C:\). Checking after + // normalization allows valid paths such as " C:\" to be considered ok (as it will become "C:\"). + if (path.IndexOf(':', 2) != -1) + throw new NotSupportedException(Environment.GetResourceString("Argument_PathFormatNotSupported")); + + return path; } } } diff --git a/mscorlib/system/security/util/urlstring.cs b/mscorlib/system/security/util/urlstring.cs index b009ff0a8..c629fb4f6 100644 --- a/mscorlib/system/security/util/urlstring.cs +++ b/mscorlib/system/security/util/urlstring.cs @@ -354,13 +354,24 @@ private String ParsePort(String url) // 3. Throws a PathTooLongException if the length of the resulting URL is >= MAX_PATH. // This is done to prevent security issues due to canonicalization truncations. // Remove this method when the Path class supports "\\?\" - internal static String PreProcessForExtendedPathRemoval(String url, bool isFileUrl) + internal static string PreProcessForExtendedPathRemoval(string url, bool isFileUrl) { - bool uncShare = false; - return PreProcessForExtendedPathRemoval(url, isFileUrl, ref uncShare); + return PreProcessForExtendedPathRemoval(checkPathLength: true, url: url, isFileUrl: isFileUrl); } - private static String PreProcessForExtendedPathRemoval(String url, bool isFileUrl, ref bool isUncShare) + internal static string PreProcessForExtendedPathRemoval(bool checkPathLength, string url, bool isFileUrl) + { + bool isUncShare = false; + return PreProcessForExtendedPathRemoval(checkPathLength: checkPathLength, url: url, isFileUrl: isFileUrl, isUncShare: ref isUncShare); + } + + // Keeping this signature to avoid reflection breaks + private static string PreProcessForExtendedPathRemoval(string url, bool isFileUrl, ref bool isUncShare) + { + return PreProcessForExtendedPathRemoval(checkPathLength: true, url: url, isFileUrl: isFileUrl, isUncShare: ref isUncShare); + } + + private static string PreProcessForExtendedPathRemoval(bool checkPathLength, string url, bool isFileUrl, ref bool isUncShare) { // This is the modified URL that we will return StringBuilder modifiedUrl = new StringBuilder(url); @@ -434,15 +445,24 @@ private static String PreProcessForExtendedPathRemoval(String url, bool isFileUr } // ITEM 3 - If the path is greater than or equal (due to terminating NULL in windows) MAX_PATH, we throw. - if (modifiedUrl.Length >= Path.MAX_PATH) + if (checkPathLength) { - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); + // This needs to be a separate method to avoid hitting the static constructor on AppContextSwitches + CheckPathTooLong(modifiedUrl); } // Create the result string from the StringBuilder return modifiedUrl.ToString(); } + [MethodImpl(MethodImplOptions.NoInlining)] + private static void CheckPathTooLong(StringBuilder path) + { + if (path.Length >= (AppContextSwitches.BlockLongPaths ? PathInternal.MaxShortPath : PathInternal.MaxLongPath)) + { + throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); + } + } // Do any misc massaging of data in the URL private String PreProcessURL(String url, bool isFileURL) diff --git a/mscorlib/system/text/stringbuilder.cs b/mscorlib/system/text/stringbuilder.cs index 9a437c9c6..04fe4d23f 100644 --- a/mscorlib/system/text/stringbuilder.cs +++ b/mscorlib/system/text/stringbuilder.cs @@ -1966,7 +1966,7 @@ private void ExpandByABlock(int minBlockCharCount) VerifyClassInvariant(); - if ((minBlockCharCount + Length) > m_MaxCapacity) + if (minBlockCharCount + Length < minBlockCharCount || (minBlockCharCount + Length) > m_MaxCapacity) throw new ArgumentOutOfRangeException("requiredLength", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity")); // Compute the length of the new block we need @@ -2026,7 +2026,8 @@ private void MakeRoom(int index, int count, out StringBuilder chunk, out int ind VerifyClassInvariant(); Contract.Assert(count > 0, "Count must be strictly positive"); Contract.Assert(index >= 0, "Index can't be negative"); - if (count + Length > m_MaxCapacity) + + if (count + Length < count || count + Length > m_MaxCapacity) throw new ArgumentOutOfRangeException("requiredLength", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity")); chunk = this; diff --git a/mscorlib/system/threading/Tasks/Task.cs b/mscorlib/system/threading/Tasks/Task.cs index 1dec12dee..948fa8af9 100644 --- a/mscorlib/system/threading/Tasks/Task.cs +++ b/mscorlib/system/threading/Tasks/Task.cs @@ -3622,7 +3622,14 @@ internal void FinishContinuations() ITaskCompletionAction singleTaskCompletionAction = continuationObject as ITaskCompletionAction; if (singleTaskCompletionAction != null) { - singleTaskCompletionAction.Invoke(this); + if (bCanInlineContinuations) + { + singleTaskCompletionAction.Invoke(this); + } + else + { + ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(singleTaskCompletionAction, this), forceGlobal: false); + } LogFinishCompletionNotification(); return; } @@ -3699,7 +3706,15 @@ internal void FinishContinuations() { Contract.Assert(currentContinuation is ITaskCompletionAction, "Expected continuation element to be Action, TaskContinuation, or ITaskContinuationAction"); var action = (ITaskCompletionAction)currentContinuation; - action.Invoke(this); + + if (bCanInlineContinuations) + { + action.Invoke(this); + } + else + { + ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(action, this), forceGlobal: false); + } } } } @@ -6662,6 +6677,30 @@ private static Task[] GetActiveTasks() } + internal sealed class CompletionActionInvoker : IThreadPoolWorkItem + { + private readonly ITaskCompletionAction m_action; + private readonly Task m_completingTask; + + internal CompletionActionInvoker(ITaskCompletionAction action, Task completingTask) + { + m_action = action; + m_completingTask = completingTask; + } + + [SecurityCritical] + public void ExecuteWorkItem() + { + m_action.Invoke(m_completingTask); + } + + [SecurityCritical] + public void MarkAborted(ThreadAbortException tae) + { + /* NOP */ + } + } + // Proxy class for better debugging experience internal class SystemThreadingTasks_TaskDebugView {