Since #8172, a host import that stays in the write watch keeps each of
its watched runs in the watch instead of taking the whole import out
when part of it was unwatched. Grand Theft Auto V (PPSA04264) imports
resident direct memory that way, and later maps the same memory at a
second address, which turns it into shared views. From then on the write
watch arms them, and the game's own stores fault and are resumed, the
red zone hazard of #361 (#9136, eboot 0x162b9e4 and 0x28f413a).
ImportWatched now marks the resident pages of each run it keeps in the
watch with GuestArenaCompareResidentWhenShared. They stay write-watched
while they are private, as #8172 needs for resident DCC targets. Once
they are shared, their collects fail and they are compared, as before
#8172, instead of being armed. Memory that is already shared when it is
imported is not marked and stays tracked as before.
agc_rewatched_direct_shared gains the imported case: resident direct
memory imported with the watch kept, then mapped a second time, must
collect as 0 through either mapping, stay writable and show a store
through both, while unimported shared direct memory is still collected.
windows_write_watch gains CheckResidentOnlyComparedWhenShared: a
resident-only mark leaves a shared view armed and collected, and still
keeps a resident page compared once a second mapping makes it shared.
WindowsMappings keys its physical pages by the section handle's value. A
handle value that an earlier case closed without ForgetSection can come
back from CreateFileMappingW with that case's stored pages, and then the
first mapping is shared from the start instead of resident, so
CheckComparedWhenShared would fail its residency check. It passes today
only because the case before it forgets its section. The case now
forgets its own handle value before it maps.
On Windows a fault that AnyPS5 resumes in guest code can overwrite the
guest's red zone, [rsp-0x80, rsp-0x10) (#361). WindowsMappings arms every
alias of a shared page read-only when a clearing collect settles it, so
each guest store into that page faults and is resumed.
CompareWhenShared marks the pages of a range, and disarms any alias of
them that is already armed. Once a marked page is shared, collectable()
and the fresh pre-check refuse a clearing collect over it before anything
is armed, so its aliases are never armed and the caller compares the
range instead. While the page is still a resident run it is collected
through GetWriteWatch as before, which takes no fault.
windows_write_watch gains CheckComparedWhenShared: a marked resident
range stays collected, then a second mapping makes it shared, and from
then on its collects fail, neither mapping is armed and stores into it
take no fault, while the unmarked half of the same mapping is still
collected.
WindowsMappings keys each physical page's state (moved into the section,
saved bytes of an unmapped resident page) by the section handle value.
sceKernelReleaseDirectMemory frees the backing before it unmaps the views,
so ForgetSection ran while those views still existed and kept their slots.
After the views went, the slots stayed under a handle value that Windows
hands out again, and the next direct allocation whose section got that
value started with the old page state: its first mapping went to the
section instead of private memory, or received the old allocation's bytes.
Views now share ownership of their slot, ForgetSection drops every slot
of the section at once, and JIT shared memory forgets its section before
closing it too.