2

I have some Exception derived types, which add additional properties to the Exception. Searching the web for examples and guidelines on how to handle the serialization of such Exception-based types led to descriptions and code samples which were rather old. Trying these samples led always to security-errors. To make it work, I had to additionally decorate the GetObjectData-Method with the System.Security.SecurityCritical attribute.

Interestingly, the SecurityPermission-Attribute, which was contained in all of the examples, but in various ways, seemed not to be necessary. Some examples only added it to the GetObjectData, some added it also to the deserializing-constructor. Some examples used the strong SecurityAction.Demand-action, most of the examples used the SecurityAction.LinkDemand-action and others declared SecurityAction.RequestMinimum.

My question is, whether the below Exception-derived type (for the serialization part of it) is correct for being used with nowadays .net frameworks (4.7.[x]+, Core[x], Standard2.[x]+) or if there is something missing, or if I even can remove some parts? Please note that I don’t want to seal the type. Other Exception-types should be able to derive from it.

[Serializable]
public class FooException : Exception{

    readonly int m_fooValue;

    public FooException(string message,int fooValue,Exception innerException=null) : base(message,innerException){
        m_fooValue = fooValue;
    }
    [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
    protected FooException(SerializationInfo info, StreamingContext context) : base(info, context) {
        m_fooValue = info.GetInt32(nameof(FooValue));            
    }

    [SecurityCritical]
    [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context) {
        if (info == null) throw new ArgumentNullException(nameof(info));
        info.AddValue(nameof(FooValue), m_fooValue);            
        base.GetObjectData(info, context);
    }

    public int FooValue => m_fooValue;
}

The derived type (FooBarException) additionally must then also set the SecurityCritical-attribute on the deserializung-constructor for satisfying the LinkDemand:

[Serializable] 
public class FooBarException : FooException{

    readonly int m_barValue;

    public FooBarException(string message,int fooValue, int barValue, Exception innerException = null) : base(message, fooValue, innerException) {
        m_barValue = barValue;
    }
    [SecurityCritical] // Additional for satisfying the LinkDemand on the base constructor
    [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
    protected FooBarException(SerializationInfo info, StreamingContext context) : base(info, context) {
        m_barValue = info.GetInt32(nameof(BarValue));
    }

    [SecurityCritical]
    [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context) {
        if (info == null) throw new ArgumentNullException(nameof(info));
        info.AddValue(nameof(BarValue), m_barValue);
        base.GetObjectData(info, context);
    }

    public int BarValue => m_barValue;


}

Sources
https://stackoverflow.com/a/100369/340628

https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.iserializable.getobjectdata?view=netframework-4.8

What is the correct way to make a custom .NET Exception serializable?

https://blog.gurock.com/articles/creating-custom-exceptions-in-dotnet/

4
  • 1
    If this needs to work in .NETCore then there is no point in making the exception [Serializable]. No support for remoting and appdomains. Commented Feb 20, 2020 at 12:54
  • What serializer are you using? BinaryFormatter? Or something else such as Json.NET? Commented Feb 21, 2020 at 0:54
  • Also, did you see How can I implement ISerializable in .NET 4+ without violating inheritance security rules?? Commented Feb 21, 2020 at 1:07
  • @dbc: No specific formatter. I'm finalizing some libraries and FX-cop nagged me about the GetObjectData-override. This drew my interest to the question on how to do this right. As far as I can see, my implementation (see my post) should be ok with actual frameworks. As MS does on the base class, I use the SecurityCritical on the GetObjectData - no need for partial trust or untrusted code to access this method. Its really only for serialization purposes. And if understand Hans Passant right, the importance of the serialization feature is fading away anyway, since .net Core is not supporting it. Commented Feb 21, 2020 at 5:45

1 Answer 1

2

After digging a little deeper, I came to the following pattern:

[Serializable]
public class FooException : Exception{

    readonly int m_fooValue;

    public FooException(string message, int fooValue, Exception innerException = null) : base(message, innerException) {
        m_fooValue = fooValue;
    }

    [SecuritySafeCritical]
    protected FooException(SerializationInfo info, StreamingContext context) : base(info, context) {
        m_fooValue = info.GetInt32(nameof(FooValue));
    }

    [SecurityCritical]      
    public override void GetObjectData(SerializationInfo info, StreamingContext context) {
        if (info == null) throw new ArgumentNullException(nameof(info));
        info.AddValue(nameof(FooValue), m_fooValue);
        base.GetObjectData(info, context);
    }

    public int FooValue => m_fooValue;
}

[Serializable]
public class FooBarException : FooException
{

    readonly int m_barValue;

    public FooBarException(string message, int fooValue, int barValue, Exception innerException = null) : base(message, fooValue, innerException) {
        m_barValue = barValue;
    }
    [SecuritySafeCritical]        
    protected FooBarException(SerializationInfo info, StreamingContext context) : base(info, context) {
        m_barValue = info.GetInt32(nameof(BarValue));
    }

    [SecurityCritical]        
    public override void GetObjectData(SerializationInfo info, StreamingContext context) {
        if (info == null) throw new ArgumentNullException(nameof(info));
        info.AddValue(nameof(BarValue), m_barValue);
        base.GetObjectData(info, context);
    }

    public int BarValue => m_barValue;


}

For the SecurityAction.LinkDemand-part of the Question, this value should not be used anymore (.net 4+). Either is SecurityAction.Demand to be used, or even the the whole SecurityPermission declaration may be replaced with SecurityCritical (depending on the case, such as for the above case).

Sign up to request clarification or add additional context in comments.

3 Comments

You could also use the ISafeSerializationData pattern as shown in the reference source, however that pattern is not supported by all serializers.
I think its ok like this. FX Cop is not complaining anymore and I have unit tests testing which work fine. No need to expand complexity. But thanks, maybe I will use ISafeSerializationData some day. For now, the libraries are finished and my boss is happy.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.