Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add BinaryFormatter auditing EventSource
  • Loading branch information
GrabYourPitchforks committed Jul 23, 2020
commit 1ffeffce412ea4939b7a8159b4825225e71d3853
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.Core.cs" Condition="'$(TargetsBrowser)' != 'true'" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatter.PlatformNotSupported.cs" Condition="'$(TargetsBrowser)' == 'true'" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatterEventSource.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryFormatterWriter.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectInfo.cs" />
<Compile Include="System\Runtime\Serialization\Formatters\Binary\BinaryObjectReader.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public object Deserialize(Stream serializationStream)
};
try
{
BinaryFormatterEventSource.Log.DeserializationStarted();
var parser = new BinaryParser(serializationStream, reader);
return reader.Deserialize(parser);
}
Expand All @@ -50,6 +51,10 @@ public object Deserialize(Stream serializationStream)
{
throw new SerializationException(SR.Serialization_CorruptedStream, e);
}
finally
{
BinaryFormatterEventSource.Log.DeserializationEnded();
}
}

[Obsolete(Obsoletions.BinaryFormatterMessage, DiagnosticId = Obsoletions.BinaryFormatterDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
Expand All @@ -73,10 +78,18 @@ public void Serialize(Stream serializationStream, object graph)
_assemblyFormat = _assemblyFormat,
};

var sow = new ObjectWriter(_surrogates, _context, formatterEnums, _binder);
BinaryFormatterWriter binaryWriter = new BinaryFormatterWriter(serializationStream, sow, _typeFormat);
sow.Serialize(graph, binaryWriter);
_crossAppDomainArray = sow._crossAppDomainArray;
try
{
BinaryFormatterEventSource.Log.SerializationStarted();
var sow = new ObjectWriter(_surrogates, _context, formatterEnums, _binder);
BinaryFormatterWriter binaryWriter = new BinaryFormatterWriter(serializationStream, sow, _typeFormat);
sow.Serialize(graph, binaryWriter);
_crossAppDomainArray = sow._crossAppDomainArray;
}
finally
{
BinaryFormatterEventSource.Log.SerializationEnded();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Threading;

namespace System.Runtime.Serialization.Formatters.Binary
{
[EventSource(
Name = "System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource",
LocalizationResources = "FxResources.System.Runtime.Serialization.Formatters.SR")]
internal sealed class BinaryFormatterEventSource : EventSource
{
private const int EventId_SerializationStarted = 10;
private const int EventId_SerializationEnded = 11;
private const int EventId_SerializingObject = 12;
private const int EventId_DeserializationStarted = 20;
private const int EventId_DeserializationEnded = 21;
private const int EventId_DeserializingObject = 22;

// Used to keep track of whether a write operation is in progress. It's
// possible the listener itself uses BinaryFormatter to write to a log,
// and if this is the case we suppress our own logging events so that we
// enter an infinite recursion scenario.
private static readonly AsyncLocal<bool> _writeInProgress = new AsyncLocal<bool>();

public static readonly BinaryFormatterEventSource Log = new BinaryFormatterEventSource();

private BinaryFormatterEventSource()
{
}

[Event(EventId_SerializationStarted, Opcode = EventOpcode.Start, Keywords = Keywords.Serialization, Level = EventLevel.Informational)]
public void SerializationStarted()
{
if (IsEnabled(EventLevel.Informational, Keywords.Serialization) && !_writeInProgress.Value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I typically suggest people not to worry about adding IsEnabled() checks unless you are on a very hot code-path trying to shave a few nanoseconds. The implementation within WriteEvent() will do an appropriate check.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followed up offline. Will leave them in for now since at least one of the operations (Type.get_AssemblyQualifiedName) guarded by the initial check might have side effects that we don't want to run if nobody is listening.

{
try
{
_writeInProgress.Value = true;
WriteEvent(EventId_SerializationStarted);
}
finally
{
_writeInProgress.Value = false;
}
}
}

[Event(EventId_SerializationEnded, Opcode = EventOpcode.Stop, Keywords = Keywords.Serialization, Level = EventLevel.Informational)]
public void SerializationEnded()
{
if (IsEnabled(EventLevel.Informational, Keywords.Serialization) && !_writeInProgress.Value)
{
try
{
_writeInProgress.Value = true;
WriteEvent(EventId_SerializationEnded);
}
finally
{
_writeInProgress.Value = false;
}
}
}

[NonEvent]
public void SerializingObject(Type type)
{
Debug.Assert(type != null);

if (IsEnabled(EventLevel.Informational, Keywords.Serialization) && !_writeInProgress.Value)
{
try
{
_writeInProgress.Value = true;
SerializingObject(type.AssemblyQualifiedName);
}
finally
{
_writeInProgress.Value = false;
}
}
}

[Event(EventId_SerializingObject, Keywords = Keywords.Serialization, Level = EventLevel.Informational)]
private void SerializingObject(string? typeName)
{
WriteEvent(EventId_SerializingObject, typeName);
}

[Event(EventId_DeserializationStarted, Opcode = EventOpcode.Start, Keywords = Keywords.Deserialization, Level = EventLevel.Informational)]
public void DeserializationStarted()
{
if (IsEnabled(EventLevel.Informational, Keywords.Deserialization) && !_writeInProgress.Value)
{
try
{
_writeInProgress.Value = true;
WriteEvent(EventId_DeserializationStarted);
}
finally
{
_writeInProgress.Value = false;
}
}
}

[Event(EventId_DeserializationEnded, Opcode = EventOpcode.Stop, Keywords = Keywords.Deserialization, Level = EventLevel.Informational)]
public void DeserializationEnded()
{
if (IsEnabled(EventLevel.Informational, Keywords.Deserialization) && !_writeInProgress.Value)
{
try
{
_writeInProgress.Value = true;
WriteEvent(EventId_DeserializationEnded);
}
finally
{
_writeInProgress.Value = false;
}
}
}

[NonEvent]
public void DeserializingObject(Type type)
{
Debug.Assert(type != null);

if (IsEnabled(EventLevel.Informational, Keywords.Deserialization) && !_writeInProgress.Value)
{
try
{
_writeInProgress.Value = true;
DeserializingObject(type.AssemblyQualifiedName);
}
finally
{
_writeInProgress.Value = false;
}
}
}

[Event(EventId_DeserializingObject, Keywords = Keywords.Deserialization, Level = EventLevel.Informational)]
private void DeserializingObject(string? typeName)
{
WriteEvent(EventId_DeserializingObject, typeName);
}

public class Keywords
{
public const EventKeywords Serialization = (EventKeywords)1;
public const EventKeywords Deserialization = (EventKeywords)2;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,11 @@ private void InitMemberInfo()

internal string GetAssemblyString() => _binderAssemblyString ?? _cache._assemblyString;

private void InvokeSerializationBinder(SerializationBinder? binder) =>
private void InvokeSerializationBinder(SerializationBinder? binder)
{
BinaryFormatterEventSource.Log.SerializingObject(_objectType!);
binder?.BindToName(_objectType!, out _binderAssemblyString, out _binderTypeName);
}

internal void GetMemberInfo(out string[]? outMemberNames, out Type[]? outMemberTypes, out object?[]? outMemberData)
{
Expand Down Expand Up @@ -392,6 +395,8 @@ internal void Init(Type? objectType, string[] memberNames, Type[]? memberTypes,

private void InitReadConstructor(Type objectType, ISurrogateSelector? surrogateSelector, StreamingContext context)
{
BinaryFormatterEventSource.Log.DeserializingObject(objectType);

if (objectType.IsArray)
{
InitNoMembers();
Expand Down
Loading