forked from dotnet/runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add BinaryFormatter auditing EventSource (dotnet#39874)
- Loading branch information
1 parent
f0ede2b
commit b165cbb
Showing
6 changed files
with
346 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
...rmatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatterEventSource.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// 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; | ||
|
||
namespace System.Runtime.Serialization.Formatters.Binary | ||
{ | ||
[EventSource( | ||
Name = "System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource")] | ||
internal sealed class BinaryFormatterEventSource : EventSource | ||
{ | ||
private const int EventId_SerializationStart = 10; | ||
private const int EventId_SerializationStop = 11; | ||
private const int EventId_SerializingObject = 12; | ||
private const int EventId_DeserializationStart = 20; | ||
private const int EventId_DeserializationStop = 21; | ||
private const int EventId_DeserializingObject = 22; | ||
|
||
public static readonly BinaryFormatterEventSource Log = new BinaryFormatterEventSource(); | ||
|
||
private BinaryFormatterEventSource() | ||
{ | ||
} | ||
|
||
[Event(EventId_SerializationStart, Opcode = EventOpcode.Start, Keywords = Keywords.Serialization, Level = EventLevel.Informational, ActivityOptions = EventActivityOptions.Recursive)] | ||
public void SerializationStart() | ||
{ | ||
if (IsEnabled(EventLevel.Informational, Keywords.Serialization)) | ||
{ | ||
WriteEvent(EventId_SerializationStart); | ||
} | ||
} | ||
|
||
[Event(EventId_SerializationStop, Opcode = EventOpcode.Stop, Keywords = Keywords.Serialization, Level = EventLevel.Informational)] | ||
public void SerializationStop() | ||
{ | ||
if (IsEnabled(EventLevel.Informational, Keywords.Serialization)) | ||
{ | ||
WriteEvent(EventId_SerializationStop); | ||
} | ||
} | ||
|
||
[NonEvent] | ||
public void SerializingObject(Type type) | ||
{ | ||
Debug.Assert(type != null); | ||
|
||
if (IsEnabled(EventLevel.Informational, Keywords.Serialization)) | ||
{ | ||
SerializingObject(type.AssemblyQualifiedName); | ||
} | ||
} | ||
|
||
[Event(EventId_SerializingObject, Keywords = Keywords.Serialization, Level = EventLevel.Informational)] | ||
private void SerializingObject(string? typeName) | ||
{ | ||
WriteEvent(EventId_SerializingObject, typeName); | ||
} | ||
|
||
[Event(EventId_DeserializationStart, Opcode = EventOpcode.Start, Keywords = Keywords.Deserialization, Level = EventLevel.Informational, ActivityOptions = EventActivityOptions.Recursive)] | ||
public void DeserializationStart() | ||
{ | ||
if (IsEnabled(EventLevel.Informational, Keywords.Deserialization)) | ||
{ | ||
WriteEvent(EventId_DeserializationStart); | ||
} | ||
} | ||
|
||
[Event(EventId_DeserializationStop, Opcode = EventOpcode.Stop, Keywords = Keywords.Deserialization, Level = EventLevel.Informational)] | ||
public void DeserializationStop() | ||
{ | ||
if (IsEnabled(EventLevel.Informational, Keywords.Deserialization)) | ||
{ | ||
WriteEvent(EventId_DeserializationStop); | ||
} | ||
} | ||
|
||
[NonEvent] | ||
public void DeserializingObject(Type type) | ||
{ | ||
Debug.Assert(type != null); | ||
|
||
if (IsEnabled(EventLevel.Informational, Keywords.Deserialization)) | ||
{ | ||
DeserializingObject(type.AssemblyQualifiedName); | ||
} | ||
} | ||
|
||
[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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
216 changes: 216 additions & 0 deletions
216
...ibraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterEventSourceTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Collections.Generic; | ||
using System.Diagnostics.Tracing; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Runtime.Serialization.Formatters.Binary; | ||
using System.Threading; | ||
using Xunit; | ||
|
||
namespace System.Runtime.Serialization.Formatters.Tests | ||
{ | ||
[ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] | ||
public static class BinaryFormatterEventSourceTests | ||
{ | ||
private const string BinaryFormatterEventSourceName = "System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource"; | ||
|
||
[Fact] | ||
public static void RecordsSerialization() | ||
{ | ||
using LoggingEventListener listener = new LoggingEventListener(); | ||
|
||
BinaryFormatter formatter = new BinaryFormatter(); | ||
formatter.Serialize(Stream.Null, CreatePerson()); | ||
string[] capturedLog = listener.CaptureLog(); | ||
|
||
string[] expected = new string[] | ||
{ | ||
"SerializationStart [Start, 00000001]: <no payload>", | ||
"SerializingObject [Info, 00000001]: " + typeof(Person).AssemblyQualifiedName, | ||
"SerializingObject [Info, 00000001]: " + typeof(Address).AssemblyQualifiedName, | ||
"SerializationStop [Stop, 00000001]: <no payload>", | ||
}; | ||
|
||
Assert.Equal(expected, capturedLog); | ||
} | ||
|
||
[Fact] | ||
public static void RecordsDeserialization() | ||
{ | ||
MemoryStream ms = new MemoryStream(); | ||
BinaryFormatter formatter = new BinaryFormatter(); | ||
formatter.Serialize(ms, CreatePerson()); | ||
ms.Position = 0; | ||
|
||
using LoggingEventListener listener = new LoggingEventListener(); | ||
formatter.Deserialize(ms); | ||
string[] capturedLog = listener.CaptureLog(); | ||
|
||
string[] expected = new string[] | ||
{ | ||
"DeserializationStart [Start, 00000002]: <no payload>", | ||
"DeserializingObject [Info, 00000002]: " + typeof(Person).AssemblyQualifiedName, | ||
"DeserializingObject [Info, 00000002]: " + typeof(Address).AssemblyQualifiedName, | ||
"DeserializationStop [Stop, 00000002]: <no payload>", | ||
}; | ||
|
||
Assert.Equal(expected, capturedLog); | ||
} | ||
|
||
[Fact] | ||
public static void RecordsNestedSerializationCalls() | ||
{ | ||
// First, serialization | ||
|
||
using LoggingEventListener listener = new LoggingEventListener(); | ||
|
||
MemoryStream ms = new MemoryStream(); | ||
BinaryFormatter formatter = new BinaryFormatter(); | ||
formatter.Serialize(ms, new ClassWithNestedDeserialization()); | ||
string[] capturedLog = listener.CaptureLog(); | ||
ms.Position = 0; | ||
|
||
string[] expected = new string[] | ||
{ | ||
"SerializationStart [Start, 00000001]: <no payload>", | ||
"SerializingObject [Info, 00000001]: " + typeof(ClassWithNestedDeserialization).AssemblyQualifiedName, | ||
"SerializationStart [Start, 00000001]: <no payload>", | ||
"SerializingObject [Info, 00000001]: " + typeof(Address).AssemblyQualifiedName, | ||
"SerializationStop [Stop, 00000001]: <no payload>", | ||
"SerializationStop [Stop, 00000001]: <no payload>", | ||
}; | ||
|
||
Assert.Equal(expected, capturedLog); | ||
listener.ClearLog(); | ||
|
||
// Then, deserialization | ||
|
||
ms.Position = 0; | ||
formatter.Deserialize(ms); | ||
capturedLog = listener.CaptureLog(); | ||
|
||
expected = new string[] | ||
{ | ||
"DeserializationStart [Start, 00000002]: <no payload>", | ||
"DeserializingObject [Info, 00000002]: " + typeof(ClassWithNestedDeserialization).AssemblyQualifiedName, | ||
"DeserializationStart [Start, 00000002]: <no payload>", | ||
"DeserializingObject [Info, 00000002]: " + typeof(Address).AssemblyQualifiedName, | ||
"DeserializationStop [Stop, 00000002]: <no payload>", | ||
"DeserializationStop [Stop, 00000002]: <no payload>", | ||
}; | ||
|
||
Assert.Equal(expected, capturedLog); | ||
} | ||
|
||
private static Person CreatePerson() | ||
{ | ||
return new Person() | ||
{ | ||
Name = "Some Chap", | ||
HomeAddress = new Address() | ||
{ | ||
Street = "123 Anywhere Ln", | ||
City = "Anywhere ST 00000 United States" | ||
} | ||
}; | ||
} | ||
|
||
private sealed class LoggingEventListener : EventListener | ||
{ | ||
private readonly Thread _activeThread = Thread.CurrentThread; | ||
private readonly List<string> _log = new List<string>(); | ||
|
||
private void AddToLog(FormattableString message) | ||
{ | ||
_log.Add(FormattableString.Invariant(message)); | ||
} | ||
|
||
// Captures the current log | ||
public string[] CaptureLog() | ||
{ | ||
return _log.ToArray(); | ||
} | ||
|
||
public void ClearLog() | ||
{ | ||
_log.Clear(); | ||
} | ||
|
||
protected override void OnEventSourceCreated(EventSource eventSource) | ||
{ | ||
if (eventSource.Name == BinaryFormatterEventSourceName) | ||
{ | ||
EnableEvents(eventSource, EventLevel.Verbose); | ||
} | ||
|
||
base.OnEventSourceCreated(eventSource); | ||
} | ||
|
||
protected override void OnEventWritten(EventWrittenEventArgs eventData) | ||
{ | ||
// The test project is parallelized. We want to filter to only events that fired | ||
// on the current thread, otherwise we could throw off the test results. | ||
|
||
if (Thread.CurrentThread != _activeThread) | ||
{ | ||
return; | ||
} | ||
|
||
AddToLog($"{eventData.EventName} [{eventData.Opcode}, {(int)eventData.Keywords & int.MaxValue:X8}]: {ParsePayload(eventData.Payload)}"); | ||
base.OnEventWritten(eventData); | ||
} | ||
|
||
private static string ParsePayload(IReadOnlyCollection<object> collection) | ||
{ | ||
if (collection?.Count > 0) | ||
{ | ||
return string.Join("; ", collection.Select(o => o?.ToString() ?? "<null>")); | ||
} | ||
else | ||
{ | ||
return "<no payload>"; | ||
} | ||
} | ||
} | ||
|
||
[Serializable] | ||
private class Person | ||
{ | ||
public string Name { get; set; } | ||
public Address HomeAddress { get; set; } | ||
} | ||
|
||
[Serializable] | ||
private class Address | ||
{ | ||
public string Street { get; set; } | ||
public string City { get; set; } | ||
} | ||
|
||
[Serializable] | ||
public class ClassWithNestedDeserialization : ISerializable | ||
{ | ||
public ClassWithNestedDeserialization() | ||
{ | ||
} | ||
|
||
protected ClassWithNestedDeserialization(SerializationInfo info, StreamingContext context) | ||
{ | ||
byte[] serializedData = (byte[])info.GetValue("SomeField", typeof(byte[])); | ||
MemoryStream ms = new MemoryStream(serializedData); | ||
BinaryFormatter formatter = new BinaryFormatter(); | ||
formatter.Deserialize(ms); // should deserialize an 'Address' instance | ||
} | ||
|
||
public void GetObjectData(SerializationInfo info, StreamingContext context) | ||
{ | ||
MemoryStream ms = new MemoryStream(); | ||
BinaryFormatter formatter = new BinaryFormatter(); | ||
formatter.Serialize(ms, new Address()); | ||
info.AddValue("SomeField", ms.ToArray()); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.