//////////////////////////////////////////////////////////////////////////////// // // 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 } } }