summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/svc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/svc.cpp')
-rw-r--r--src/core/hle/kernel/svc.cpp91
1 files changed, 79 insertions, 12 deletions
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 3afcce3fe..3e4dd61dc 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -39,6 +39,73 @@ namespace {
constexpr bool Is4KBAligned(VAddr address) {
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
/// 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,
src_addr, size);
- if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
- return ERR_INVALID_ADDRESS;
- }
+ auto* const current_process = Core::CurrentProcess();
+ const auto& vm_manager = current_process->VMManager();
- if (size == 0 || !Is4KBAligned(size)) {
- return ERR_INVALID_SIZE;
+ const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
+ if (result != RESULT_SUCCESS) {
+ return result;
}
- return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size);
+ return current_process->MirrorMemory(dst_addr, src_addr, size);
}
/// 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,
src_addr, size);
- if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
- return ERR_INVALID_ADDRESS;
- }
+ auto* const current_process = Core::CurrentProcess();
+ const auto& vm_manager = current_process->VMManager();
- if (size == 0 || !Is4KBAligned(size)) {
- return ERR_INVALID_SIZE;
+ const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
+ if (result != RESULT_SUCCESS) {
+ return result;
}
- return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size);
+ return current_process->UnmapMemory(dst_addr, src_addr, size);
}
/// Connect to an OS service given the port name, returns the handle to the port to out