| /* ********************************************************** |
| * Copyright (c) 2013-2014 Google, Inc. All rights reserved. |
| * Copyright (c) 2007-2010 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| #define _GNU_SOURCE /* MREMAP_MAYMOVE */ |
| |
| #include "dr_api.h" |
| #include "client_tools.h" |
| #ifdef UNIX |
| # include <sys/personality.h> |
| # include <sys/mman.h> |
| #endif |
| #include <limits.h> |
| |
| char *global; |
| #define SIZE 10 |
| #define VAL 17 |
| |
| static app_pc app_gencode; |
| |
| /* i#262 add exec if READ_IMPLIES_EXEC is set in personality */ |
| static bool add_exec = false; |
| |
| static client_id_t client_id; |
| |
| static |
| void write_array(char *array) |
| { |
| int i; |
| for (i=0; i<SIZE; i++) |
| array[i] = VAL; |
| } |
| |
| /* i#262 add exec if READ_IMPLIES_EXEC is set in personality */ |
| static uint |
| get_os_mem_prot(uint prot) |
| { |
| if (add_exec && TEST(DR_MEMPROT_READ, prot)) |
| return (prot | DR_MEMPROT_EXEC); |
| return prot; |
| } |
| |
| /* WARNING i#262: if you use the cmake binary package, ctest is built |
| * without a GNU_STACK section, which causes the linux kernel to set |
| * the READ_IMPLIES_EXEC personality flag, which is propagated to |
| * children and causes all mmaps to be +x, breaking all these tests |
| * that check for mmapped memory to be +rw or +r! |
| */ |
| static |
| void global_test(void) |
| { |
| char *array; |
| uint prot; |
| dr_fprintf(STDERR, " testing global memory alloc..."); |
| array = dr_global_alloc(SIZE); |
| write_array(array); |
| dr_query_memory((const byte *)array, NULL, NULL, &prot); |
| if (prot != get_os_mem_prot(DR_MEMPROT_READ|DR_MEMPROT_WRITE)) |
| dr_fprintf(STDERR, "[error: prot %d doesn't match rw] ", prot); |
| dr_global_free(array, SIZE); |
| dr_fprintf(STDERR, "success\n"); |
| } |
| |
| #ifdef X64 |
| # define PREFERRED_ADDR (char *)0x1000000000 |
| #else |
| # define PREFERRED_ADDR (char *)0x2000000 |
| #endif |
| |
| #ifdef X64 |
| static void |
| test_instr_as_immed(void) |
| { |
| void *drcontext = dr_get_current_drcontext(); |
| instrlist_t *ilist = instrlist_create(drcontext); |
| byte *pc; |
| instr_t *ins0, *ins1, *ins2; |
| opnd_t opnd; |
| byte *highmem = PREFERRED_ADDR; |
| pc = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| |
| DR_MEMPROT_EXEC, highmem); |
| ASSERT(pc == highmem); |
| |
| /* Test push_imm of instr */ |
| ins0 = INSTR_CREATE_nop(drcontext); |
| instrlist_append(ilist, ins0); |
| instrlist_insert_push_instr_addr(drcontext, ins0, highmem, |
| ilist, NULL, &ins1, &ins2); |
| ASSERT(ins2 != NULL); |
| instrlist_append(ilist, INSTR_CREATE_pop |
| (drcontext, opnd_create_reg(DR_REG_RAX))); |
| instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); |
| pc = instrlist_encode(drcontext, ilist, highmem, true); |
| instrlist_clear(drcontext, ilist); |
| ASSERT(pc < highmem + PAGE_SIZE); |
| pc = ((byte* (*)(void))highmem)(); |
| ASSERT(pc == highmem); |
| |
| /* Test mov_imm of instr */ |
| ins0 = INSTR_CREATE_nop(drcontext); |
| instrlist_append(ilist, ins0); |
| /* Beyond TOS, but a convenient mem dest */ |
| opnd = opnd_create_base_disp(DR_REG_RSP, DR_REG_NULL, 0, -8, OPSZ_8); |
| instrlist_insert_mov_instr_addr(drcontext, ins0, highmem, opnd, |
| ilist, NULL, &ins1, &ins2); |
| ASSERT(ins2 != NULL); |
| instrlist_append(ilist, INSTR_CREATE_mov_ld |
| (drcontext, opnd_create_reg(DR_REG_RAX), opnd)); |
| instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); |
| pc = instrlist_encode(drcontext, ilist, highmem, true); |
| instrlist_clear(drcontext, ilist); |
| ASSERT(pc < highmem + PAGE_SIZE); |
| pc = ((byte* (*)(void))highmem)(); |
| ASSERT(pc == highmem); |
| |
| instrlist_clear_and_destroy(drcontext, ilist); |
| dr_raw_mem_free(highmem, PAGE_SIZE); |
| } |
| |
| static void |
| reachability_test(void) |
| { |
| void *drcontext = dr_get_current_drcontext(); |
| instrlist_t *ilist = instrlist_create(drcontext); |
| byte *gencode = (byte *) |
| dr_nonheap_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE|DR_MEMPROT_EXEC); |
| byte *pc; |
| int res; |
| byte *highmem = PREFERRED_ADDR; |
| pc = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| |
| DR_MEMPROT_EXEC, highmem); |
| ASSERT(pc == highmem); |
| |
| dr_fprintf(STDERR, " reachability test..."); |
| |
| /* Test auto-magically turning rip-rel that won't reach but targets xax |
| * into absmem. |
| */ |
| instrlist_append(ilist, INSTR_CREATE_mov_ld |
| (drcontext, opnd_create_reg(DR_REG_EAX), |
| opnd_create_rel_addr(highmem, OPSZ_4))); |
| instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); |
| pc = instrlist_encode(drcontext, ilist, gencode, false); |
| instrlist_clear(drcontext, ilist); |
| ASSERT(pc < gencode + PAGE_SIZE); |
| *(int*)highmem = 0x12345678; |
| res = ((int (*)(void))gencode)(); |
| ASSERT(res == 0x12345678); |
| |
| /* Test auto-magically turning a reachable absmem into a rip-rel. */ |
| instrlist_append(ilist, INSTR_CREATE_mov_ld |
| (drcontext, opnd_create_reg(DR_REG_ECX), |
| opnd_create_abs_addr(highmem + 0x800, OPSZ_4))); |
| instrlist_append(ilist, INSTR_CREATE_mov_ld |
| (drcontext, opnd_create_reg(DR_REG_EAX), |
| opnd_create_reg(DR_REG_ECX))); |
| instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); |
| pc = instrlist_encode(drcontext, ilist, highmem, false); |
| instrlist_clear(drcontext, ilist); |
| ASSERT(pc < highmem + PAGE_SIZE); |
| *(int*)(highmem + 0x800) = 0x12345678; |
| res = ((int (*)(void))highmem)(); |
| ASSERT(res == 0x12345678); |
| |
| dr_raw_mem_free(highmem, PAGE_SIZE); |
| |
| /* Test targeting upper 2GB of low 4GB */ |
| highmem = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| |
| DR_MEMPROT_EXEC, (byte *)0xabcd0000); |
| instrlist_append(ilist, INSTR_CREATE_mov_ld |
| (drcontext, opnd_create_reg(DR_REG_ECX), |
| opnd_create_abs_addr(highmem, OPSZ_4))); |
| instrlist_append(ilist, INSTR_CREATE_mov_ld |
| (drcontext, opnd_create_reg(DR_REG_EAX), |
| opnd_create_reg(DR_REG_ECX))); |
| instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); |
| pc = instrlist_encode(drcontext, ilist, gencode, false); |
| instrlist_clear(drcontext, ilist); |
| ASSERT(pc < gencode + PAGE_SIZE); |
| *(int*)highmem = 0x12345678; |
| res = ((int (*)(void))gencode)(); |
| ASSERT(res == 0x12345678); |
| dr_raw_mem_free(highmem, PAGE_SIZE); |
| |
| /* Test targeting lower 2GB of low 4GB */ |
| highmem = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| |
| DR_MEMPROT_EXEC, (byte *)0x143d0000); |
| instrlist_append(ilist, INSTR_CREATE_mov_ld |
| (drcontext, opnd_create_reg(DR_REG_ECX), |
| opnd_create_abs_addr(highmem, OPSZ_4))); |
| instrlist_append(ilist, INSTR_CREATE_mov_ld |
| (drcontext, opnd_create_reg(DR_REG_EAX), |
| opnd_create_reg(DR_REG_ECX))); |
| instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); |
| pc = instrlist_encode(drcontext, ilist, gencode, false); |
| instrlist_clear(drcontext, ilist); |
| ASSERT(pc < gencode + PAGE_SIZE); |
| *(int*)highmem = 0x12345678; |
| res = ((int (*)(void))gencode)(); |
| ASSERT(res == 0x12345678); |
| dr_raw_mem_free(highmem, PAGE_SIZE); |
| |
| instrlist_clear_and_destroy(drcontext, ilist); |
| dr_nonheap_free(gencode, PAGE_SIZE); |
| |
| test_instr_as_immed(); |
| |
| dr_fprintf(STDERR, "success\n"); |
| } |
| #endif |
| |
| static |
| void raw_alloc_test(void) |
| { |
| uint prot; |
| char *array = PREFERRED_ADDR; |
| dr_mem_info_t info; |
| bool res; |
| dr_fprintf(STDERR, " testing raw memory alloc..."); |
| res = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ | DR_MEMPROT_WRITE, |
| array) != NULL; |
| if (!res) { |
| dr_fprintf(STDERR, "[error: fail to alloc at "PFX"]\n", array); |
| return; |
| } |
| write_array(array); |
| dr_query_memory((const byte *)array, NULL, NULL, &prot); |
| if (prot != get_os_mem_prot(DR_MEMPROT_READ|DR_MEMPROT_WRITE)) |
| dr_fprintf(STDERR, "[error: prot %d doesn't match rw]\n", prot); |
| dr_raw_mem_free(array, PAGE_SIZE); |
| dr_query_memory_ex((const byte *)array, &info); |
| if (info.prot != DR_MEMPROT_NONE) |
| dr_fprintf(STDERR, "[error: prot %d doesn't match none]\n", info.prot); |
| dr_fprintf(STDERR, "success\n"); |
| } |
| |
| static |
| void nonheap_test(void) |
| { |
| uint prot; |
| char *array = |
| dr_nonheap_alloc(SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE|DR_MEMPROT_EXEC); |
| dr_fprintf(STDERR, " testing nonheap memory alloc..."); |
| write_array(array); |
| dr_query_memory((const byte *)array, NULL, NULL, &prot); |
| if (prot != get_os_mem_prot((DR_MEMPROT_READ|DR_MEMPROT_WRITE|DR_MEMPROT_EXEC))) |
| dr_fprintf(STDERR, "[error: prot %d doesn't match rwx] ", prot); |
| dr_memory_protect(array, SIZE, DR_MEMPROT_NONE); |
| dr_query_memory((const byte *)array, NULL, NULL, &prot); |
| if (prot != get_os_mem_prot(DR_MEMPROT_NONE)) |
| dr_fprintf(STDERR, "[error: prot %d doesn't match none] ", prot); |
| dr_memory_protect(array, SIZE, DR_MEMPROT_READ); |
| dr_query_memory((const byte *)array, NULL, NULL, &prot); |
| if (prot != get_os_mem_prot(DR_MEMPROT_READ)) |
| dr_fprintf(STDERR, "[error: prot %d doesn't match r] ", prot); |
| if (dr_safe_write(array, 1, (const void *) &prot, NULL)) |
| dr_fprintf(STDERR, "[error: should not be writable] "); |
| dr_nonheap_free(array, SIZE); |
| dr_fprintf(STDERR, "success\n"); |
| } |
| |
| static bool |
| reachable_from_client(void *addr) |
| { |
| ssize_t diff = (byte *)addr - dr_get_client_base(client_id); |
| return (diff <= INT_MAX && diff >= INT_MIN); |
| } |
| |
| static |
| void custom_test(void) |
| { |
| void *drcontext = dr_get_current_drcontext(); |
| void *array; |
| size_t size; |
| uint prot; |
| |
| dr_fprintf(STDERR, " testing custom memory alloc...."); |
| |
| /* test global */ |
| array = dr_custom_alloc(NULL, 0, SIZE, 0, NULL); |
| write_array(array); |
| dr_custom_free(NULL, 0, array, SIZE); |
| |
| array = dr_custom_alloc(NULL, DR_ALLOC_CACHE_REACHABLE, SIZE, 0, NULL); |
| ASSERT(reachable_from_client(array)); |
| write_array(array); |
| dr_custom_free(NULL, DR_ALLOC_CACHE_REACHABLE, array, SIZE); |
| |
| /* test thread-local */ |
| array = dr_custom_alloc(drcontext, DR_ALLOC_THREAD_PRIVATE, SIZE, 0, NULL); |
| write_array(array); |
| dr_custom_free(drcontext, DR_ALLOC_THREAD_PRIVATE, array, SIZE); |
| |
| array = dr_custom_alloc(drcontext, DR_ALLOC_THREAD_PRIVATE|DR_ALLOC_CACHE_REACHABLE, |
| SIZE, 0, NULL); |
| ASSERT(reachable_from_client(array)); |
| write_array(array); |
| dr_custom_free(drcontext, DR_ALLOC_THREAD_PRIVATE|DR_ALLOC_CACHE_REACHABLE, |
| array, SIZE); |
| |
| /* test non-heap */ |
| array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP, PAGE_SIZE, |
| DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL); |
| write_array(array); |
| dr_custom_free(NULL, DR_ALLOC_NON_HEAP, array, PAGE_SIZE); |
| |
| array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_FIXED_LOCATION, PAGE_SIZE, |
| DR_MEMPROT_READ|DR_MEMPROT_WRITE, PREFERRED_ADDR); |
| ASSERT(array == (void *)PREFERRED_ADDR); |
| write_array(array); |
| dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_FIXED_LOCATION, array, PAGE_SIZE); |
| |
| array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_CACHE_REACHABLE, |
| PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL); |
| ASSERT(reachable_from_client(array)); |
| write_array(array); |
| dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_CACHE_REACHABLE, |
| array, PAGE_SIZE); |
| |
| array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_LOW_2GB, |
| PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL); |
| #ifdef X64 |
| ASSERT((ptr_uint_t)array < 0x80000000); |
| #endif |
| write_array(array); |
| dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_LOW_2GB, array, PAGE_SIZE); |
| |
| array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_NON_DR, |
| PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL); |
| write_array(array); |
| dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_NON_DR, |
| array, PAGE_SIZE); |
| |
| array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP, PAGE_SIZE, |
| DR_MEMPROT_READ|DR_MEMPROT_WRITE|DR_MEMPROT_EXEC, NULL); |
| ASSERT(dr_query_memory((byte *)array, NULL, &size, &prot) && |
| size == PAGE_SIZE && prot == (DR_MEMPROT_READ|DR_MEMPROT_WRITE| |
| DR_MEMPROT_EXEC)); |
| write_array(array); |
| dr_custom_free(NULL, DR_ALLOC_NON_HEAP, array, PAGE_SIZE); |
| |
| dr_fprintf(STDERR, "success\n"); |
| } |
| |
| #ifdef WINDOWS |
| static |
| void custom_windows_test(void) |
| { |
| void *array; |
| MEMORY_BASIC_INFORMATION mbi; |
| bool ok; |
| |
| dr_fprintf(STDERR, " testing custom windows alloc...."); |
| |
| array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP | DR_ALLOC_NON_DR | |
| DR_ALLOC_RESERVE_ONLY, PAGE_SIZE*2, |
| DR_MEMPROT_NONE, NULL); |
| if (array == NULL) |
| dr_fprintf(STDERR, "error: unable to reserve\n"); |
| if (dr_virtual_query(array, &mbi, sizeof(mbi)) != sizeof(mbi)) |
| dr_fprintf(STDERR, "error: unable to query prot\n"); |
| /* 0 is sometimes returned (see VirtualQuery docs) */ |
| if (mbi.Protect != PAGE_NOACCESS && mbi.Protect != 0) |
| dr_fprintf(STDERR, "error: wrong reserve prot %x\n", mbi.Protect); |
| if (mbi.State != MEM_RESERVE) |
| dr_fprintf(STDERR, "error: memory wasn't reserved\n"); |
| |
| array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP | DR_ALLOC_NON_DR | |
| DR_ALLOC_COMMIT_ONLY | DR_ALLOC_FIXED_LOCATION, |
| PAGE_SIZE, DR_MEMPROT_READ | DR_MEMPROT_WRITE, array); |
| if (array == NULL) |
| dr_fprintf(STDERR, "error: unable to commit\n"); |
| if (dr_virtual_query(array, &mbi, sizeof(mbi)) != sizeof(mbi)) |
| dr_fprintf(STDERR, "error: unable to query prot\n"); |
| if (mbi.Protect != PAGE_READWRITE) |
| dr_fprintf(STDERR, "error: wrong commit prot %x\n", mbi.Protect); |
| if (mbi.State != MEM_COMMIT || mbi.RegionSize != PAGE_SIZE) |
| dr_fprintf(STDERR, "error: memory wasn't committed\n"); |
| |
| write_array(array); |
| |
| ok = dr_custom_free(NULL, DR_ALLOC_NON_HEAP | DR_ALLOC_NON_DR | |
| DR_ALLOC_COMMIT_ONLY, array, PAGE_SIZE); |
| if (!ok) |
| dr_fprintf(STDERR, "error: failed to de-commit\n"); |
| if (dr_virtual_query(array, &mbi, sizeof(mbi)) != sizeof(mbi)) |
| dr_fprintf(STDERR, "error: unable to query prot\n"); |
| /* 0 is sometimes returned (see VirtualQuery docs) */ |
| if (mbi.Protect != PAGE_NOACCESS && mbi.Protect != 0) |
| dr_fprintf(STDERR, "error: wrong decommit prot %x\n", mbi.Protect); |
| if (mbi.State != MEM_RESERVE) |
| dr_fprintf(STDERR, "error: memory wasn't de-committed %x\n", mbi.State); |
| |
| ok = dr_custom_free(NULL, DR_ALLOC_NON_HEAP | DR_ALLOC_NON_DR | |
| DR_ALLOC_RESERVE_ONLY, array, PAGE_SIZE*2); |
| if (!ok) |
| dr_fprintf(STDERR, "error: failed to un-reserve\n"); |
| if (dr_virtual_query(array, &mbi, sizeof(mbi)) != sizeof(mbi)) |
| dr_fprintf(STDERR, "error: unable to query prot\n"); |
| /* 0 is sometimes returned (see VirtualQuery docs) */ |
| if (mbi.Protect != PAGE_NOACCESS && mbi.Protect != 0) |
| dr_fprintf(STDERR, "error: wrong unreserve prot %x\n", mbi.Protect); |
| if (mbi.State != MEM_FREE) |
| dr_fprintf(STDERR, "error: memory wasn't un-reserved\n"); |
| |
| dr_fprintf(STDERR, "success\n"); |
| } |
| #endif |
| |
| #ifdef UNIX |
| static |
| void custom_unix_test(void) |
| { |
| void *array; |
| bool ok; |
| |
| /* "linux" is replaced by "1" in .template so we use "Linux" */ |
| dr_fprintf(STDERR, " testing custom Linux alloc...."); |
| |
| array = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ | DR_MEMPROT_WRITE, NULL); |
| if (array == NULL) |
| dr_fprintf(STDERR, "error: unable to mmap\n"); |
| write_array(array); |
| |
| # ifdef LINUX |
| array = dr_raw_mremap(array, PAGE_SIZE, PAGE_SIZE*2, MREMAP_MAYMOVE, NULL); |
| if ((ptr_int_t)array <= 0 && (ptr_int_t)array >= -PAGE_SIZE) |
| dr_fprintf(STDERR, "error: unable to mremap\n"); |
| write_array(array); |
| # endif |
| |
| ok = dr_raw_mem_free(array, PAGE_SIZE*2); |
| if (!ok) |
| dr_fprintf(STDERR, "error: failed to munmap\n"); |
| |
| # ifdef LINUX |
| array = dr_raw_brk(0); |
| if (array == NULL) |
| dr_fprintf(STDERR, "error: unable to query brk\n"); |
| # endif |
| |
| dr_fprintf(STDERR, "success\n"); |
| } |
| #endif |
| |
| static |
| void memory_iteration_test(void) |
| { |
| dr_mem_info_t info; |
| byte *pc = NULL; |
| while (true) { |
| bool res = dr_query_memory_ex(pc, &info); |
| if (!res) { |
| ASSERT(info.type == DR_MEMTYPE_ERROR |
| IF_WINDOWS(|| info.type == DR_MEMTYPE_ERROR_WINKERNEL)); |
| if (info.type == DR_MEMTYPE_ERROR) |
| dr_fprintf(STDERR, "error: memory iteration failed\n"); |
| break; |
| } |
| ASSERT(info.type != DR_MEMTYPE_ERROR |
| IF_WINDOWS(&& info.type != DR_MEMTYPE_ERROR_WINKERNEL)); |
| if (POINTER_OVERFLOW_ON_ADD(pc, info.size)) |
| break; |
| pc += info.size; |
| } |
| } |
| |
| #ifdef UNIX |
| static void |
| calloc_test(void) |
| { |
| /* using the trigger from i#1115 */ |
| char *array = (char *) calloc(100000, 16); |
| if (array[100000*16 - 1] != 0) |
| dr_fprintf(STDERR, "error: calloc not zeroing\n"); |
| } |
| #endif |
| |
| static |
| void local_test(void *drcontext) |
| { |
| char *array; |
| dr_fprintf(STDERR, " testing local memory alloc...."); |
| array = dr_thread_alloc(drcontext, SIZE); |
| write_array(array); |
| dr_thread_free(drcontext, array, SIZE); |
| dr_fprintf(STDERR, "success\n"); |
| } |
| |
| static |
| void thread_init_event(void *drcontext) |
| { |
| static bool tested = false; |
| if (!tested) { |
| dr_fprintf(STDERR, "thread initialization:\n"); |
| local_test(drcontext); |
| global_test(); |
| tested = true; |
| } |
| } |
| |
| static |
| void inline_alloc_test(void) |
| { |
| dr_fprintf(STDERR, "code cache:\n"); |
| local_test(dr_get_current_drcontext()); |
| global_test(); |
| nonheap_test(); |
| raw_alloc_test(); |
| #ifdef UNIX |
| calloc_test(); |
| #endif |
| #ifdef X64 |
| reachability_test(); |
| #endif |
| } |
| |
| #define MINSERT instrlist_meta_preinsert |
| |
| static |
| dr_emit_flags_t bb_event(void* drcontext, void *tag, instrlist_t* bb, bool for_trace, bool translating) |
| { |
| static bool inserted = false; |
| if (!inserted) { |
| instr_t* instr = instrlist_first(bb); |
| |
| dr_prepare_for_call(drcontext, bb, instr); |
| |
| MINSERT(bb, instr, INSTR_CREATE_call |
| (drcontext, opnd_create_pc((void*)inline_alloc_test))); |
| |
| dr_cleanup_after_call(drcontext, bb, instr, 0); |
| |
| inserted = true; |
| } |
| |
| /* Match nop,nop,nop,ret in client's +w code */ |
| if (instr_get_opcode(instrlist_first(bb)) == OP_nop) { |
| instr_t *nxt = instr_get_next(instrlist_first(bb)); |
| if (nxt != NULL && instr_get_opcode(nxt) == OP_nop) { |
| nxt = instr_get_next(nxt); |
| if (nxt != NULL && instr_get_opcode(nxt) == OP_nop) { |
| nxt = instr_get_next(nxt); |
| if (nxt != NULL && instr_get_opcode(nxt) == OP_ret) { |
| uint prot; |
| dr_mem_info_t info; |
| app_pc pc = dr_fragment_app_pc(tag); |
| #ifdef WINDOWS |
| MEMORY_BASIC_INFORMATION mbi; |
| #endif |
| dr_fprintf(STDERR, " testing query pretend-write...."); |
| |
| if (!dr_query_memory(pc, NULL, NULL, &prot)) |
| dr_fprintf(STDERR, "error: unable to query code prot\n"); |
| if (!TESTALL(DR_MEMPROT_WRITE | DR_MEMPROT_PRETEND_WRITE, prot)) |
| dr_fprintf(STDERR, "error: not pretend-writable\n"); |
| |
| if (!dr_query_memory_ex(pc, &info)) |
| dr_fprintf(STDERR, "error: unable to query code prot\n"); |
| if (!TESTALL(DR_MEMPROT_WRITE | DR_MEMPROT_PRETEND_WRITE, info.prot)) |
| dr_fprintf(STDERR, "error: not pretend-writable\n"); |
| |
| #ifdef WINDOWS |
| if (dr_virtual_query(pc, &mbi, sizeof(mbi)) != sizeof(mbi)) |
| dr_fprintf(STDERR, "error: unable to query code prot\n"); |
| if (mbi.Protect != PAGE_EXECUTE_READWRITE) |
| dr_fprintf(STDERR, "error: not pretend-writable\n"); |
| #endif |
| |
| /* Store the pc so we can write to it once in a safe place, |
| * at the next syscall, to test i#143c#4. |
| */ |
| if (app_gencode == NULL) |
| app_gencode = pc; |
| |
| dr_fprintf(STDERR, "success\n"); |
| } |
| } |
| } |
| } |
| |
| /* store, since we're not deterministic */ |
| return DR_EMIT_STORE_TRANSLATIONS; |
| } |
| |
| /* i#143c#4: test DR handling a fault from a client writing to a |
| * pretend-writable page, which can only be done when nolinking and |
| * when holding no locks. Thus, we store the pc in the bb event above |
| * and wait for the next syscall. |
| */ |
| static bool |
| filter_syscall_event(void *drcontext, int sysnum) |
| { |
| return true; |
| } |
| |
| static bool |
| pre_syscall_event(void *drcontext, int sysnum) |
| { |
| if (app_gencode != NULL) { |
| *app_gencode = 90; |
| app_gencode = NULL; |
| dr_fprintf(STDERR, "wrote to app code page successfully\n"); |
| } |
| return true; |
| } |
| |
| DR_EXPORT |
| void dr_init(client_id_t id) |
| { |
| client_id = id; |
| |
| dr_fprintf(STDERR, "thank you for testing the client interface\n"); |
| |
| #ifdef UNIX |
| /* i#262: check if READ_IMPLIES_EXEC in personality. If true, |
| * we expect the readable memory also has exec right. |
| */ |
| int res = personality(0xffffffff); |
| if (res == -1) |
| fprintf(stderr, "Error: fail to get personality\n"); |
| else if (TEST(READ_IMPLIES_EXEC, res)) |
| add_exec = true; |
| #endif |
| |
| global_test(); |
| nonheap_test(); |
| custom_test(); |
| #ifdef WINDOWS |
| custom_windows_test(); |
| #else |
| custom_unix_test(); |
| #endif |
| memory_iteration_test(); |
| |
| dr_register_bb_event(bb_event); |
| dr_register_thread_init_event(thread_init_event); |
| dr_register_filter_syscall_event(filter_syscall_event); |
| dr_register_pre_syscall_event(pre_syscall_event); |
| } |