Ensure that threads are ready before their preemption timer can be

armed.

It was discovered during testing of 'Hatsune Miku Project DIVA: Mega Mix'
that if a thread was starting while preemption was being enabled a NULL
pointer dereference could occur in the timer_settime call as
timer_create may not have been called yet.
This commit is contained in:
Billy Laws 2021-05-30 18:15:04 +01:00
parent a7dc69223b
commit af5ee96b9a

View File

@ -231,16 +231,25 @@ namespace skyline::kernel::type {
} }
void KThread::ArmPreemptionTimer(std::chrono::nanoseconds timeToFire) { void KThread::ArmPreemptionTimer(std::chrono::nanoseconds timeToFire) {
struct itimerspec spec{.it_value = { std::unique_lock lock(statusMutex);
.tv_nsec = std::min(timeToFire.count(), static_cast<long long>(constant::NsInSecond)), statusCondition.wait(lock, [this]() { return ready || killed; });
.tv_sec = std::max(std::chrono::duration_cast<std::chrono::seconds>(timeToFire).count() - 1, 0LL), if (!killed && running) {
}}; struct itimerspec spec{.it_value = {
timer_settime(preemptionTimer, 0, &spec, nullptr); .tv_nsec = std::min(timeToFire.count(), static_cast<long long>(constant::NsInSecond)),
isPreempted = true; .tv_sec = std::max(std::chrono::duration_cast<std::chrono::seconds>(timeToFire).count() - 1, 0LL),
}};
timer_settime(preemptionTimer, 0, &spec, nullptr);
isPreempted = true;
}
} }
void KThread::DisarmPreemptionTimer() { void KThread::DisarmPreemptionTimer() {
if (isPreempted) { if (!isPreempted)
return;
std::unique_lock lock(statusMutex);
statusCondition.wait(lock, [this]() { return ready || killed; });
if (!killed && running) {
struct itimerspec spec{}; struct itimerspec spec{};
timer_settime(preemptionTimer, 0, &spec, nullptr); timer_settime(preemptionTimer, 0, &spec, nullptr);
isPreempted = false; isPreempted = false;