diff options
| author | Sssnowyyy <78863999+Sssnowyyy@users.noreply.github.com> | 2026-11-02 11:27:20 -0500 |
|---|---|---|
| committer | Sssnowyyy <78863999+Sssnowyyy@users.noreply.github.com> | 2026-11-02 14:53:47 -0500 |
| commit | e277b4ba61694f229b38ec0dc62f31c35d0cbff3 (patch) | |
| tree | 5a89d43ccd30da38ad8c3423344d091d0b3f7267 /src/core/hle | |
| parent | 089105ab44c87f1ef0526221144a1979c56558e6 (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.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/service/nx2_gsv.h | 12 |
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); |
