SPU reservations: Do not illegally dereference reservation data

This commit is contained in:
Eladash 2020-09-15 21:19:58 +03:00 committed by Ivan
parent a28ab0a408
commit 6dcd482dd0
4 changed files with 90 additions and 8 deletions

View file

@ -2599,10 +2599,14 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args)
if (raddr)
{
// Last check for event before we clear the reservation
if (raddr == addr || rtime != vm::reservation_acquire(raddr, 128) || !cmp_rdata(rdata, vm::_ref<spu_rdata_t>(raddr)))
if (raddr == addr)
{
set_events(SPU_EVENT_LR);
}
else
{
get_events(SPU_EVENT_LR);
}
}
if (!vm::check_addr(addr, 1, vm::page_writable))
@ -2974,7 +2978,7 @@ bool spu_thread::process_mfc_cmd()
if (raddr && raddr != addr)
{
// Last check for event before we replace the reservation with a new one
if (vm::reservation_acquire(raddr, 128) != rtime || !cmp_rdata(temp, vm::_ref<spu_rdata_t>(raddr)))
if (reservation_check(raddr, temp))
{
set_events(SPU_EVENT_LR);
}
@ -3146,6 +3150,28 @@ bool spu_thread::process_mfc_cmd()
ch_mfc_cmd.cmd, ch_mfc_cmd.lsa, ch_mfc_cmd.eal, ch_mfc_cmd.tag, ch_mfc_cmd.size);
}
bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data)
{
if (!addr)
{
// No reservation to be lost in the first place
return false;
}
if ((vm::reservation_acquire(addr, 128) & -128) != rtime)
{
return true;
}
// Ensure data is allocated (HACK: would raise LR event if not)
vm::range_lock<false>(range_lock, addr, 128);
const bool res = *range_lock && cmp_rdata(data, vm::_ref<decltype(rdata)>(addr));
range_lock->release(0);
return !res;
}
spu_thread::ch_events_t spu_thread::get_events(u32 mask_hint, bool waiting, bool reading)
{
if (auto mask1 = ch_events.load().mask; mask1 & ~SPU_EVENT_IMPLEMENTED)
@ -3157,10 +3183,13 @@ retry:
u32 collect = 0;
// Check reservation status and set SPU_EVENT_LR if lost
if (mask_hint & SPU_EVENT_LR && raddr && ((vm::reservation_acquire(raddr, sizeof(rdata)) & -128) != rtime || !cmp_rdata(rdata, vm::_ref<spu_rdata_t>(raddr))))
if (mask_hint & SPU_EVENT_LR)
{
collect |= SPU_EVENT_LR;
raddr = 0;
if (reservation_check(raddr, rdata))
{
collect |= SPU_EVENT_LR;
raddr = 0;
}
}
// SPU Decrementer Event on underflow (use the upper 32-bits to determine it)