Skip to content

[RANDO]: Stop spawn overrides from pretending to be jiggies - #358

Closed
JeodC wants to merge 1 commit into
HarbourMasters:develop-splitrockfrom
JeodC:fix-jiggyactor
Closed

[RANDO]: Stop spawn overrides from pretending to be jiggies#358
JeodC wants to merge 1 commit into
HarbourMasters:develop-splitrockfrom
JeodC:fix-jiggyactor

Conversation

@JeodC

@JeodC JeodC commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Seen in TTC Lobby after hitting the witch switch for the cannon jiggy and then transitioning to that map. In short, func_803333DC writes the jiggy id into the overridden actor's local and installs jiggy_free on it--because it's originally a jiggy. This in turn corrupts the overridden actor, who has its own code to run. It reuses local for its own state, so when jiggy_free runs, chjiggy_getJiggyId reads back garbage. That means jiggylist_list[garbage] happens, and we get an OOB indirect call and an OOB marker = 0 write. This corrupts the heap and crashes.

New VB: VB_OVERRIDE_JIGGY_ACTOR_LINK exists for the express purpose of early returning when the overridden spawn is not a jiggy.

@Caladius

Caladius commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

My concern here is why is this all of a sudden an issue? What changed after the Rando PR that causes this crash to occur in the first place?

@JeodC

JeodC commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

My concern here is why is this all of a sudden an issue? What changed after the Rando PR that causes this crash to occur in the first place?

Decomp pass 5 converted a lot of code out into structs for actors and updates. I do not think there is any regression, but a change in code behavior. I will see if I can confirm a specific trace for this instance.

@Caladius

Caladius commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

All good, just curiosity in case this is something that we should have caught at some point during Rando development haha

@JeodC

JeodC commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

If during your testing whatever was shot out of the cannon happened to be a jiggy, it would not go through this crash path. That's a possibility too isn't it?

@Caladius

Caladius commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

In true Rando testing fashion, we shot EVERYTHING out of that cannon. It's oddly satisfying

@JeodC

JeodC commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Well, I'll have to walk back my original theory for why this happened. It's true that func_803333DC stamps the jiggy id into the spawned actor's local and installs jiggy_free on its marker. jiggy_free then reads that id back as a jiggylist_list index, with no bounds check and with the if (bk_free) guard body left empty so the call happens unconditionally. If the substituted actor reused local for its own state, the read-back would be garbage and the index arbitrary.

Problem is, the substituted note doesn't touch local. SetCustomActorParametersEX only writes it for honeycomb/jiggy/token items; a note falls through to default. musicNote_update only sets scale. And NoteRetention's NoteLocal write is gated behind bundleInfo->actor_id == ACTOR_51_MUSIC_NOTE, while BUNDLE_10__JIGGY is row 16 of gBundle_array, so that guard rejects it. The stamped id survives intact, jiggy_free derives index 53, which is in bounds, and that slot's bk_free is the empty func_80347C70. There's nothing out of bounds on the path.

Will keep digging.

@JeodC JeodC closed this Aug 2, 2026
@JeodC
JeodC deleted the fix-jiggyactor branch August 2, 2026 23:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants