/// <summary>
        /// Unmaps mapped memory at a given range.
        /// </summary>
        /// <param name="location">Address of the range</param>
        /// <param name="size">Size of the range in bytes</param>
        public void UnmapRange(IntPtr location, IntPtr size)
        {
            ulong startAddress = (ulong)location;
            ulong unmapSize    = (ulong)size;
            ulong endAddress   = startAddress + unmapSize;

            var overlaps = Array.Empty <IntervalTreeNode <ulong, byte> >();
            int count    = 0;

            lock (_mappings)
            {
                count = _mappings.Get(startAddress, endAddress, ref overlaps);
            }

            for (int index = 0; index < count; index++)
            {
                var overlap = overlaps[index];

                // Tree operations might modify the node start/end values, so save a copy before we modify the tree.
                ulong unmapStart = Math.Max(overlap.Start, startAddress);
                ulong unmapEnd   = Math.Min(overlap.End, endAddress);

                _mappings.Remove(overlap);

                ulong currentAddress = unmapStart;
                while (currentAddress < unmapEnd)
                {
                    WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2);
                    currentAddress += PageSize;
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Coalesces adjacent placeholders after unmap.
        /// </summary>
        /// <param name="address">Address of the region that was unmapped</param>
        /// <param name="size">Size of the region that was unmapped in bytes</param>
        /// <param name="owner">Memory block that owns the mapping</param>
        private void CoalesceForUnmap(ulong address, ulong size, MemoryBlock owner)
        {
            ulong endAddress    = address + size;
            ulong blockAddress  = (ulong)owner.Pointer;
            ulong blockEnd      = blockAddress + owner.Size;
            var   overlaps      = Array.Empty <IntervalTreeNode <ulong, ulong> >();
            int   unmappedCount = 0;

            lock (_mappings)
            {
                int count = _mappings.Get(
                    Math.Max(address - MinimumPageSize, blockAddress),
                    Math.Min(endAddress + MinimumPageSize, blockEnd), ref overlaps);

                if (count < 2)
                {
                    // Nothing to coalesce if we only have 1 or no overlaps.
                    return;
                }

                for (int index = 0; index < count; index++)
                {
                    var overlap = overlaps[index];

                    if (!IsMapped(overlap.Value))
                    {
                        if (address > overlap.Start)
                        {
                            address = overlap.Start;
                        }

                        if (endAddress < overlap.End)
                        {
                            endAddress = overlap.End;
                        }

                        _mappings.Remove(overlap);

                        unmappedCount++;
                    }
                }

                _mappings.Add(address, endAddress, ulong.MaxValue);
            }

            if (unmappedCount > 1)
            {
                size = endAddress - address;

                CheckFreeResult(WindowsApi.VirtualFree(
                                    (IntPtr)address,
                                    (IntPtr)size,
                                    AllocationType.Release | AllocationType.CoalescePlaceholders));
            }
        }
Exemple #3
0
        /// <summary>
        /// Maps a shared memory view on a previously reserved memory region.
        /// </summary>
        /// <param name="sharedMemory">Shared memory that will be the backing storage for the view</param>
        /// <param name="srcOffset">Offset in the shared memory to map</param>
        /// <param name="location">Address to map the view into</param>
        /// <param name="size">Size of the view in bytes</param>
        /// <exception cref="WindowsApiException">Thrown when the Windows API returns an error mapping the memory</exception>
        private void MapViewInternal(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
        {
            SplitForMap((ulong)location, (ulong)size, srcOffset);

            var ptr = WindowsApi.MapViewOfFile3(
                sharedMemory,
                WindowsApi.CurrentProcessHandle,
                location,
                srcOffset,
                size,
                0x4000,
                MemoryProtection.ReadWrite,
                IntPtr.Zero,
                0);

            if (ptr == IntPtr.Zero)
            {
                throw new WindowsApiException("MapViewOfFile3");
            }
        }
Exemple #4
0
        /// <summary>
        /// Unreserves a range of memory that has been previously reserved with <see cref="ReserveRange"/>.
        /// </summary>
        /// <param name="address">Start address of the region to unreserve</param>
        /// <param name="size">Size in bytes of the region to unreserve</param>
        /// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unreserving the memory</exception>
        public void UnreserveRange(ulong address, ulong size)
        {
            ulong endAddress = address + size;

            var overlaps = Array.Empty <IntervalTreeNode <ulong, ulong> >();
            int count;

            lock (_mappings)
            {
                count = _mappings.Get(address, endAddress, ref overlaps);

                for (int index = 0; index < count; index++)
                {
                    var overlap = overlaps[index];

                    if (IsMapped(overlap.Value))
                    {
                        if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2))
                        {
                            throw new WindowsApiException("UnmapViewOfFile2");
                        }
                    }

                    _mappings.Remove(overlap);
                }
            }

            if (count > 1)
            {
                CheckFreeResult(WindowsApi.VirtualFree(
                                    (IntPtr)address,
                                    (IntPtr)size,
                                    AllocationType.Release | AllocationType.CoalescePlaceholders));
            }

            RemoveProtection(address, size);
        }
Exemple #5
0
        /// <summary>
        /// Reprotects a region of memory that has been mapped.
        /// </summary>
        /// <param name="address">Address of the region to reprotect</param>
        /// <param name="size">Size of the region to reprotect in bytes</param>
        /// <param name="permission">New permissions</param>
        /// <param name="throwOnError">Throw an exception instead of returning an error if the operation fails</param>
        /// <returns>True if the reprotection was successful or if <paramref name="throwOnError"/> is true, false otherwise</returns>
        /// <exception cref="WindowsApiException">If <paramref name="throwOnError"/> is true, it is thrown when the Windows API returns an error reprotecting the memory</exception>
        private bool ReprotectViewInternal(IntPtr address, IntPtr size, MemoryPermission permission, bool throwOnError)
        {
            ulong reprotectAddress = (ulong)address;
            ulong reprotectSize    = (ulong)size;
            ulong endAddress       = reprotectAddress + reprotectSize;

            var overlaps = Array.Empty <IntervalTreeNode <ulong, ulong> >();
            int count;

            lock (_mappings)
            {
                count = _mappings.Get(reprotectAddress, endAddress, ref overlaps);
            }

            bool success = true;

            for (int index = 0; index < count; index++)
            {
                var overlap = overlaps[index];

                ulong mappedAddress = overlap.Start;
                ulong mappedSize    = overlap.End - overlap.Start;

                if (mappedAddress < reprotectAddress)
                {
                    ulong delta = reprotectAddress - mappedAddress;
                    mappedAddress = reprotectAddress;
                    mappedSize   -= delta;
                }

                ulong mappedEndAddress = mappedAddress + mappedSize;

                if (mappedEndAddress > endAddress)
                {
                    ulong delta = mappedEndAddress - endAddress;
                    mappedSize -= delta;
                }

                if (!WindowsApi.VirtualProtect((IntPtr)mappedAddress, (IntPtr)mappedSize, WindowsApi.GetProtection(permission), out _))
                {
                    if (throwOnError)
                    {
                        throw new WindowsApiException("VirtualProtect");
                    }

                    success = false;
                }

                // We only keep track of "non-standard" protections,
                // that is, everything that is not just RW (which is the default when views are mapped).
                if (permission == MemoryPermission.ReadAndWrite)
                {
                    RemoveProtection(mappedAddress, mappedSize);
                }
                else
                {
                    AddProtection(mappedAddress, mappedSize, permission);
                }
            }

            return(success);
        }
Exemple #6
0
        /// <summary>
        /// Unmaps a view that has been previously mapped with <see cref="MapView"/>.
        /// </summary>
        /// <remarks>
        /// For "partial unmaps" (when not the entire mapped range is being unmapped), it might be
        /// necessary to unmap the whole range and then remap the sub-ranges that should remain mapped.
        /// </remarks>
        /// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param>
        /// <param name="location">Address to unmap</param>
        /// <param name="size">Size of the region to unmap in bytes</param>
        /// <param name="owner">Memory block that owns the mapping</param>
        /// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unmapping or remapping the memory</exception>
        private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
        {
            ulong startAddress = (ulong)location;
            ulong unmapSize    = (ulong)size;
            ulong endAddress   = startAddress + unmapSize;

            var overlaps = Array.Empty <IntervalTreeNode <ulong, ulong> >();
            int count;

            lock (_mappings)
            {
                count = _mappings.Get(startAddress, endAddress, ref overlaps);
            }

            for (int index = 0; index < count; index++)
            {
                var overlap = overlaps[index];

                if (IsMapped(overlap.Value))
                {
                    if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2))
                    {
                        throw new WindowsApiException("UnmapViewOfFile2");
                    }

                    // Tree operations might modify the node start/end values, so save a copy before we modify the tree.
                    ulong overlapStart = overlap.Start;
                    ulong overlapEnd   = overlap.End;
                    ulong overlapValue = overlap.Value;

                    lock (_mappings)
                    {
                        _mappings.Remove(overlap);
                        _mappings.Add(overlapStart, overlapEnd, ulong.MaxValue);
                    }

                    bool overlapStartsBefore = overlapStart < startAddress;
                    bool overlapEndsAfter    = overlapEnd > endAddress;

                    if (overlapStartsBefore || overlapEndsAfter)
                    {
                        // If the overlap extends beyond the region we are unmapping,
                        // then we need to re-map the regions that are supposed to remain mapped.
                        // This is necessary because Windows does not support partial view unmaps.
                        // That is, you can only fully unmap a view that was previously mapped, you can't just unmap a chunck of it.

                        LockCookie lockCookie = _partialUnmapLock.UpgradeToWriterLock(Timeout.Infinite);

                        _partialUnmapsCount++;

                        if (overlapStartsBefore)
                        {
                            ulong remapSize = startAddress - overlapStart;

                            MapViewInternal(sharedMemory, overlapValue, (IntPtr)overlapStart, (IntPtr)remapSize);
                            RestoreRangeProtection(overlapStart, remapSize);
                        }

                        if (overlapEndsAfter)
                        {
                            ulong overlappedSize     = endAddress - overlapStart;
                            ulong remapBackingOffset = overlapValue + overlappedSize;
                            ulong remapAddress       = overlapStart + overlappedSize;
                            ulong remapSize          = overlapEnd - endAddress;

                            MapViewInternal(sharedMemory, remapBackingOffset, (IntPtr)remapAddress, (IntPtr)remapSize);
                            RestoreRangeProtection(remapAddress, remapSize);
                        }

                        _partialUnmapLock.DowngradeFromWriterLock(ref lockCookie);
                    }
                }
            }

            CoalesceForUnmap(startAddress, unmapSize, owner);
            RemoveProtection(startAddress, unmapSize);
        }
Exemple #7
0
        /// <summary>
        /// Splits a larger placeholder, slicing at the start and end address, for a new memory mapping.
        /// </summary>
        /// <param name="address">Address to split</param>
        /// <param name="size">Size of the new region</param>
        /// <param name="backingOffset">Offset in the shared memory that will be mapped</param>
        private void SplitForMap(ulong address, ulong size, ulong backingOffset)
        {
            ulong endAddress = address + size;

            var overlaps = Array.Empty <IntervalTreeNode <ulong, ulong> >();

            lock (_mappings)
            {
                int count = _mappings.Get(address, endAddress, ref overlaps);

                Debug.Assert(count == 1);
                Debug.Assert(!IsMapped(overlaps[0].Value));

                var overlap = overlaps[0];

                // Tree operations might modify the node start/end values, so save a copy before we modify the tree.
                ulong overlapStart = overlap.Start;
                ulong overlapEnd   = overlap.End;
                ulong overlapValue = overlap.Value;

                _mappings.Remove(overlap);

                bool overlapStartsBefore = overlapStart < address;
                bool overlapEndsAfter    = overlapEnd > endAddress;

                if (overlapStartsBefore && overlapEndsAfter)
                {
                    CheckFreeResult(WindowsApi.VirtualFree(
                                        (IntPtr)address,
                                        (IntPtr)size,
                                        AllocationType.Release | AllocationType.PreservePlaceholder));

                    _mappings.Add(overlapStart, address, overlapValue);
                    _mappings.Add(endAddress, overlapEnd, AddBackingOffset(overlapValue, endAddress - overlapStart));
                }
                else if (overlapStartsBefore)
                {
                    ulong overlappedSize = overlapEnd - address;

                    CheckFreeResult(WindowsApi.VirtualFree(
                                        (IntPtr)address,
                                        (IntPtr)overlappedSize,
                                        AllocationType.Release | AllocationType.PreservePlaceholder));

                    _mappings.Add(overlapStart, address, overlapValue);
                }
                else if (overlapEndsAfter)
                {
                    ulong overlappedSize = endAddress - overlapStart;

                    CheckFreeResult(WindowsApi.VirtualFree(
                                        (IntPtr)overlapStart,
                                        (IntPtr)overlappedSize,
                                        AllocationType.Release | AllocationType.PreservePlaceholder));

                    _mappings.Add(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize));
                }

                _mappings.Add(address, endAddress, backingOffset);
            }
        }
Exemple #8
0
 private static string CreateMessage(string functionName)
 {
     return($"{functionName} returned error code 0x{WindowsApi.GetLastError():X}.");
 }