1
0
Fork 0
forked from suyu/suyu

svc: Add missing address range sanitizing checks to MapMemory/UnmapMemory

This adds the missing address range checking that the service functions
do before attempting to map or unmap memory. Given that both service
functions perform the same set of checks in the same order, we can wrap
these into a function and just call it from both functions, which
deduplicates a little bit of code.
This commit is contained in:
Lioncash 2018-10-10 14:18:27 -04:00
parent 03ec936ca0
commit 72e9cb523e
2 changed files with 83 additions and 14 deletions

View file

@ -22,6 +22,7 @@ enum {
HandleTableFull = 105, HandleTableFull = 105,
InvalidMemoryState = 106, InvalidMemoryState = 106,
InvalidMemoryPermissions = 108, InvalidMemoryPermissions = 108,
InvalidMemoryRange = 110,
InvalidThreadPriority = 112, InvalidThreadPriority = 112,
InvalidProcessorId = 113, InvalidProcessorId = 113,
InvalidHandle = 114, InvalidHandle = 114,
@ -56,6 +57,7 @@ constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidA
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
ErrCodes::InvalidMemoryPermissions); ErrCodes::InvalidMemoryPermissions);
constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);

View file

@ -39,6 +39,73 @@ namespace {
constexpr bool Is4KBAligned(VAddr address) { constexpr bool Is4KBAligned(VAddr address) {
return (address & 0xFFF) == 0; return (address & 0xFFF) == 0;
} }
// Checks if address + size is greater than the given address
// This can return false if the size causes an overflow of a 64-bit type
// or if the given size is zero.
constexpr bool IsValidAddressRange(VAddr address, u64 size) {
return address + size > address;
}
// Checks if a given address range lies within a larger address range.
constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
VAddr address_range_end) {
const VAddr end_address = address + size - 1;
return address_range_begin <= address && end_address <= address_range_end - 1;
}
bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
vm.GetAddressSpaceEndAddress());
}
bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
vm.GetNewMapRegionEndAddress());
}
// Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order.
ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
u64 size) {
if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
return ERR_INVALID_ADDRESS;
}
if (size == 0 || !Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(dst_addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsValidAddressRange(src_addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
return ERR_INVALID_MEMORY_RANGE;
}
const VAddr dst_end_address = dst_addr + size;
if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
dst_addr < vm_manager.GetHeapRegionEndAddress()) {
return ERR_INVALID_MEMORY_RANGE;
}
if (dst_end_address > vm_manager.GetNewMapRegionBaseAddress() &&
dst_addr < vm_manager.GetMapRegionEndAddress()) {
return ERR_INVALID_MEMORY_RANGE;
}
return RESULT_SUCCESS;
}
} // Anonymous namespace } // Anonymous namespace
/// Set the process heap to a given Size. It can both extend and shrink the heap. /// Set the process heap to a given Size. It can both extend and shrink the heap.
@ -69,15 +136,15 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size); src_addr, size);
if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { auto* const current_process = Core::CurrentProcess();
return ERR_INVALID_ADDRESS; const auto& vm_manager = current_process->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
if (result != RESULT_SUCCESS) {
return result;
} }
if (size == 0 || !Is4KBAligned(size)) { return current_process->MirrorMemory(dst_addr, src_addr, size);
return ERR_INVALID_SIZE;
}
return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size);
} }
/// Unmaps a region that was previously mapped with svcMapMemory /// Unmaps a region that was previously mapped with svcMapMemory
@ -85,15 +152,15 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size); src_addr, size);
if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { auto* const current_process = Core::CurrentProcess();
return ERR_INVALID_ADDRESS; const auto& vm_manager = current_process->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
if (result != RESULT_SUCCESS) {
return result;
} }
if (size == 0 || !Is4KBAligned(size)) { return current_process->UnmapMemory(dst_addr, src_addr, size);
return ERR_INVALID_SIZE;
}
return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size);
} }
/// Connect to an OS service given the port name, returns the handle to the port to out /// Connect to an OS service given the port name, returns the handle to the port to out