From d83f1f8235e19565e157613181eb00516173d475 Mon Sep 17 00:00:00 2001 From: nmzik Date: Sun, 6 Sep 2026 11:08:05 +0400 Subject: [PATCH] memory: handle GPU faults at the mapped boundary Co-authored-by: CheesyPoofs346 --- .../renderer/cache/gpuResourceManager.cpp | 4 +- tests/ShaderRecompilerComputeTests.cpp | 46 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/graphics/host_gpu/renderer/cache/gpuResourceManager.cpp b/src/graphics/host_gpu/renderer/cache/gpuResourceManager.cpp index a65485d56..8674f258d 100644 --- a/src/graphics/host_gpu/renderer/cache/gpuResourceManager.cpp +++ b/src/graphics/host_gpu/renderer/cache/gpuResourceManager.cpp @@ -12,7 +12,9 @@ GpuResourceManager::GpuResourceManager(GraphicContext& graphics, CommandSchedule GpuResourceManager::~GpuResourceManager() = default; bool GpuResourceManager::HandleFault(PageFaultAccess access, uint64_t fault_vaddr) noexcept { - constexpr uint64_t fault_size = 8; + // The host reports the faulting byte, not the instruction's access width. Both caches + // resolve its page; guessing a width can cross the end of a valid guest mapping. + constexpr uint64_t fault_size = 1; if (!IsMapped(fault_vaddr, fault_size)) { return false; } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 0aa86f18d..d624b6150 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -7620,6 +7620,52 @@ public: m_device.destroyDescriptorSetLayout(observer_set_layout, nullptr); m_device.destroyShaderModule(ms_depth_module, nullptr); + constexpr uint64_t mapped_end = base + allocation_size; + constexpr uint64_t final_word_address = mapped_end - sizeof(uint32_t); + auto *final_word = reinterpret_cast(final_word_address); + auto *final_byte = reinterpret_cast(mapped_end - 1); + Require(name, "fault boundary containment", + resources.IsMapped(final_word_address, sizeof(uint32_t)) && + !resources.IsMapped(final_word_address, sizeof(uint64_t)) && + !resources.HandleFault(PageFaultAccess::Write, mapped_end) && + !resources.HandleFault(PageFaultAccess::Read, mapped_end), + "fault handling changed the mapped range's exclusive end"); + Require(name, "fault boundary setup", + resources.InvalidateMemory(final_word_address, sizeof(uint32_t)), + "the final mapped word could not be initialized"); + *final_word = 0; + auto final_word_desc = MakeLinearDesc( + final_word_address, sizeof(uint32_t), vk::Format::eR32Uint, + Prospero::BufferFormat::k32UInt, Prospero::ImageType::kColor2D, + {1, 1, 1}, 1, 4, 1); + const auto final_word_image = texture_cache.FindImage(final_word_desc); + (void)texture_cache.FindTexture(final_word_image, final_word_desc); + Require(name, "fault boundary image tracking", + texture_cache.GetImage(final_word_image).IsTracked() && + !texture_cache.GetImage(final_word_image).IsCpuDirty(), + "the final mapped word's image was not clean before its CPU store"); + *final_word = 45; + Require(name, "fault boundary CPU store", + *final_word == 45 && + texture_cache.GetImage(final_word_image).IsDefinitelyCpuDirty(), + "a valid four-byte store at the mapping end did not invalidate its image"); + + constexpr uint32_t final_gpu_value = 0xa1b2c3d4u; + auto &boundary_buffer_cache = resources.GetBufferCache(); + (void)boundary_buffer_cache.ObtainBuffer(final_word_address, sizeof(uint32_t), true); + boundary_buffer_cache.FillBuffer(final_word_address, sizeof(uint32_t), + final_gpu_value, false); + Require(name, "fault boundary GPU ownership", + boundary_buffer_cache.HasGpuDirtyBytes(final_word_address, sizeof(uint32_t)) && + !HostMemoryIsReadable(mapped_end - 1), + "the final mapped byte was not protected by GPU ownership"); + const uint8_t final_gpu_byte = *final_byte; + Require(name, "fault boundary GPU readback", + final_gpu_byte == static_cast(final_gpu_value >> 24u) && + *final_word == final_gpu_value && + !boundary_buffer_cache.HasGpuDirtyBytes(final_word_address, sizeof(uint32_t)), + "a final-byte CPU read did not publish the GPU-owned final word"); + resources.SetGpu(nullptr); resources.UnmapMemory(base, allocation_size); scheduler.Finish();