Merge pull request #3683 from Sonicadvance1/fix_broken_mprotect

SMCTracking: Fix incorrect mprotect tracking
This commit is contained in:
Ryan Houdek 2024-06-20 22:49:51 -07:00 committed by GitHub
commit d7348c8aff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -245,7 +245,14 @@ void SyscallHandler::VMATracking::ClearUnsafe(FEXCore::Context::Context* CTX, ui
// Change flags of mappings in a range and split the mappings if needed
void SyscallHandler::VMATracking::ChangeUnsafe(uintptr_t Base, uintptr_t Length, VMAProt NewProt) {
const auto Top = Base + Length;
// This needs to handle multiple split-merge strategies:
// 1) Exact overlap - No Split, no Merge. Only protection tracking changes.
// 2) Exact base overlap - Single insert, can never fail.
// 3) Insert in middle of VMA range. 1 or 2 inserts, can never fail.
// 4) Partial overlapping merge. The most interesting strategy.
// - More information below about this one.
auto Top = Base + Length;
// find the first Mapping at or after the Range ends, or ::end()
// Top is the address after the end
@ -256,70 +263,213 @@ void SyscallHandler::VMATracking::ChangeUnsafe(uintptr_t Base, uintptr_t Length,
MappingIter--;
auto Current = &MappingIter->second;
const auto MapBase = Current->Base;
const auto MapTop = MapBase + Current->Length;
const auto MapFlags = Current->Flags;
const auto MapProt = Current->Prot;
const auto OffsetDiff = Current->Offset - MapBase;
if (MapTop <= Base) {
// Mapping ends before the Range start, exit
if (Current->Base <= Base || Current->Base + Current->Length < Top) {
break;
} else if (MapProt.All == NewProt.All) {
// Mapping already has the needed prots
continue;
} else {
const bool HasFirstPart = MapBase < Base;
const bool HasTrailingPart = MapTop > Top;
}
if (HasFirstPart) {
// Mapping starts before range, split first part
const auto CurrentBase = Current->Base;
const auto CurrentTop = CurrentBase + Current->Length;
const auto CurrentFlags = Current->Flags;
const auto CurrentProt = Current->Prot;
// Trim end of original mapping
Current->Length = Base - MapBase;
///< Resource mapping base.
const auto OffsetDiff = Current->Offset - CurrentBase;
// Make new VMA with new flags, insert for length of range
auto NewOffset = OffsetDiff + Base;
auto NewLength = Top - Base;
// Merge strategy 4)
// CurrentBase range doesn't fully overlap the starting range but does overlap the tail.
// This is the most confusing strategy as it requires splitting the protect range itself.
//
// if the VMA has tail data after the protection range we must first deal with that:
// 1) Split the tail data in to new VMA range with original protections. Must not fail.
// 2) Adjust the overlapping VMA protections to the new protections and the truncated length
// 3) Truncate the mprotecting length and top to be that untouched range. Next loop will continue inserting.
// [ Incoming Ranges ]
// CurrentVMA: [CurrentBase ====== CurrentTop)
// CurrentMProtectRange: [Base =============== Top)**********************
// [ Modified Ranges ]
// New Tail Range: [TailBase === Tail Top)
// CurrentVMA Modified Range: [=======)
// Remaining Tracking: [Base ==== NewTop)
//
// Next loop iterations will decompose the remaining mprotects in to more merge strategies.
auto [Iter, Inserted] =
VMAs.emplace(Base, VMAEntry {Current->Resource, Current, Current->ResourceNextVMA, Base, NewOffset, NewLength, MapFlags, NewProt});
LOGMAN_THROW_A_FMT(Inserted == true, "VMA tracking error");
auto RestOfMapping = &Iter->second;
// Steps:
// 1) Split VMA if Top != CurrentTop
// 2) Change [CurrentBase, Top) protections
// 3) Change CurrentVMA length
// 4) Adjust searching length for [Base, CurrentBase)
const bool HasTailData = CurrentTop > Top;
if (Current->Resource) {
ListInsertAfter(Current, RestOfMapping);
}
if (HasTailData) {
// We now need to insert another VMA entry afterwards to ensure consistency.
// This will have the original VMA's protection flags.
Current = RestOfMapping;
} else {
// Mapping starts in range, just change Prot
Current->Prot = NewProt;
// Make new VMA with new flags, insert for length of range
auto NewOffset = OffsetDiff + CurrentBase;
auto NewLength = CurrentTop - Top;
auto [Iter, Inserted] = VMAs.emplace(Top, VMAEntry {.Resource = Current->Resource,
.ResourcePrevVMA = Current,
.ResourceNextVMA = Current->ResourceNextVMA,
.Base = Top,
.Offset = NewOffset,
.Length = NewLength,
.Flags = CurrentFlags,
.Prot = CurrentProt});
if (!Inserted) {
// We can't recover from this.
// Shouldn't ever happen.
ERROR_AND_DIE_FMT("{}:{}: VMA tracking error", __func__, __LINE__);
}
if (HasTrailingPart) {
// ends after Range, split last part and insert with original flags
// Trim the mapping (possibly already trimmed)
Current->Length = Top - Current->Base;
// prot has already been changed
// Make new VMA with original flags, insert for remaining length
auto NewOffset = OffsetDiff + Top;
auto NewLength = MapTop - Top;
auto [Iter, Inserted] =
VMAs.emplace(Top, VMAEntry {Current->Resource, Current, Current->ResourceNextVMA, Top, NewOffset, NewLength, MapFlags, MapProt});
LOGMAN_THROW_A_FMT(Inserted == true, "VMA tracking error");
auto TrailingMapping = &Iter->second;
if (Current->Resource) {
ListInsertAfter(Current, TrailingMapping);
}
if (Current->Resource) {
ListInsertAfter(Current, &Iter->second);
}
}
// Change CurrentVMA's protections
Current->Prot = NewProt;
// Change CurrentVMA's length
Current->Length = Top - CurrentBase;
// Adjust the protection length we're searching for.
// Next loop will pick up the next check.
Length = CurrentBase - Base;
Top = Base + Length;
}
auto Current = &MappingIter->second;
const auto CurrentBase = Current->Base;
const auto CurrentTop = CurrentBase + Current->Length;
const auto CurrentFlags = Current->Flags;
const auto CurrentProt = Current->Prot;
///< Resource mapping base.
const auto OffsetDiff = Current->Offset - CurrentBase;
if (CurrentTop <= Base) {
// Mapping is below what we care about
// [CurrentBase === CurrentTop)
// [Base === Top)
} else if (CurrentBase == Base && CurrentTop == Top) {
// Merge strategy 1)
// Exact encompassing, quite common.
// [CurrentBase ======================== CurrentTop)
// [Base ====================================== Top)
Current->Prot = NewProt;
} else if (CurrentBase == Base && CurrentTop > Top) {
// Merge strategy 2)
// [CurrentBase ======================== CurrentTop)
// [Base =============== Top)***********************
// VMA fully encompasses with matching base.
// VMA needs to split.
// Steps:
// 1) Set new permissions for this VMA
// 2) Trim VMA->Length to match [CurrentBase, CurrentBase+Length)
// 2) Insert new node at [CurrentBase+Length, CurrentTop)
// 1) Set new permissions
Current->Prot = NewProt;
// Trim end of original mapping
// New length for Current VMA is Top - CurrentBase
Current->Length = Top - CurrentBase;
// Make new VMA with original protections, insert for remaining length
auto NewOffset = OffsetDiff + Top;
auto NewLength = CurrentTop - Top;
auto [Iter, Inserted] = VMAs.emplace(Top, VMAEntry {.Resource = Current->Resource,
.ResourcePrevVMA = Current,
.ResourceNextVMA = Current->ResourceNextVMA,
.Base = Top,
.Offset = NewOffset,
.Length = NewLength,
.Flags = CurrentFlags,
.Prot = CurrentProt});
if (!Inserted) [[unlikely]] {
// We can't recover from this.
// Shouldn't ever happen.
ERROR_AND_DIE_FMT("{}:{}: VMA tracking error", __func__, __LINE__);
}
if (Current->Resource) {
ListInsertAfter(Current, &Iter->second);
}
} else if (CurrentBase < Base && CurrentTop >= Top) {
// Merge strategy 3)
// VMA fully encompasses, VMA needs to split.
// Explicitly VMA base doesn't match current base.
// [CurrentBase ======================== CurrentTop)
// ***************[Base =============== Top)********
// Steps:
// 1) Split the CurrentVMA
// 2) Set new length of CurrentVMA
// 3) If there is tail length still, Insert another new VMA with CurrentVMA data.
const bool HasTailData = CurrentTop > Top;
// Trim end of original mapping
Current->Length = Base - CurrentBase;
{
// Make new VMA with new flags, insert for length of range
auto NewOffset = OffsetDiff + Base;
auto NewLength = Top - Base;
auto [Iter, Inserted] = VMAs.emplace(Base, VMAEntry {.Resource = Current->Resource,
.ResourcePrevVMA = Current,
.ResourceNextVMA = Current->ResourceNextVMA,
.Base = Base,
.Offset = NewOffset,
.Length = NewLength,
.Flags = CurrentFlags,
.Prot = NewProt});
if (!Inserted) [[unlikely]] {
// We can't recover from this.
// Shouldn't ever happen.
ERROR_AND_DIE_FMT("{}:{}: VMA tracking error", __func__, __LINE__);
}
if (Current->Resource) {
ListInsertAfter(Current, &Iter->second);
}
}
if (HasTailData) {
// We now need to insert another VMA entry afterwards to ensure consistency.
// This will have the original VMA's protection flags.
// Make new VMA with new flags, insert for length of range
auto NewOffset = OffsetDiff + Top;
auto NewLength = CurrentTop - Top;
auto [Iter, Inserted] = VMAs.emplace(Top, VMAEntry {.Resource = Current->Resource,
.ResourcePrevVMA = Current,
.ResourceNextVMA = Current->ResourceNextVMA,
.Base = Top,
.Offset = NewOffset,
.Length = NewLength,
.Flags = CurrentFlags,
.Prot = CurrentProt});
if (!Inserted) {
// We can't recover from this.
// Shouldn't ever happen.
ERROR_AND_DIE_FMT("{}:{}: VMA tracking error", __func__, __LINE__);
}
if (Current->Resource) {
ListInsertAfter(Current, &Iter->second);
}
}
} else {
ERROR_AND_DIE_FMT("Unexpected {} Merge strategy! [0x{:x}, 0x{:x}) Versus [0x{:x}, 0x{:x})\n", __func__, CurrentBase, CurrentTop, Base, Top);
}
}