diff options
Diffstat (limited to 'src/mscorlib/shared/System/Runtime/Serialization')
14 files changed, 587 insertions, 0 deletions
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/IDeserializationCallback.cs b/src/mscorlib/shared/System/Runtime/Serialization/IDeserializationCallback.cs new file mode 100644 index 0000000000..a1c1671a8b --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/IDeserializationCallback.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.Serialization +{ + public interface IDeserializationCallback + { + void OnDeserialization(object sender); + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/IFormatterConverter.cs b/src/mscorlib/shared/System/Runtime/Serialization/IFormatterConverter.cs new file mode 100644 index 0000000000..c173144854 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/IFormatterConverter.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.Serialization +{ + [CLSCompliant(false)] + public interface IFormatterConverter + { + object Convert(object value, Type type); + object Convert(object value, TypeCode typeCode); + bool ToBoolean(object value); + char ToChar(object value); + sbyte ToSByte(object value); + byte ToByte(object value); + short ToInt16(object value); + ushort ToUInt16(object value); + int ToInt32(object value); + uint ToUInt32(object value); + long ToInt64(object value); + ulong ToUInt64(object value); + float ToSingle(object value); + double ToDouble(object value); + decimal ToDecimal(object value); + DateTime ToDateTime(object value); + string ToString(object value); + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/IObjectReference.cs b/src/mscorlib/shared/System/Runtime/Serialization/IObjectReference.cs new file mode 100644 index 0000000000..d41bc50dde --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/IObjectReference.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.Serialization +{ + public interface IObjectReference + { + object GetRealObject(StreamingContext context); + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/ISafeSerializationData.cs b/src/mscorlib/shared/System/Runtime/Serialization/ISafeSerializationData.cs new file mode 100644 index 0000000000..5089d134c3 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/ISafeSerializationData.cs @@ -0,0 +1,207 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.Serialization +{ + // + // #SafeSerialization + // + // Types which are serializable via the ISerializable interface have a problem when it comes to allowing + // transparent subtypes which can allow themselves to serialize since the GetObjectData method is + // SecurityCritical. + // + // For instance, System.Exception implements ISerializable, however it is also desirable to have + // transparent exceptions with their own fields that need to be serialized. (For instance, in transparent + // assemblies such as the DLR and F#, or even in partial trust application code). Since overriding + // GetObjectData requires that the overriding method be security critical, this won't work directly. + // + // SafeSerializationManager solves this problem by allowing any partial trust code to contribute + // individual chunks of serializable data to be included in the serialized version of the derived class. + // These chunks are then deserialized back out of the serialized type and notified that they should + // populate the fields of the deserialized object when serialization is complete. This allows partial + // trust or transparent code to participate in serialization of an ISerializable type without having to + // override GetObjectData or implement the ISerializable constructor. + // + // On the serialization side, SafeSerializationManager has an event SerializeObjectState which it will + // fire in response to serialization in order to gather the units of serializable data that should be + // stored with the rest of the object during serialization. Methods which respond to these events + // create serializable objects which implement the ISafeSerializationData interface and add them to the + // collection of other serialized data by calling AddSerializedState on the SafeSerializationEventArgs + // passed into the event. + // + // By using an event rather than a virtual method on the base ISerializable object, we allow multiple + // potentially untrusted subclasses to participate in serialization, without each one having to ensure + // that it calls up to the base type in order for the whole system to work. (For instance Exception : + // TrustedException : UntrustedException, in this scenario UntrustedException would be able to override + // the virtual method an prevent TrustedException from ever seeing the method call, either accidentally + // or maliciously). + // + // Further, by only allowing additions of new chunks of serialization state rather than exposing the + // whole underlying list, we avoid exposing potentially sensitive serialized state to any of the + // potentially untrusted subclasses. + // + // At deserialization time, SafeSerializationManager performs the reverse operation. It deserializes the + // chunks of serialized state, and then notifies them that the object they belong to is deserialized by + // calling their CompleteSerialization method. In repsonse to this call, the state objects populate the + // fields of the object being deserialized with the state that they held. + // + // From a security perspective, the chunks of serialized state can only contain data that the specific + // subclass itself had access to read (otherwise it wouldn't be able to populate the type with that + // data), as opposed to having access to far more data in the SerializationInfo that GetObjectData uses. + // Similarly, at deserialization time, the serialized state can only modify fields that the type itself + // has access to (again, as opposed to the full SerializationInfo which could be modified). + // + // Individual types which wish to participate in safe serialization do so by containing an instance of a + // SafeSerializationManager and exposing its serialization event. During GetObjectData, the + // SafeSerializationManager is serialized just like any other field of the containing type. However, at + // the end of serialization it is called back one last time to CompleteSerialization. + // + // In CompleteSerialization, if the SafeSerializationManager detects that it has extra chunks of + // data to handle, it substitutes the root type being serialized (formerly the real type hosting the + // SafeSerializationManager) with itself. This allows it to gain more control over the deserialization + // process. It also saves away an extra bit of state in the serialization info indicating the real type + // of object that should be recreated during deserialization. + // + // At this point the serialized state looks like this: + // Data: + // realSerializedData1 + // ... + // realSerializedDataN + // safeSerializationData -> this is the serialization data member of the parent type + // _serializedState -> list of saved serialized states from subclasses responding to the safe + // serialization event + // RealTypeSerializationName -> type which is using safe serialization + // Type: + // SafeSerializationManager + // + // That is, the serialized data claims to be of type SafeSerializationManager, however contains only the + // data from the real object being serialized along with one bit of safe serialization metadata. + // + // At deserialization time, since the serialized data claims to be of type SafeSerializationManager, the + // root object being created is an instance of the SafeSerializationManager class. However, it detects + // that this isn't a real SafeSerializationManager (by looking for the real type field in the metadata), + // and simply saves away the SerializationInfo and the real type being deserialized. + // + // Since SafeSerializationManager implements IObjectReference, the next step of deserialization is the + // GetRealObject callback. This callback is the one responsible for getting the + // SafeSerializationManager out of the way and instead creating an instance of the actual type which was + // serialized. + // + // It does this by first creating an instance of the real type being deserialzed (saved away in the + // deserialzation constructor), but not running any of its constructors. Instead, it walks the + // inheritance hierarchy (moving toward the most derived type) looking for the last full trust type to + // implement the standard ISerializable constructor before any type does not implement the constructor. + // It is this last type's deserialization constructor which is then invoked, passing in the saved + // SerializationInfo. Once the constructors are run, we return this object as the real deserialized + // object. + // + // The reason that we do this walk is so that ISerializable types can protect themselves from malicious + // input during deserialization by making their deserialization constructors unavailable to partial + // trust code. By not requiring every type have a copy of this constructor, partial trust code can + // participate in safe serialization and not be required to have access to the parent's constructor. + // + // It should be noted however, that this heuristic means that if a full trust type does derive from + // a transparent or partial trust type using this safe serialization mechanism, that full trust type + // will not have its constructor called. Further, the protection of not invoking partial trust + // deserialization constructors only comes into play if SafeSerializationManager is in control of + // deserialization, which means there must be at least one (even empty) safe serialization event + // handler registered. + // + // Another interesting note is that at this point there are now two SafeSerializationManagers alive for + // this deserialization. The first object is the one which is controlling the deserialization and was + // created as the root object of the deserialization. The second one is the object which contains the + // serialized data chunks and is a data member of the real object being deserialized. For this reason, + // the data objects cannot be notified that the deserialization is complete during GetRealObject since + // the ISafeSerializationData objects are not members of the active SafeSerializationManager instance. + // + // The next step is the OnDeserialized callback, which comes to SafeSerializableObject since it was + // pretending to be the root object of the deserialization. It responds to this callback by calling + // any existing OnDeserialized callback on the real type that was deserialized. + // + // The real type needs to call its data member SafeSerializationData object's CompleteDeserialization + // method in response to the OnDeserialized call. This CompleteDeserialization call will then iterate + // through the ISafeSerializationData objects calling each of their CompleteDeserialization methods so + // that they can plug the nearly-complete object with their saved data. + // + // The reason for having a new ISafeSerializationData interface which is basically identical to + // IDeserializationCallback is that IDeserializationCallback will be called on the stored data chunks + // by the serialization code when they are deserialized, and that's not a desirable behavior. + // Essentially, we need to change the meaning of the object parameter to mean "parent object which + // participated in safe serialization", rather than "this object". + // + // Implementing safe serialization on an ISerialiable type is relatively straight forward. (For an + // example, see System.Exception): + // + // 1. Include a data member of type SafeSerializationManager: + // + // private SafeSerializationManager _safeSerializationManager; + // + // 2. Add a protected SerializeObjectState event, which passes through to the SafeSerializationManager: + // + // protected event EventHandler<SafeSerializationEventArgs> SerializeObjectState + // { + // add { _safeSerializationManager.SerializeObjectState += value; } + // remove { _safeSerializationManager.SerializeObjectState -= value; } + // } + // + // 3. Serialize the safe serialization object in GetObjectData, and call its CompleteSerialization method: + // + // { + // info.AddValue("_safeSerializationManager", _safeSerializationManager, typeof(SafeSerializationManager)); + // _safeSerializationManager.CompleteSerialization(this, info, context); + // } + // + // 4. Add an OnDeserialized handler if one doesn't already exist, and call CompleteDeserialization in it: + // + // [OnDeserialized] + // private void OnDeserialized(StreamingContext context) + // { + // _safeSerializationManager.CompleteDeserialization(this); + // } + // + // On the client side, using safe serialization is also pretty easy. For example: + // + // [Serializable] + // public class TransparentException : Exception + // { + // [Serializable] + // private struct TransparentExceptionState : ISafeSerializationData + // { + // public string _extraData; + // + // void ISafeSerializationData.CompleteDeserialization(object obj) + // { + // TransparentException exception = obj as TransparentException; + // exception._state = this; + // } + // } + // + // [NonSerialized] + // private TransparentExceptionState _state = new TransparentExceptionState(); + // + // public TransparentException() + // { + // SerializeObjectState += delegate(object exception, SafeSerializationEventArgs eventArgs) + // { + // eventArgs.AddSerializedState(_state); + // }; + // } + // + // public string ExtraData + // { + // get { return _state._extraData; } + // set { _state._extraData = value; } + // } + // } + // + + // Interface to be supported by objects which are stored in safe serialization stores + public interface ISafeSerializationData + { + // CompleteDeserialization is called when the object to which the extra serialized data was attached + // has completed its deserialization, and now needs to be populated with the extra data stored in + // this object. + void CompleteDeserialization(object deserialized); + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/ISerializable.cs b/src/mscorlib/shared/System/Runtime/Serialization/ISerializable.cs new file mode 100644 index 0000000000..383b3f07af --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/ISerializable.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.Serialization +{ + public interface ISerializable + { + void GetObjectData(SerializationInfo info, StreamingContext context); + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializedAttribute.cs b/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializedAttribute.cs new file mode 100644 index 0000000000..408a55ccf9 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializedAttribute.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.Serialization +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class OnDeserializedAttribute : Attribute + { + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializingAttribute.cs b/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializingAttribute.cs new file mode 100644 index 0000000000..162857e8d3 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializingAttribute.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.Serialization +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class OnDeserializingAttribute : Attribute + { + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/OnSerializedAttribute.cs b/src/mscorlib/shared/System/Runtime/Serialization/OnSerializedAttribute.cs new file mode 100644 index 0000000000..020dd0257c --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/OnSerializedAttribute.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.Serialization +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class OnSerializedAttribute : Attribute + { + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/OnSerializingAttribute.cs b/src/mscorlib/shared/System/Runtime/Serialization/OnSerializingAttribute.cs new file mode 100644 index 0000000000..8dc8af3f23 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/OnSerializingAttribute.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.Serialization +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class OnSerializingAttribute : Attribute + { + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/OptionalFieldAttribute.cs b/src/mscorlib/shared/System/Runtime/Serialization/OptionalFieldAttribute.cs new file mode 100644 index 0000000000..84daa539be --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/OptionalFieldAttribute.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.Serialization +{ + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + public sealed class OptionalFieldAttribute : Attribute + { + private int _versionAdded = 1; + + public int VersionAdded + { + get { return _versionAdded; } + set + { + if (value < 1) + { + throw new ArgumentException(SR.Serialization_OptionalFieldVersionValue); + } + _versionAdded = value; + } + } + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/SafeSerializationEventArgs.cs b/src/mscorlib/shared/System/Runtime/Serialization/SafeSerializationEventArgs.cs new file mode 100644 index 0000000000..896b91fca0 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/SafeSerializationEventArgs.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace System.Runtime.Serialization +{ + // SafeSerializationEventArgs are provided to the delegates which do safe serialization. Each delegate + // serializes its own state into an IDeserializationCallback instance which must, itself, be serializable. + // These indivdiual states are then added to the SafeSerializationEventArgs in order to be saved away when + // the original ISerializable type is serialized. + public sealed class SafeSerializationEventArgs : EventArgs + { + private readonly List<object> _serializedStates = new List<object>(); + + internal SafeSerializationEventArgs() { } + + public void AddSerializedState(ISafeSerializationData serializedState) + { + if (serializedState == null) + throw new ArgumentNullException(nameof(serializedState)); + if (!serializedState.GetType().IsSerializable) + throw new ArgumentException(SR.Format(SR.Serialization_NonSerType, serializedState.GetType(), serializedState.GetType().Assembly.FullName)); + + _serializedStates.Add(serializedState); + } + + public StreamingContext StreamingContext { get; } + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs b/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs new file mode 100644 index 0000000000..a359daf4f9 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Serialization; + +namespace System.Runtime.Serialization +{ + [Serializable] + public class SerializationException : SystemException + { + private static String s_nullMessage = SR.SerializationException; + + // Creates a new SerializationException with its message + // string set to a default message. + public SerializationException() + : base(s_nullMessage) + { + HResult = __HResults.COR_E_SERIALIZATION; + } + + public SerializationException(String message) + : base(message) + { + HResult = __HResults.COR_E_SERIALIZATION; + } + + public SerializationException(String message, Exception innerException) + : base(message, innerException) + { + HResult = __HResults.COR_E_SERIALIZATION; + } + + protected SerializationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/SerializationInfoEnumerator.cs b/src/mscorlib/shared/System/Runtime/Serialization/SerializationInfoEnumerator.cs new file mode 100644 index 0000000000..6399510736 --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/SerializationInfoEnumerator.cs @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Diagnostics; + +namespace System.Runtime.Serialization +{ + public struct SerializationEntry + { + private string _name; + private object _value; + private Type _type; + + internal SerializationEntry(string entryName, object entryValue, Type entryType) + { + _name = entryName; + _value = entryValue; + _type = entryType; + } + + public object Value => _value; + public string Name => _name; + public Type ObjectType => _type; + } + + public sealed class SerializationInfoEnumerator : IEnumerator + { + private readonly string[] _members; + private readonly object[] _data; + private readonly Type[] _types; + private readonly int _numItems; + private int _currItem; + private bool _current; + + internal SerializationInfoEnumerator(string[] members, object[] info, Type[] types, int numItems) + { + Debug.Assert(members != null, "[SerializationInfoEnumerator.ctor]members!=null"); + Debug.Assert(info != null, "[SerializationInfoEnumerator.ctor]info!=null"); + Debug.Assert(types != null, "[SerializationInfoEnumerator.ctor]types!=null"); + Debug.Assert(numItems >= 0, "[SerializationInfoEnumerator.ctor]numItems>=0"); + Debug.Assert(members.Length >= numItems, "[SerializationInfoEnumerator.ctor]members.Length>=numItems"); + Debug.Assert(info.Length >= numItems, "[SerializationInfoEnumerator.ctor]info.Length>=numItems"); + Debug.Assert(types.Length >= numItems, "[SerializationInfoEnumerator.ctor]types.Length>=numItems"); + + _members = members; + _data = info; + _types = types; + + //The MoveNext semantic is much easier if we enforce that [0..m_numItems] are valid entries + //in the enumerator, hence we subtract 1. + _numItems = numItems - 1; + _currItem = -1; + _current = false; + } + + public bool MoveNext() + { + if (_currItem < _numItems) + { + _currItem++; + _current = true; + } + else + { + _current = false; + } + + return _current; + } + + object IEnumerator.Current => Current; + + public SerializationEntry Current + { + get + { + if (_current == false) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return new SerializationEntry(_members[_currItem], _data[_currItem], _types[_currItem]); + } + } + + public void Reset() + { + _currItem = -1; + _current = false; + } + + public string Name + { + get + { + if (_current == false) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return _members[_currItem]; + } + } + public object Value + { + get + { + if (_current == false) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return _data[_currItem]; + } + } + public Type ObjectType + { + get + { + if (_current == false) + { + throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen); + } + return _types[_currItem]; + } + } + } +} diff --git a/src/mscorlib/shared/System/Runtime/Serialization/StreamingContext.cs b/src/mscorlib/shared/System/Runtime/Serialization/StreamingContext.cs new file mode 100644 index 0000000000..1026a87d1e --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/Serialization/StreamingContext.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.Serialization +{ + [Serializable] + public struct StreamingContext + { + private readonly object _additionalContext; + private readonly StreamingContextStates _state; + + public StreamingContext(StreamingContextStates state) : this(state, null) + { + } + + public StreamingContext(StreamingContextStates state, object additional) + { + _state = state; + _additionalContext = additional; + } + + public override bool Equals(object obj) + { + if (!(obj is StreamingContext)) + { + return false; + } + StreamingContext ctx = (StreamingContext)obj; + return ctx._additionalContext == _additionalContext && ctx._state == _state; + } + + public override int GetHashCode() => (int)_state; + + public StreamingContextStates State => _state; + + public object Context => _additionalContext; + } + + [Flags] + public enum StreamingContextStates + { + CrossProcess = 0x01, + CrossMachine = 0x02, + File = 0x04, + Persistence = 0x08, + Remoting = 0x10, + Other = 0x20, + Clone = 0x40, + CrossAppDomain = 0x80, + All = 0xFF, + } +} |