aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle
diff options
context:
space:
mode:
authorSssnowyyy <78863999+Sssnowyyy@users.noreply.github.com>2026-11-02 11:27:20 -0500
committerSssnowyyy <78863999+Sssnowyyy@users.noreply.github.com>2026-11-02 14:53:47 -0500
commite277b4ba61694f229b38ec0dc62f31c35d0cbff3 (patch)
tree5a89d43ccd30da38ad8c3423344d091d0b3f7267 /src/core/hle
parent089105ab44c87f1ef0526221144a1979c56558e6 (diff)
nx2: make the 216 sparse request range checks overflow-safe
4d6a13e3d9 and 3d6156434a added bounds checks to the nx2:gpu 216 sparse map path, but the region checks still compute gpu_address + size, which wraps for a guest-controlled size. 3d6156434a only guards size == 0 and the memory offset on the map path, so an unmap (memory handle 0) with a huge size matches a region through the wrapped sum and reaches Nx2SparseTile, whose own check wraps the same way, and then smmu.Unmap(device, size) with a giant page count. Reject a request whose end wraps the address space before it is used, for map and unmap alike, with a warning like the existing unknown-memory check. Also compare by subtraction in the region lookups here and in Nx2SparseTile and Nx2BackSparse, so none of them depends on the sum. Assisted-by: Claude Sonnet 5.5 (implementation and commit message)
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp4
-rw-r--r--src/core/hle/service/nx2_gsv.h12
2 files changed, 11 insertions, 5 deletions
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 40313a10fb..843d31caf1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -270,7 +270,7 @@ void nvhost_as_gpu::Nx2BackSparse(u64 gpu_address, u64 cpu_address, u64 size) {
--it;
const auto& [start, range] = *it;
const auto& [region_size, device_address] = range;
- if (gpu_address + size > start + region_size) {
+ if (size > region_size || gpu_address - start > region_size - size) {
return;
}
system.Host1x().MemoryManager().Map(device_address + (gpu_address - start), cpu_address, size,
@@ -287,7 +287,7 @@ void nvhost_as_gpu::Nx2SparseTile(u64 gpu_address, u64 cpu_address, u64 size) {
--it;
const auto& [start, range] = *it;
const auto& [region_size, device_address] = range;
- if (gpu_address + size > start + region_size) {
+ if (size > region_size || gpu_address - start > region_size - size) {
return;
}
// In GPU-thread order: command lists submitted before the request still see the old tile.
diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h
index 37c797fa3f..bc354b4d8f 100644
--- a/src/core/hle/service/nx2_gsv.h
+++ b/src/core/hle/service/nx2_gsv.h
@@ -15,6 +15,7 @@
#include <cstdlib>
#include <cstdio>
#include <initializer_list>
+#include <limits>
#include <atomic>
#include <deque>
#include <map>
@@ -1076,13 +1077,18 @@ private:
}
const u64 gpu_address = request.gpu_address;
const u64 size = request.size;
+ if (size > std::numeric_limits<u64>::max() - gpu_address) {
+ LOG_WARNING(Service, "nx2:gpu 216 [{:#x}, +{:#x}] wraps the address space", gpu_address,
+ size);
+ return;
+ }
auto& registry = Nvidia::Devices::Nx2GpuMemoryRegistry::Get();
auto it = m_sparse_regions.upper_bound(gpu_address);
SparseRegion* region = nullptr;
u64 start = 0;
if (it != m_sparse_regions.begin()) {
--it;
- if (gpu_address + size <= it->first + it->second.size) {
+ if (size <= it->second.size && gpu_address - it->first <= it->second.size - size) {
start = it->first;
region = &it->second;
}
@@ -1145,7 +1151,7 @@ private:
return std::nullopt;
}
--it;
- if (gpu_address + size > it->first + it->second) {
+ if (size > it->second || gpu_address - it->first > it->second - size) {
return std::nullopt;
}
return it->first;
@@ -1240,7 +1246,7 @@ private:
return std::nullopt;
}
--it;
- if (gpu_address + size > it->first + it->second.size) {
+ if (size > it->second.size || gpu_address - it->first > it->second.size - size) {
return std::nullopt;
}
return it->second.cpu_address + (gpu_address - it->first);