stalker: Fix recompile error in Stalker when processing the same real_address block with different insn multiple times. - #995
Conversation
…_address block with different insn multiple times.
7787a26 to
bf204fe
Compare
oleavr
left a comment
There was a problem hiding this comment.
Thanks! Sorry it took me so long to get to this.
| { | ||
| const gint trust_threshold = ctx->stalker->trust_threshold; | ||
| gboolean still_up_to_date; | ||
| GumExecBlock *active_block = gum_exec_block_get_active_block(block); |
There was a problem hiding this comment.
This seems like it's dealing with a symptom instead of the root cause -- isn't the root cause that ctx->mappings should have been updated so it has the new block (storage_block)?
There was a problem hiding this comment.
This seems like it's dealing with a symptom instead of the root cause -- isn't the root cause that
ctx->mappingsshould have been updated so it has the new block (storage_block)?
Since my understanding of this project may not be very comprehensive, my previous commits made only minor modifications based on the original implementation approach — the fix works without updating ctx->mappings while maintaining the original design.
However, after hearing your explanation, I re-evaluated the code, and you are right. If we consider a more thorough modification, we could indeed try discarding the original block and updating ctx->mappings with the newly recompiled new_block, while also freeing the original block.
By doing so, ctx->scratch_slab and block->storage_block can be deprecated, making the recompilation logic clearer and more concise. Additionally, the following two cases in the original gum_exec_ctx_recompile_block implementation can be unified into a single approach (no need to use scratch to calculate and reuse block, generates a new block in any case):
- When the size of the recompiled
scratch_blockfits within the space of the originalblock, the space inscratch_slabis used as a draft to overwrite the originalblock. - When the original
blockspace is insufficient (the worst-case scenario requiring two compilations), abjump is written intoblock->code_startto redirect execution tostorage_block->code_start.
There was a problem hiding this comment.
(Sorry for the delay.) We cannot free/immediately re-cycle the original block, because other blocks may have been backpatched to jump directly to it. So that's why we leave it in place, but just overwrite its first instruction with a branch to the replacement block.
| GDestroyNotify data_destroy) | ||
| { | ||
| GumExecBlock * block = self->exec_block; | ||
| GumExecBlock * active_block = gum_exec_block_get_active_block(block); |
There was a problem hiding this comment.
This also seems like a symptom and not the correct fix. It seems the code generation is happening on the wrong block. Maybe it's just another symptom of failing to update the mappings?
Problem Resolution:
gum_exec_ctx_obtain_block_for, the instruction changes in the samereal_addresscould not trigger a recompile, resulting in erroneous reuse of the first block whenblock->storage_blockbranch.transform(), the generatedjs_callbackwas not destroyed, leading to an issue.Reproduction Scenario:
Test
gumstalker-arm64has been tested, andgumstalker-armwas fixed based on it but not tested.