kernel: fix page leak on process termination

This commit is contained in:
Liam
2024-01-07 13:59:48 -05:00
parent f7a3c135e2
commit f2fed21c11
4 changed files with 91 additions and 25 deletions

View File

@ -431,9 +431,82 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
m_memory_block_slab_manager));
}
Result KPageTableBase::FinalizeProcess() {
// Only process tables should be finalized.
ASSERT(!this->IsKernel());
// HLE processes don't have memory mapped.
R_SUCCEED_IF(m_impl == nullptr);
// NOTE: Here Nintendo calls an unknown OnFinalize function.
// this->OnFinalize();
// NOTE: Here Nintendo calls a second unknown OnFinalize function.
// this->OnFinalize2();
// Get implementation objects.
auto& impl = this->GetImpl();
auto& mm = m_kernel.MemoryManager();
// Traverse, freeing all pages.
{
// Get the address space size.
const size_t as_size = this->GetAddressSpaceSize();
// Begin the traversal.
TraversalContext context;
TraversalEntry cur_entry = {
.phys_addr = 0,
.block_size = 0,
};
bool cur_valid = false;
TraversalEntry next_entry;
bool next_valid;
size_t tot_size = 0;
next_valid = impl.BeginTraversal(std::addressof(next_entry), std::addressof(context),
this->GetAddressSpaceStart());
// Iterate over entries.
while (true) {
if ((!next_valid && !cur_valid) ||
(next_valid && cur_valid &&
next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) {
cur_entry.block_size += next_entry.block_size;
} else {
if (cur_valid && IsHeapPhysicalAddressForFinalize(cur_entry.phys_addr)) {
mm.Close(cur_entry.phys_addr, cur_entry.block_size / PageSize);
}
// Update tracking variables.
tot_size += cur_entry.block_size;
cur_entry = next_entry;
cur_valid = next_valid;
}
if (cur_entry.block_size + tot_size >= as_size) {
break;
}
next_valid =
impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
}
// Handle the last block.
if (cur_valid && IsHeapPhysicalAddressForFinalize(cur_entry.phys_addr)) {
mm.Close(cur_entry.phys_addr, cur_entry.block_size / PageSize);
}
}
R_SUCCEED();
}
void KPageTableBase::Finalize() {
this->FinalizeProcess();
auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
if (Settings::IsFastmemEnabled()) {
if (m_impl->fastmem_arena) {
m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
}
};

View File

@ -241,6 +241,7 @@ public:
KResourceLimit* resource_limit, Core::Memory::Memory& memory,
KProcessAddress aslr_space_start);
Result FinalizeProcess();
void Finalize();
bool IsKernel() const {

View File

@ -171,6 +171,12 @@ void KProcess::Finalize() {
m_resource_limit->Close();
}
// Clear expensive resources, as the destructor is not called for guest objects.
for (auto& interface : m_arm_interfaces) {
interface.reset();
}
m_exclusive_monitor.reset();
// Perform inherited finalization.
KSynchronizationObject::Finalize();
}