void test_branches_outside_chunk() { char *load_area = allocate_code_space(1); int rc; int size = &branch_forwards_end - &branch_forwards; assert(size == 16 || size == 32); assert(&branch_backwards_end - &branch_backwards == size); rc = nacl_load_code(load_area, &branch_forwards, size); assert(rc == 0); rc = nacl_load_code(load_area + size, &branch_backwards, size); assert(rc == 0); }
object make_procedure(object opbuffer, object name, long argc) { long op_size = BUFFER_LENGTH(opbuffer); object result; gc_tmp2 = name; gc_tmp3 = opbuffer; result = make_heap_object(PROCEDURE_TYPE, sizeof(struct proc_heap_structure)); PROC_OPS(result) = (long *)allocate_code_space(op_size); memcpy(PROC_OPS(result),BUFFER_DATA(gc_tmp3),op_size); PROC_MODULE(result) = gc_tmp2; PROC_ARGC(result) = argc; return result; }
/* Check that we can dynamically delete code. */ void test_deleting_code(void) { uint8_t *load_area = (uint8_t *) allocate_code_space(1); uint8_t buf[BUF_SIZE]; int rc; int (*func)(void); copy_and_pad_fragment(buf, sizeof(buf), &template_func, &template_func_end); rc = nacl_dyncode_create(load_area, buf, sizeof(buf)); assert(rc == 0); func = (int (*)(void)) (uintptr_t) load_area; rc = func(); assert(rc == MARKER_OLD); rc = dyncode_delete_with_retry(load_area, sizeof(buf)); assert(rc == 0); assert(load_area[0] != buf[0]); /* Attempting to unload the code again should fail. */ rc = nacl_dyncode_delete(load_area, sizeof(buf)); assert(rc == -1); assert(errno == EFAULT); /* * We should be able to load new code at the same address. This * assumes that no other threads are running, otherwise this request * can be rejected. * * This fails under ARM QEMU. QEMU will flush its instruction * translation cache based on writes to the same virtual address, * but it ignores our explicit cache flush system calls. Valgrind * has a similar problem, except that there is no cache flush system * call on x86. */ if (getenv("UNDER_QEMU_ARM") != NULL || getenv("RUNNING_ON_VALGRIND") != NULL) { printf("Skipping loading new code under emulator\n"); } else { printf("Testing loading new code...\n"); copy_and_pad_fragment(buf, sizeof(buf), &template_func_replacement, &template_func_replacement_end); rc = nacl_dyncode_create(load_area, buf, sizeof(buf)); assert(rc == 0); func = (int (*)(void)) (uintptr_t) load_area; rc = func(); assert(rc == MARKER_NEW); rc = nacl_dyncode_delete(load_area, sizeof(buf)); assert(rc == 0); assert(load_area[0] != buf[0]); } }
int test_simle_replacement(const char *fragment1, const char *fragment1_end, const char *fragment2, const char *fragment2_end) { uint8_t *load_area = allocate_code_space(1); uint8_t buf[BUF_SIZE]; int rc; /* The original version is fine. */ copy_and_pad_fragment(buf, sizeof(buf), fragment1, fragment1_end); rc = nacl_dyncode_create(load_area, buf, sizeof(buf)); assert(rc == 0); copy_and_pad_fragment(buf, sizeof(buf), fragment2, fragment2_end); rc = nacl_dyncode_modify(load_area, buf, sizeof(buf)); return rc; }
/* The syscall may have to mmap() shared memory temporarily, so there is some interaction with page size. Check that we can load to non-page-aligned addresses. */ void test_loading_code_non_page_aligned() { char *load_area = allocate_code_space(1); uint8_t buf[32]; int rc; copy_and_pad_fragment(buf, sizeof(buf), &template_func, &template_func_end); rc = nacl_load_code(load_area, buf, sizeof(buf)); assert(rc == 0); assert(memcmp(load_area, buf, sizeof(buf)) == 0); load_area += 32; rc = nacl_load_code(load_area, buf, sizeof(buf)); assert(rc == 0); assert(memcmp(load_area, buf, sizeof(buf)) == 0); }
void test_fail_on_overwrite() { void *load_area = allocate_code_space(1); uint8_t buf[32]; int rc; copy_and_pad_fragment(buf, sizeof(buf), &template_func, &template_func_end); rc = nacl_load_code(load_area, buf, sizeof(buf)); assert(rc == 0); copy_and_pad_fragment(buf, sizeof(buf), &template_func, &template_func_end); rc = nacl_load_code(load_area, buf, sizeof(buf)); assert(rc == -EINVAL); }
void test_hlt_filled_bundle() { uint8_t bad_code[NUM_BUNDLES_FOR_HLT * NACL_BUNDLE_SIZE]; void *load_area; int ix; for (ix = 0; ix < NUM_BUNDLES_FOR_HLT; ++ix) { fill_nops(bad_code, sizeof bad_code); fill_hlts(bad_code + ix * NACL_BUNDLE_SIZE, NACL_BUNDLE_SIZE); load_area = allocate_code_space(1); /* hlts are now allowed */ assert(0 == nacl_load_code(load_area, bad_code, sizeof bad_code)); /* but not twice... */ assert(0 != nacl_load_code(load_area, bad_code, sizeof bad_code)); } }
/* * Check that regions surrounding the region we load code into are * correctly filled with halt instructions. Loading code causes the * pages to become allocated, and unused parts of these pages should * be filled with halts. */ void test_demand_alloc_surrounding_hlt_filling(void) { int pad_size = 0x4000; /* This must be less than one 64k page. */ int code_size = 0x28000; int total_size = pad_size * 2 + code_size; assert(total_size % DYNAMIC_CODE_PAGE_SIZE == 0); char *load_area = allocate_code_space(total_size / DYNAMIC_CODE_PAGE_SIZE); uint8_t *data = alloca(code_size); int rc; fill_nops(data, code_size); rc = nacl_load_code(load_area + pad_size, data, code_size); assert(rc == 0); check_region_is_filled_with_hlts(load_area, pad_size); assert(memcmp(load_area + pad_size, data, code_size) == 0); check_region_is_filled_with_hlts(load_area + pad_size + code_size, pad_size); }
void test_validation_error_does_not_leak(void) { void *load_area = allocate_code_space(1); uint8_t buf[BUF_SIZE]; int rc; copy_and_pad_fragment(buf, sizeof(buf), &invalid_code, &invalid_code_end); rc = nacl_load_code(load_area, buf, sizeof(buf)); assert(rc == -EINVAL); /* * Make sure that the failed validation didn't claim the memory. * See: http://code.google.com/p/nativeclient/issues/detail?id=2566 */ copy_and_pad_fragment(buf, sizeof(buf), &template_func, &template_func_end); rc = nacl_load_code(load_area, buf, sizeof(buf)); assert(rc == 0); }
/* Allowing mmap() to overwrite the dynamic code area would be unsafe. */ void test_fail_on_mmap_to_dyncode_area() { void *addr = allocate_code_space(1); size_t page_size = 0x10000; void *result; int rc; assert((uintptr_t) addr % page_size == 0); result = mmap(addr, page_size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); assert(result == MAP_FAILED); assert(errno == EINVAL); rc = munmap(addr, page_size); assert(rc == -1); assert(errno == EINVAL); /* TODO(mseaborn): Test mprotect() once NaCl provides it. */ }
/* Check that we can load and run code. */ void test_loading_code() { void *load_area = allocate_code_space(1); uint8_t buf[32]; int rc; int (*func)(); copy_and_pad_fragment(buf, sizeof(buf), &template_func, &template_func_end); rc = nacl_load_code(load_area, buf, sizeof(buf)); assert(rc == 0); assert(memcmp(load_area, buf, sizeof(buf)) == 0); /* Need double cast otherwise gcc complains with "ISO C forbids conversion of object pointer to function pointer type [-pedantic]". */ func = (int (*)()) (uintptr_t) load_area; rc = func(); assert(rc == 1234); }
/* * This is mostly the same as test_loading_code() except that we * repeat the test many times within the same page. Unlike the other * tests, this will consistently fail on ARM if we do not flush the * instruction cache, so it reproduces the bug * http://code.google.com/p/nativeclient/issues/detail?id=699 */ void test_stress(void) { void *load_area = allocate_code_space(1); uint8_t *dest; uint8_t *dest_max; uint8_t buf[BUF_SIZE]; copy_and_pad_fragment(buf, sizeof(buf), &template_func, &template_func_end); dest_max = (uint8_t *) load_area + DYNAMIC_CODE_PAGE_SIZE; for (dest = load_area; dest < dest_max; dest += sizeof(buf)) { int (*func)(void); int rc; rc = nacl_load_code(dest, buf, sizeof(buf)); assert(rc == 0); func = (int (*)(void)) (uintptr_t) dest; rc = func(); assert(rc == MARKER_OLD); } }
/* Check that we can dynamically rewrite code. */ void test_replacing_code(void) { uint8_t *load_area = allocate_code_space(1); uint8_t buf[BUF_SIZE]; int rc; int (*func)(void); copy_and_pad_fragment(buf, sizeof(buf), &template_func, &template_func_end); rc = nacl_dyncode_create(load_area, buf, sizeof(buf)); assert(rc == 0); func = (int (*)(void)) (uintptr_t) load_area; rc = func(); assert(rc == MARKER_OLD); /* write replacement to the same location */ copy_and_pad_fragment(buf, sizeof(buf), &template_func_replacement, &template_func_replacement_end); rc = nacl_dyncode_modify(load_area, buf, sizeof(buf)); assert(rc == 0); func = (int (*)(void)) (uintptr_t) load_area; rc = func(); assert(rc == MARKER_NEW); }
/* * Check that dyncode_create() works on a set of pages when a strict * subset of those pages were allocated by a previous dyncode_create() * call. This provides some coverage of the coalescing of mprotect() * calls that dyncode_create() does. */ void test_demand_alloc_of_fragmented_pages(void) { int smaller_size = 2 * DYNAMIC_CODE_PAGE_SIZE; int smaller_size_load_offset = 2 * DYNAMIC_CODE_PAGE_SIZE; int larger_size = 6 * DYNAMIC_CODE_PAGE_SIZE; char *load_area = allocate_code_space(6); uint8_t *data = alloca(larger_size); int rc; fill_nops(data, larger_size); /* Cause pages 2 and 3 to be allocated. */ rc = nacl_load_code(load_area + smaller_size_load_offset, data, smaller_size); assert(rc == 0); rc = dyncode_delete_with_retry(load_area + smaller_size_load_offset, smaller_size); assert(rc == 0); /* Cause pages 0, 1, 4 and 5 to be allocated as well. */ rc = nacl_load_code(load_area, data, larger_size); assert(rc == 0); }
void test_deleting_code_from_invalid_ranges(void) { uint8_t *load_addr = (uint8_t *) allocate_code_space(1) + 32; uint8_t buf[64]; int rc; /* We specifically want to test using multiple instruction bundles. */ assert(sizeof(buf) / NACL_BUNDLE_SIZE >= 2); assert(sizeof(buf) % NACL_BUNDLE_SIZE == 0); rc = dyncode_delete_with_retry(load_addr, sizeof(buf)); assert(rc == -1); assert(errno == EFAULT); fill_hlts(buf, sizeof(buf)); rc = nacl_dyncode_create(load_addr, buf, sizeof(buf)); assert(rc == 0); /* Overlapping before. */ rc = nacl_dyncode_delete(load_addr - NACL_BUNDLE_SIZE, sizeof(buf) + NACL_BUNDLE_SIZE); assert(rc == -1); assert(errno == EFAULT); /* Overlapping after. */ rc = nacl_dyncode_delete(load_addr, sizeof(buf) + NACL_BUNDLE_SIZE); assert(rc == -1); assert(errno == EFAULT); /* Missing the end of the loaded chunk. */ rc = nacl_dyncode_delete(load_addr, sizeof(buf) - NACL_BUNDLE_SIZE); assert(rc == -1); assert(errno == EFAULT); /* Missing the start of the loaded chunk. */ rc = nacl_dyncode_delete(load_addr + NACL_BUNDLE_SIZE, sizeof(buf) - NACL_BUNDLE_SIZE); assert(rc == -1); assert(errno == EFAULT); /* The correct range should work, though. */ rc = nacl_dyncode_delete(load_addr, sizeof(buf)); assert(rc == 0); }
void test_fail_on_non_bundle_aligned_dest_addresses() { char *load_area = allocate_code_space(1); int rc; uint8_t nops[32]; fill_nops(nops, sizeof(nops)); /* Test unaligned destination. */ rc = nacl_load_code(load_area + 1, nops, 32); assert(rc == -EINVAL); rc = nacl_load_code(load_area + 4, nops, 32); assert(rc == -EINVAL); /* Test unaligned size. */ rc = nacl_load_code(load_area, nops + 1, 31); assert(rc == -EINVAL); rc = nacl_load_code(load_area, nops + 4, 28); assert(rc == -EINVAL); /* Check that the code we're trying works otherwise. */ rc = nacl_load_code(load_area, nops, 32); assert(rc == 0); }
int is_replacement_enabled(void) { char trash; return (0 == nacl_dyncode_modify(allocate_code_space(1), &trash, 0)); }
/* nacl_dyncode_delete() succeeds trivially on the empty range. */ void test_deleting_zero_size(void) { uint8_t *load_addr = (uint8_t *) allocate_code_space(1); int rc = nacl_dyncode_delete(load_addr, 0); assert(rc == 0); }
void test_loading_zero_size() { char *load_area = allocate_code_space(1); int rc = nacl_load_code(load_area, &template_func, 0); assert(rc == 0); }