From d7c532d8894ce806c9af13b8dd3eec975642b348 Mon Sep 17 00:00:00 2001 From: comex Date: Sat, 1 Jul 2023 15:00:39 -0700 Subject: Fixes and workarounds to make UBSan happier on macOS There are still some other issues not addressed here, but it's a start. Workarounds for false-positive reports: - `RasterizerAccelerated`: Put a gigantic array behind a `unique_ptr`, because UBSan has a [hardcoded limit](https://stackoverflow.com/questions/64531383/c-runtime-error-using-fsanitize-undefined-object-has-a-possibly-invalid-vp) of how big it thinks objects can be, specifically when dealing with offset-to-top values used with multiple inheritance. Hopefully this doesn't have a performance impact. - `QueryCacheBase::QueryCacheBase`: Avoid an operation that UBSan thinks is UB even though it at least arguably isn't. See the link in the comment for more information. Fixes for correct reports: - `PageTable`, `Memory`: Use `uintptr_t` values instead of pointers to avoid UB from pointer overflow (when pointer arithmetic wraps around the address space). - `KScheduler::Reload`: `thread->GetOwnerProcess()` can be `nullptr`; avoid calling methods on it in this case. (The existing code returns a garbage reference to a field, which is then passed into `LoadWatchpointArray`, and apparently it's never used, so it's harmless in practice but still triggers UBSan.) - `KAutoObject::Close`: This function calls `this->Destroy()`, which overwrites the beginning of the object with junk (specifically a free list pointer). Then it calls `this->UnregisterWithKernel()`. UBSan complains about a type mismatch because the vtable has been overwritten, and I believe this is indeed UB. `UnregisterWithKernel` also loads `m_kernel` from the 'freed' object, which seems to be technically safe (the overwriting doesn't extend as far as that field), but seems dubious. Switch to a `static` method and load `m_kernel` in advance. --- src/core/memory.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'src/core/memory.cpp') diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 805963178..7538c1d23 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -73,7 +73,7 @@ struct Memory::Impl { return {}; } - return system.DeviceMemory().GetPointer(paddr) + vaddr; + return system.DeviceMemory().GetPointer(paddr + vaddr); } [[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const { @@ -84,7 +84,7 @@ struct Memory::Impl { return {}; } - return system.DeviceMemory().GetPointer(paddr) + vaddr; + return system.DeviceMemory().GetPointer(paddr + vaddr); } u8 Read8(const Common::ProcessAddress addr) { @@ -204,7 +204,8 @@ struct Memory::Impl { break; } case Common::PageType::Memory: { - u8* mem_ptr = pointer + page_offset + (page_index << YUZU_PAGEBITS); + u8* mem_ptr = + reinterpret_cast(pointer + page_offset + (page_index << YUZU_PAGEBITS)); on_memory(copy_amount, mem_ptr); break; } @@ -448,7 +449,7 @@ struct Memory::Impl { break; case Common::PageType::Memory: current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( - nullptr, Common::PageType::DebugMemory); + 0, Common::PageType::DebugMemory); break; default: UNREACHABLE(); @@ -466,7 +467,8 @@ struct Memory::Impl { case Common::PageType::DebugMemory: { u8* const pointer{GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK)}; current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( - pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory); + reinterpret_cast(pointer) - (vaddr & ~YUZU_PAGEMASK), + Common::PageType::Memory); break; } default: @@ -506,7 +508,7 @@ struct Memory::Impl { case Common::PageType::DebugMemory: case Common::PageType::Memory: current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( - nullptr, Common::PageType::RasterizerCachedMemory); + 0, Common::PageType::RasterizerCachedMemory); break; case Common::PageType::RasterizerCachedMemory: // There can be more than one GPU region mapped per CPU region, so it's common @@ -534,10 +536,11 @@ struct Memory::Impl { // pagetable after unmapping a VMA. In that case the underlying VMA will no // longer exist, and we should just leave the pagetable entry blank. current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( - nullptr, Common::PageType::Unmapped); + 0, Common::PageType::Unmapped); } else { current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store( - pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory); + reinterpret_cast(pointer) - (vaddr & ~YUZU_PAGEMASK), + Common::PageType::Memory); } break; } @@ -584,7 +587,7 @@ struct Memory::Impl { "Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE); while (base != end) { - page_table.pointers[base].Store(nullptr, type); + page_table.pointers[base].Store(0, type); page_table.backing_addr[base] = 0; page_table.blocks[base] = 0; base += 1; @@ -593,7 +596,8 @@ struct Memory::Impl { auto orig_base = base; while (base != end) { auto host_ptr = - system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS); + reinterpret_cast(system.DeviceMemory().GetPointer(target)) - + (base << YUZU_PAGEBITS); auto backing = GetInteger(target) - (base << YUZU_PAGEBITS); page_table.pointers[base].Store(host_ptr, type); page_table.backing_addr[base] = backing; @@ -619,8 +623,8 @@ struct Memory::Impl { // Avoid adding any extra logic to this fast-path block const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw(); - if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { - return &pointer[vaddr]; + if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) { + return reinterpret_cast(pointer + vaddr); } switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) { case Common::PageType::Unmapped: @@ -814,7 +818,7 @@ bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { return false; } const auto [pointer, type] = page_table.pointers[page].PointerType(); - return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory || + return pointer != 0 || type == Common::PageType::RasterizerCachedMemory || type == Common::PageType::DebugMemory; } -- cgit v1.2.3