DynamoRIO / DynamoRIO/dynamorio

dr_insert_clean_call() mangles interworking calls to A32 functions

Open
#2,889 4 comments 0 reactions 0 assignees View on GitHub
OpSys-ARM
Dominant language
C
Stars
3.2k
Forks
629
Avg merge
2d 15h
Merged PRs (30d)
31

Description

Let's assume that we have a DR plugin with which we wish to insert calls to a statically compiled A32 function using `dr_insert_clean_call()`. This function lies outside the code cache-reachable region, therefore `dr_insert_clean_call()` ends up calling `insert_reachable_cti()`. Here, code is generated to set the pointer to the function:

insert_mov_immed_ptrsz(dcontext, (ptr_int_t)
PC_AS_JMP_TGT(dr_get_isa_mode(dcontext), target),
opnd_create_reg(scratch), ilist, where, NULL, NULL);

where, `PC_AS_JMP_TGT()` is defined as:

# define PC_AS_JMP_TGT(isa_mode, pc) \
((isa_mode) == DR_ISA_ARM_THUMB ? (app_pc)(((ptr_uint_t)pc) | 1) : pc)

The problem occurs when 1) instrumenting T32 code, so `isa_mode == DR_ISA_ARM_THUMB` and 2) the called function is compiled in A32. In this case, the LSB is incorrectly set and the interworking branch will switch to the T32 mode when executing the following BLX instruction. Selecting the execution mode for the callee based on the execution mode of the caller seems fundametally flawed to me, because the two instructions sets of AArch32 are interworking and there's no expectation that the callee and caller are compiled in the same mode. As a note, the reverse, inserting a call to a T32 function from A32 code, should work correctly, because pointers to T32-compiled functions already have the LSB bit set before `dr_insert_clean_call()` is even called.

I don't know how `insert_reachable_cti()` is used throughout the codebase, but in this particular case it seems like it should just leave the pointer untouched.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.