From cc7174690c2c552bc5320ac922dc8be0e60ea67f Mon Sep 17 00:00:00 2001 From: AdvDebug <90452585+AdvDebug@users.noreply.github.com> Date: Sat, 1 Aug 2026 14:41:41 +0300 Subject: [PATCH] Fix partial unmap tracking in Unicorn binding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve mapped-region bookkeeping so `UnmapMemory` correctly handles partial overlaps by splitting surviving segments and updating pointers. Track each region’s original buffer base to prevent invalid frees when regions are split, and only release buffers when no surviving alias remains. Disposal now also deduplicates buffer frees to avoid double-free risks. --- .../Core/Emulation/UnicornBinding/Unicorn.cs | 109 +++++++++++++++--- 1 file changed, 92 insertions(+), 17 deletions(-) diff --git a/Brovan/Core/Emulation/UnicornBinding/Unicorn.cs b/Brovan/Core/Emulation/UnicornBinding/Unicorn.cs index c8484f7..a55b34f 100644 --- a/Brovan/Core/Emulation/UnicornBinding/Unicorn.cs +++ b/Brovan/Core/Emulation/UnicornBinding/Unicorn.cs @@ -41,6 +41,8 @@ namespace Brovan.Core.Emulation private readonly List _regionIndex = new List(); private bool _regionIndexDirty = true; private readonly List _pendingFrees = new List(); + private readonly List _unmapSurvivors = new List(); + private readonly List _unmapReleasedBuffers = new List(); private List HooksList = new List(); private sealed class MappedRegion @@ -48,6 +50,7 @@ namespace Brovan.Core.Emulation public ulong Address; public ulong Size; public IntPtr Ptr; + public IntPtr BufferBase; } private readonly struct RegionAddressComparer : IComparer @@ -177,7 +180,7 @@ namespace Brovan.Core.Emulation _error = uc_mem_map_ptr(_uc, address, new UIntPtr(size), protection, (IntPtr)ptr); if (_error == UCErrors.UC_ERR_OK) { - _mappedRegions.Add(new MappedRegion { Address = address, Size = size, Ptr = (IntPtr)ptr }); + _mappedRegions.Add(new MappedRegion { Address = address, Size = size, Ptr = (IntPtr)ptr, BufferBase = (IntPtr)ptr }); _regionIndexDirty = true; return true; } @@ -187,7 +190,7 @@ namespace Brovan.Core.Emulation _error = uc_mem_map(_uc, address, new UIntPtr(size), protection); if (_error == UCErrors.UC_ERR_OK) { - _mappedRegions.Add(new MappedRegion { Address = address, Size = size, Ptr = IntPtr.Zero }); + _mappedRegions.Add(new MappedRegion { Address = address, Size = size, Ptr = IntPtr.Zero, BufferBase = IntPtr.Zero }); _regionIndexDirty = true; return true; } @@ -212,24 +215,91 @@ namespace Brovan.Core.Emulation if (_error == UCErrors.UC_ERR_OK) { FlushTlb(); - - for (int i = 0; i < _mappedRegions.Count; i++) - { - if (_mappedRegions[i].Address == address && _mappedRegions[i].Size == size) - { - if (_mappedRegions[i].Ptr != IntPtr.Zero) - _pendingFrees.Add(_mappedRegions[i].Ptr); - _mappedRegions.RemoveAt(i); - _regionIndexDirty = true; - break; - } - } + TrimMappedRegions(address, size); return true; } } return false; } + private unsafe void TrimMappedRegions(ulong address, ulong size) + { + ulong end = address + size; + bool changed = false; + + _unmapSurvivors.Clear(); + _unmapReleasedBuffers.Clear(); + + for (int i = _mappedRegions.Count - 1; i >= 0; i--) + { + MappedRegion Region = _mappedRegions[i]; + ulong RegionEnd = Region.Address + Region.Size; + if (RegionEnd <= address || end <= Region.Address) + continue; + + ulong OverlapStart = Region.Address > address ? Region.Address : address; + ulong OverlapEnd = RegionEnd < end ? RegionEnd : end; + + _mappedRegions.RemoveAt(i); + changed = true; + + if (OverlapStart > Region.Address) + { + _unmapSurvivors.Add(new MappedRegion + { + Address = Region.Address, + Size = OverlapStart - Region.Address, + Ptr = Region.Ptr, + BufferBase = Region.BufferBase + }); + } + + if (RegionEnd > OverlapEnd) + { + IntPtr TailPtr = Region.Ptr == IntPtr.Zero + ? IntPtr.Zero + : (IntPtr)((byte*)Region.Ptr + (OverlapEnd - Region.Address)); + + _unmapSurvivors.Add(new MappedRegion + { + Address = OverlapEnd, + Size = RegionEnd - OverlapEnd, + Ptr = TailPtr, + BufferBase = Region.BufferBase + }); + } + + if (Region.BufferBase != IntPtr.Zero && !_unmapReleasedBuffers.Contains(Region.BufferBase)) + _unmapReleasedBuffers.Add(Region.BufferBase); + } + + if (!changed) + return; + + for (int i = 0; i < _unmapSurvivors.Count; i++) + _mappedRegions.Add(_unmapSurvivors[i]); + + _regionIndexDirty = true; + + for (int i = 0; i < _unmapReleasedBuffers.Count; i++) + { + IntPtr Buffer = _unmapReleasedBuffers[i]; + bool StillAliased = false; + + for (int j = 0; j < _mappedRegions.Count; j++) + { + if (_mappedRegions[j].BufferBase == Buffer) + { + StillAliased = true; + break; + } + } + + if (!StillAliased) + _pendingFrees.Add(Buffer); + } + } + /// /// Write to an emulated memory address. /// @@ -1523,11 +1593,16 @@ namespace Brovan.Core.Emulation { unsafe { + _unmapReleasedBuffers.Clear(); foreach (var region in _mappedRegions) { - if (region.Ptr != IntPtr.Zero) - NativeMemory.AlignedFree((void*)region.Ptr); + if (region.BufferBase != IntPtr.Zero && !_unmapReleasedBuffers.Contains(region.BufferBase)) + _unmapReleasedBuffers.Add(region.BufferBase); } + foreach (IntPtr buffer in _unmapReleasedBuffers) + NativeMemory.AlignedFree((void*)buffer); + _unmapReleasedBuffers.Clear(); + _unmapSurvivors.Clear(); _mappedRegions.Clear(); _regionIndex.Clear(); _regionIndexDirty = true; @@ -1552,4 +1627,4 @@ namespace Brovan.Core.Emulation } } } -} +} \ No newline at end of file