Skip to content

Commit

Permalink
i#1931: Android v5 pthread struct overflow
Browse files Browse the repository at this point in the history
Fixes a regression from i#1920's support for Android v6 by splitting the DR
tls base offsets for v5 versus v6.  To fix the offset I am putting in
indirection of the tls offs through a variable. This makes the assembly
more painful. I do not have a good way to test it in both directions,
unfortunately, without an Android 5 device.

Fixes #1931

Review-URL: https://codereview.appspot.com/303440043
  • Loading branch information
derekbruening committed Aug 28, 2016
1 parent 14ceca0 commit 959718e
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 47 deletions.
9 changes: 2 additions & 7 deletions core/unix/include/android_linker.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,7 @@ typedef struct _android_v5_pthread_internal_t {
/* The TLS register points here, to slot #0 (ANDROID_TLS_SLOT_SELF) */
void *tls[ANDROID_BIONIC_TLS_SLOTS];
int /* really pthread_key_t */ pthread_keys[ANDROID_PTHREAD_KEYS_TOT];
char dlerror_buffer[ANDROID_DLERROR_BUFFER_SIZE];
/* We use this to store the DR TLS base. We assume its distance from tls[]
* is the same for all versions.
*/
/* This is our added field. */
void *dr_tls_base;
} android_v5_pthread_internal_t;

Expand All @@ -112,9 +109,7 @@ typedef struct _android_v6_pthread_internal_t {
void *tls[ANDROID_BIONIC_TLS_SLOTS];
int /* really pthread_key_t */ pthread_keys[ANDROID_PTHREAD_KEYS_TOT];
char dlerror_buffer[ANDROID_DLERROR_BUFFER_SIZE];
/* We use this to store the DR TLS base. We assume its distance from tls[]
* is the same for all versions.
*/
/* This is our added field. */
void *dr_tls_base;
} android_v6_pthread_internal_t;

Expand Down
25 changes: 16 additions & 9 deletions core/unix/loader_android.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ static uint android_version = 6;

extern void *kernel_init_sp;

uint android_tls_base_offs;

static size_t
get_pthread_tls_offs(void)
{
Expand All @@ -62,8 +64,8 @@ get_pthread_tls_offs(void)
return offsetof(android_v6_pthread_internal_t, tls);
}

static void
get_android_version(void)
void
init_android_version(void)
{
# define VER_FILE "/system/build.prop"
# define VER_PROP "ro.build.version.release="
Expand All @@ -89,12 +91,19 @@ get_android_version(void)

/* We have to exactly duplicate the offset of key fields in Android's
* pthread_internal_t struct.
* We want the dr_tls_base_offset offset from the tls field constant.
*/
ASSERT(DR_TLS_BASE_OFFSET == offsetof(android_v5_pthread_internal_t, dr_tls_base)
- offsetof(android_v5_pthread_internal_t, tls) /* the self slot */);
ASSERT(DR_TLS_BASE_OFFSET == offsetof(android_v6_pthread_internal_t, dr_tls_base)
- offsetof(android_v6_pthread_internal_t, tls) /* the self slot */);
if (android_version <= 5) {
android_tls_base_offs = offsetof(android_v5_pthread_internal_t, dr_tls_base)
- offsetof(android_v5_pthread_internal_t, tls) /* the self slot */;
/* i#1931: ensure we do not cross onto a new page. */
ASSERT(PAGE_START(android_tls_base_offs) ==
PAGE_START(sizeof(android_v5_pthread_internal_t) - sizeof(void*)));
} else {
android_tls_base_offs = offsetof(android_v6_pthread_internal_t, dr_tls_base)
- offsetof(android_v6_pthread_internal_t, tls) /* the self slot */;
ASSERT(PAGE_START(android_tls_base_offs) ==
PAGE_START(sizeof(android_v6_pthread_internal_t) - sizeof(void*)));
}
}

static size_t
Expand All @@ -120,8 +129,6 @@ privload_tls_init(void *app_tls)
char **e;
pid_t tid;

get_android_version();

/* We have to duplicate the pthread setup that the Android loader does.
* We expect app_tls to be either NULL or garbage, as we have early injection.
*/
Expand Down
85 changes: 56 additions & 29 deletions core/unix/os.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,17 @@ struct compat_rlimit {
*/
#define MCXT_SYSCALL_RES(mc) ((mc)->IF_X86_ELSE(xax, r0))
#if defined(AARCH64)
# define ASM_R2 "x2"
# define ASM_R3 "x3"
# define READ_TP_TO_R3 \
# define READ_TP_TO_R3_DISP_IN_R2 \
"mrs "ASM_R3", tpidr_el0\n\t" \
"ldr "ASM_R3", ["ASM_R3", #"STRINGIFY(DR_TLS_BASE_OFFSET)"] \n\t"
"ldr "ASM_R3", ["ASM_R3", "ASM_R2"] \n\t"
#elif defined(ARM)
# define ASM_R2 "r2"
# define ASM_R3 "r3"
# define READ_TP_TO_R3 \
# define READ_TP_TO_R3_DISP_IN_R2 \
"mrc p15, 0, "ASM_R3", c13, c0, "STRINGIFY(USR_TLS_REG_OPCODE)" \n\t" \
"ldr "ASM_R3", ["ASM_R3", #"STRINGIFY(DR_TLS_BASE_OFFSET)"] \n\t"
"ldr "ASM_R3", ["ASM_R3", "ASM_R2"] \n\t"
#endif /* ARM */

/* Prototype for all functions in .init_array. */
Expand Down Expand Up @@ -869,6 +871,14 @@ os_init(void)
if (DYNAMO_OPTION(emulate_brk))
init_emulated_brk(NULL);
#endif

#ifdef ANDROID
/* This must be set up earlier than privload_tls_init, and must be set up
* for non-client-interface as well, as this initializes DR_TLS_BASE_OFFSET
* (i#1931).
*/
init_android_version();
#endif
}

/* called before any logfiles are opened */
Expand Down Expand Up @@ -1328,36 +1338,53 @@ os_timeout(int time_in_milliseconds)
asm("mov %"ASM_SEG":(%%"ASM_XAX"), %%"ASM_XAX : : : ASM_XAX); \
asm("mov %%"ASM_XAX", %0" : "=m"((var)) : : ASM_XAX);
#elif defined(AARCHXX)
# define WRITE_TLS_SLOT_IMM(imm, var) \
__asm__ __volatile__( \
READ_TP_TO_R3 \
"str %0, ["ASM_R3", %1] \n\t" \
: : "r" (var), "i" (imm) \
: "memory", ASM_R3);
# define READ_TLS_SLOT_IMM(imm, var) \
__asm__ __volatile__( \
READ_TP_TO_R3 \
"ldr %0, ["ASM_R3", %1] \n\t" \
: "=r" (var) \
: "i" (imm) \
: ASM_R3);
/* Android needs indirection through a global. The Android toolchain has
* trouble with relocations if we use a global directly in asm, so we convert to
* a local variable in these macros. We pay the cost of the extra instructions
* for Linux ARM to share the code.
*/
# define WRITE_TLS_SLOT_IMM(imm, var) do { \
uint _base_offs = DR_TLS_BASE_OFFSET; \
__asm__ __volatile__( \
"mov "ASM_R2", %0 \n\t" \
READ_TP_TO_R3_DISP_IN_R2 \
"str %1, ["ASM_R3", %2] \n\t" \
: : "r" (_base_offs), "r" (var), "i" (imm) \
: "memory", ASM_R2, ASM_R3); \
} while (0)
# define READ_TLS_SLOT_IMM(imm, var) do { \
uint _base_offs = DR_TLS_BASE_OFFSET; \
__asm__ __volatile__( \
"mov "ASM_R2", %1 \n\t" \
READ_TP_TO_R3_DISP_IN_R2 \
"ldr %0, ["ASM_R3", %2] \n\t" \
: "=r" (var) \
: "r" (_base_offs), "i" (imm) \
: ASM_R2, ASM_R3); \
} while (0)
# define WRITE_TLS_INT_SLOT_IMM WRITE_TLS_SLOT_IMM /* b/c 32-bit */
# define READ_TLS_INT_SLOT_IMM READ_TLS_SLOT_IMM /* b/c 32-bit */
# define WRITE_TLS_SLOT(offs, var) \
__asm__ __volatile__( \
READ_TP_TO_R3 \
"add "ASM_R3", "ASM_R3", %1 \n\t" \
"str %0, ["ASM_R3"] \n\t" \
: : "r" (var), "r" (offs) \
: "memory", ASM_R3);
# define READ_TLS_SLOT(offs, var) \
# define WRITE_TLS_SLOT(offs, var) do { \
uint _base_offs = DR_TLS_BASE_OFFSET; \
__asm__ __volatile__( \
"mov "ASM_R2", %0 \n\t" \
READ_TP_TO_R3_DISP_IN_R2 \
"add "ASM_R3", "ASM_R3", %2 \n\t" \
"str %1, ["ASM_R3"] \n\t" \
: : "r" (_base_offs), "r" (var), "r" (offs) \
: "memory", ASM_R2, ASM_R3); \
} while (0)
# define READ_TLS_SLOT(offs, var) do { \
uint _base_offs = DR_TLS_BASE_OFFSET; \
__asm__ __volatile__( \
READ_TP_TO_R3 \
"add "ASM_R3", "ASM_R3", %1 \n\t" \
"mov "ASM_R2", %1 \n\t" \
READ_TP_TO_R3_DISP_IN_R2 \
"add "ASM_R3", "ASM_R3", %2 \n\t" \
"ldr %0, ["ASM_R3"] \n\t" \
: "=r" (var) \
: "r" (offs) \
: ASM_R3);
: "r" (_base_offs), "r" (offs) \
: ASM_R2, ASM_R3); \
} while (0)
#endif /* X86/ARM */

/* FIXME: on X86, we assume that DR's thread register is not already in use by app */
Expand Down
8 changes: 6 additions & 2 deletions core/unix/os_exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,15 @@

#ifdef AARCHXX
# ifdef ANDROID
/* We have our own slot at the end of our instance of Android's pthread_internal_t */
/* We have our own slot at the end of our instance of Android's pthread_internal_t.
* However, its offset varies by Android version, requiring indirection through
* a variable.
*/
# ifdef AARCH64
# error NYI
# else
# define DR_TLS_BASE_OFFSET 1100
extern uint android_tls_base_offs;
# define DR_TLS_BASE_OFFSET android_tls_base_offs
# endif
# else
/* The TLS slot for DR's TLS base.
Expand Down
1 change: 1 addition & 0 deletions core/unix/os_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ void *privload_tls_init(void *app_tp);
void privload_tls_exit(void *dr_tp);
#ifdef ANDROID
bool get_kernel_args(int *argc OUT, char ***argv OUT, char ***envp OUT);
void init_android_version(void);
#endif

/* in nudgesig.c */
Expand Down

0 comments on commit 959718e

Please sign in to comment.