diff options
| author | 2023-07-01 15:00:39 -0700 | |
|---|---|---|
| committer | 2023-07-15 12:00:28 -0700 | |
| commit | d7c532d8894ce806c9af13b8dd3eec975642b348 (patch) | |
| tree | 1353e6d1776384575019db28987e23237552a9de /src/core/hle/kernel | |
| parent | file_sys/content_archive: Detect compressed NCAs (#11047) (diff) | |
| download | yuzu-d7c532d8894ce806c9af13b8dd3eec975642b348.tar.gz yuzu-d7c532d8894ce806c9af13b8dd3eec975642b348.tar.xz yuzu-d7c532d8894ce806c9af13b8dd3eec975642b348.zip | |
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.
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/k_auto_object.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_auto_object.h | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_scheduler.cpp | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.cpp | 2 |
4 files changed, 8 insertions, 6 deletions
diff --git a/src/core/hle/kernel/k_auto_object.cpp b/src/core/hle/kernel/k_auto_object.cpp index 0ae42c95c..9cd7a9fd5 100644 --- a/src/core/hle/kernel/k_auto_object.cpp +++ b/src/core/hle/kernel/k_auto_object.cpp | |||
| @@ -15,8 +15,8 @@ void KAutoObject::RegisterWithKernel() { | |||
| 15 | m_kernel.RegisterKernelObject(this); | 15 | m_kernel.RegisterKernelObject(this); |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | void KAutoObject::UnregisterWithKernel() { | 18 | void KAutoObject::UnregisterWithKernel(KernelCore& kernel, KAutoObject* self) { |
| 19 | m_kernel.UnregisterKernelObject(this); | 19 | kernel.UnregisterKernelObject(self); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | } // namespace Kernel | 22 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index f384b1568..8d4e0df44 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h | |||
| @@ -159,14 +159,15 @@ public: | |||
| 159 | 159 | ||
| 160 | // If ref count hits zero, destroy the object. | 160 | // If ref count hits zero, destroy the object. |
| 161 | if (cur_ref_count - 1 == 0) { | 161 | if (cur_ref_count - 1 == 0) { |
| 162 | KernelCore& kernel = m_kernel; | ||
| 162 | this->Destroy(); | 163 | this->Destroy(); |
| 163 | this->UnregisterWithKernel(); | 164 | KAutoObject::UnregisterWithKernel(kernel, this); |
| 164 | } | 165 | } |
| 165 | } | 166 | } |
| 166 | 167 | ||
| 167 | private: | 168 | private: |
| 168 | void RegisterWithKernel(); | 169 | void RegisterWithKernel(); |
| 169 | void UnregisterWithKernel(); | 170 | static void UnregisterWithKernel(KernelCore& kernel, KAutoObject* self); |
| 170 | 171 | ||
| 171 | protected: | 172 | protected: |
| 172 | KernelCore& m_kernel; | 173 | KernelCore& m_kernel; |
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 75ce5a23c..d8143c650 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp | |||
| @@ -510,11 +510,12 @@ void KScheduler::Unload(KThread* thread) { | |||
| 510 | 510 | ||
| 511 | void KScheduler::Reload(KThread* thread) { | 511 | void KScheduler::Reload(KThread* thread) { |
| 512 | auto& cpu_core = m_kernel.System().ArmInterface(m_core_id); | 512 | auto& cpu_core = m_kernel.System().ArmInterface(m_core_id); |
| 513 | auto* process = thread->GetOwnerProcess(); | ||
| 513 | cpu_core.LoadContext(thread->GetContext32()); | 514 | cpu_core.LoadContext(thread->GetContext32()); |
| 514 | cpu_core.LoadContext(thread->GetContext64()); | 515 | cpu_core.LoadContext(thread->GetContext64()); |
| 515 | cpu_core.SetTlsAddress(GetInteger(thread->GetTlsAddress())); | 516 | cpu_core.SetTlsAddress(GetInteger(thread->GetTlsAddress())); |
| 516 | cpu_core.SetTPIDR_EL0(thread->GetTpidrEl0()); | 517 | cpu_core.SetTPIDR_EL0(thread->GetTpidrEl0()); |
| 517 | cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints()); | 518 | cpu_core.LoadWatchpointArray(process ? &process->GetWatchpoints() : nullptr); |
| 518 | cpu_core.ClearExclusiveState(); | 519 | cpu_core.ClearExclusiveState(); |
| 519 | } | 520 | } |
| 520 | 521 | ||
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index adb6ec581..2a105a762 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -129,7 +129,7 @@ Result KThread::Initialize(KThreadFunction func, uintptr_t arg, KProcessAddress | |||
| 129 | case ThreadType::User: | 129 | case ThreadType::User: |
| 130 | ASSERT(((owner == nullptr) || | 130 | ASSERT(((owner == nullptr) || |
| 131 | (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); | 131 | (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); |
| 132 | ASSERT(((owner == nullptr) || | 132 | ASSERT(((owner == nullptr) || (prio > Svc::LowestThreadPriority) || |
| 133 | (owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask())); | 133 | (owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask())); |
| 134 | break; | 134 | break; |
| 135 | case ThreadType::Kernel: | 135 | case ThreadType::Kernel: |