singrdk/base/Kernel/Singularity/SharedHeapWalker.cs

137 lines
5.6 KiB
C#

////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: SharedHeapWalker.cs
//
// Note:
//
using System.Threading;
using Microsoft.Singularity.Memory;
using Microsoft.Singularity.Channels;
namespace Microsoft.Singularity
{
internal class SharedHeapWalker
{
internal static SharedHeapWalker walker;
private AutoResetEvent doWalkEvent;
private unsafe SharedHeap.AllocationOwnerId allocationOwnerId;
private unsafe SharedHeap.AllocationOwnerId endpointOwnerId;
private SharedHeap.AllocationMatches match;
private SharedHeap.AllocationVisitor allocationVisitor;
private SharedHeap.AllocationVisitor endpointVisitor;
internal static unsafe void Initialize(
SharedHeap.AllocationOwnerId allocId,
SharedHeap.AllocationOwnerId endpointId)
{
walker = new SharedHeapWalker(allocId, endpointId);
}
private unsafe SharedHeapWalker(
SharedHeap.AllocationOwnerId allocId,
SharedHeap.AllocationOwnerId endpointId)
{
doWalkEvent = new AutoResetEvent(false);
allocationOwnerId = allocId;
endpointOwnerId = endpointId;
match = new SharedHeap.AllocationMatches(Matches);
allocationVisitor = new SharedHeap.AllocationVisitor(AllocationVisit);
endpointVisitor = new SharedHeap.AllocationVisitor(EndpointVisit);
Thread.CreateThread(Thread.CurrentProcess,
new ThreadStart(Loop)).Start();
}
// Tell the shared heap walker to start a walk at some time in
// the future. Multiple calls to DoWalk may be coalesced together,
// resulting in only a single walk.
internal void RequestWalk()
{
doWalkEvent.Set();
}
// Main loop of the shared heap walker. Walk each node in
// the shared heap whenever Signal is called.
//
// Currently, we signal the walker whenever any process exits,
// so that one very small process exit can trigger a traversal
// of a large shared heap. This raises issues:
// - Is this a wise use of resources in the common case?
// - Can a malicious small process leverage the large shared heap
// to deny service to others?
// We could avoid these issues by keeping segregated shared
// heap allocation lists (one list per process), but we feared
// the common case overhead of maintaining these lists (e.g. locking
// during Send/Receive). Furthermore:
// - The shared heap is typically not large, and process
// exit is infrequent compared to other operations, so the common
// case resource usage is not likely to be significant.
// - Only one thread walks the heap, so the denial-of-service
// potential is small. At worst, such an attack could:
// - Cause the thread below to use some fraction of the CPU time
// (a waste of cycles, but not a denial of service)
// - Try to increase the size of the shared heap in order to
// increase the latency of a shared heap walk (but it's not
// clear that well-behaved apps are affected by this latency)
private void Loop()
{
while (true) {
try {
doWalkEvent.WaitOne();
Tracing.Log(Tracing.Audit, "Shared heap walk begin");
allocationOwnerId.IterateMatchingForModify(
match, allocationVisitor);
endpointOwnerId.IterateMatchingForModify(
match, endpointVisitor);
// (Don't visit the endpointPeer list; this gets erased
// as part of walking the endpoint list.)
Tracing.Log(Tracing.Audit, "Shared heap walk end");
}
catch(System.Exception) {
Tracing.Log(Tracing.Warning, "Exception thrown in shared heap walker");
}
}
}
// requires alloc's owner locked
private unsafe bool Matches(SharedHeap.Allocation* alloc)
{
// TODO: using an integer id is only an approximate way to
// get the right process. Is this the right design?
Process process = Process.GetProcessByID(
SharedHeap.Allocation.GetOwnerProcessId(alloc));
return process == null;
}
// requires alloc's owner unlocked
private unsafe void AllocationVisit(SharedHeap.Allocation* alloc)
{
// TODO: support PAGING
#if !PAGING
SharedHeap.CurrentProcessSharedHeap.Free(alloc, allocationOwnerId);
#endif //PAGING
}
// requires alloc's owner unlocked
private unsafe void EndpointVisit(SharedHeap.Allocation* alloc)
{
// TODO: support PAGING
#if !PAGING
EndpointCore* epData = (EndpointCore*)
// Currently use unchecked access here, since the shared heap
// walker does not run in the same process as the data.
SharedHeap.Allocation.GetDataUnchecked(alloc);
if (!epData->Closed()) {
epData->Dispose();
}
EndpointCore.Free(alloc);
#endif //PAGING
}
}
}