137 lines
5.6 KiB
C#
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
|
||
|
}
|
||
|
}
|
||
|
}
|