Revision control
Copy as Markdown
// Copyright (C) Rolland Dudemaine All rights reserved.
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "config.h"
#include "OSAllocator.h"
#include <INTEGRITY.h>
#include <memory_region.h>
#include <set>
#include <wtf/Assertions.h>
#include <wtf/UnusedParam.h>
#define ASP_PAGESIZE 0x1000
namespace WTF {
struct MRPair {
mutable MemoryRegion pmr;
mutable MemoryRegion vmr;
mutable bool mapped;
Address start;
MRPair(Address _start = 0) :
pmr(0),
vmr(0),
mapped(false),
start(_start)
{}
bool operator<(const MRPair& rhs) const
{
return this->start < rhs.start;
}
};
class MRContainer
{
private:
std::set<MRPair> mrset;
LocalMutex iteratorGuard;
public:
MRContainer() {
CheckSuccess(CreateLocalMutex(&iteratorGuard));
}
const MRPair* getMRPair(Address start) {
WaitForLocalMutex(iteratorGuard);
auto pairIterator = mrset.find(MRPair(start));
const MRPair* result = ((pairIterator == mrset.end()) ? NULL : &(*pairIterator));
ReleaseLocalMutex(iteratorGuard);
return result;
}
Error deleteMRPair(const MRPair* pair) {
int erased = 0;
WaitForLocalMutex(iteratorGuard);
erased = mrset.erase(*pair);
ReleaseLocalMutex(iteratorGuard);
if(erased == 1)
return Success;
else
return ArgumentError; /* An exception could be thrown in this case */
}
Error insertMRPair(MRPair* pair) {
WaitForLocalMutex(iteratorGuard);
auto inserted = mrset.insert(*pair);
ReleaseLocalMutex(iteratorGuard);
if(inserted.second == true)
return Success;
else
return Failure; /* An exception could be thrown in this case */
}
~MRContainer() {
CheckSuccess(CloseLocalMutex(iteratorGuard));
}
};
static MRContainer memoryRegionsContainer;
Error setAttributes(MemoryRegion mr, bool writable, bool executable)
{
Value attributes = MEMORY_READ;
if(writable)
attributes |= MEMORY_WRITE;
if(executable)
attributes |= MEMORY_EXEC;
return SetMemoryRegionAttributes(mr, attributes);
}
void OSAllocator::setMemoryAttributes(void* addr, size_t size, bool writable, bool executable)
{
Address addressIterator = Address(addr);
for(int i=0; i<(size + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) {
const MRPair* pair = memoryRegionsContainer.getMRPair(addressIterator);
CheckSuccess(setAttributes(pair->vmr, writable, executable));
addressIterator += ASP_PAGESIZE;
}
}
void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable)
{
MemoryRegion VMR;
Address virtualStart, length;
CheckSuccess(AllocateAnyMemoryRegion(__ghs_VirtualMemoryRegionPool, bytes, &VMR));
CheckSuccess(GetMemoryRegionAddresses(VMR, &virtualStart, &length));
Address addressIterator = virtualStart;
for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) {
MRPair pair;
pair.start = addressIterator;
CheckSuccess(SplitMemoryRegion(VMR, ASP_PAGESIZE, &pair.vmr));
CheckSuccess(setAttributes(pair.vmr, writable, executable));
memoryRegionsContainer.insertMRPair(&pair);
addressIterator += ASP_PAGESIZE;
}
CheckSuccess(CloseMemoryRegion(VMR));
return (void*)virtualStart;
}
void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages)
{
MemoryRegion VMR;
Address virtualStart, length;
CheckSuccess(AllocateAnyMemoryRegion(__ghs_VirtualMemoryRegionPool, bytes, &VMR));
CheckSuccess(GetMemoryRegionAddresses(VMR, &virtualStart, &length));
Address addressIterator = virtualStart;
for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) {
MRPair pair;
pair.start = addressIterator;
CheckSuccess(SplitMemoryRegion(VMR, ASP_PAGESIZE, &pair.vmr));
CheckSuccess(setAttributes(pair.vmr, writable, executable));
/* Do not map the first and the last pages if guard pages are required */
if(!includesGuardPages || (i!=0 && i!= (bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE -1))
{
CheckSuccess(GetPageFromAddressSpaceFreeList(GetCurrentAddressSpace(), &pair.pmr));
CheckSuccess(MapMemoryRegion(pair.vmr, pair.pmr));
pair.mapped = true;
}
memoryRegionsContainer.insertMRPair(&pair);
addressIterator += ASP_PAGESIZE;
}
CheckSuccess(CloseMemoryRegion(VMR));
return (void*)virtualStart;
}
void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable)
{
for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++)
{
const MRPair* pair = memoryRegionsContainer.getMRPair((Address)address);
if(pair == NULL)
return;
CheckSuccess(setAttributes(pair->vmr, writable, executable));
CheckSuccess(GetPageFromAddressSpaceFreeList(GetCurrentAddressSpace(), &pair->pmr));
CheckSuccess(MapMemoryRegion(pair->vmr, pair->pmr));
pair->mapped = true;
address = (char*)address + ASP_PAGESIZE;
}
}
void OSAllocator::decommit(void* address, size_t bytes)
{
for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++)
{
const MRPair* pair = memoryRegionsContainer.getMRPair((Address)address);
if(pair == NULL)
return;
if(pair->mapped == false)
continue;
CheckSuccess(UnmapMemoryRegion(pair->vmr));
CheckSuccess(PutPageOnAddressSpaceFreeList(GetCurrentAddressSpace(), pair->pmr));
pair->mapped = false;
address = (char*)address + ASP_PAGESIZE;
}
}
void OSAllocator::releaseDecommitted(void* address, size_t bytes)
{
for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++)
{
const MRPair* pair = memoryRegionsContainer.getMRPair((Address)address);
if(pair == NULL)
return;
/* Check if the memory is still committed */
if(pair->mapped == true)
{
CheckSuccess(UnmapMemoryRegion(pair->vmr));
CheckSuccess(PutPageOnAddressSpaceFreeList(GetCurrentAddressSpace(), pair->pmr));
pair->mapped = false;
}
CheckSuccess(AddToMemoryPool(__ghs_VirtualMemoryRegionPool, pair->vmr));
address = (char*)address + ASP_PAGESIZE;
memoryRegionsContainer.deleteMRPair(pair);
}
}
bool OSAllocator::canAllocateExecutableMemory()
{
return true;
}
} // namespace WTF