void test_external_jump_target_replacement(void) { uint8_t *load_area = allocate_code_space(1); /* BUF_SIZE * 2 because this function necessarily has an extra bundle. */ uint8_t buf[BUF_SIZE * 2]; int rc; int (*func)(void); const int kNaClBundleSize = NACL_BUNDLE_SIZE; copy_and_pad_fragment(buf, sizeof(buf), &template_func_external_jump_target, &template_func_external_jump_target_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); copy_and_pad_fragment(buf, sizeof(buf), &template_func_external_jump_target_replace, &template_func_external_jump_target_replace_end); /* Only copy one bundle so we can test an unaligned external jump target */ rc = nacl_dyncode_modify(load_area, buf, kNaClBundleSize); assert(rc == 0); func = (int (*)(void)) (uintptr_t) load_area; rc = func(); assert(rc == MARKER_NEW); }
/* Check that we can dynamically rewrite code. */ void test_replacing_code_unaligned(void) { uint8_t *load_area = allocate_code_space(1); uint8_t buf[BUF_SIZE]; int first_diff = 0; 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, unaligned */ copy_and_pad_fragment(buf, sizeof(buf), &template_func_replacement, &template_func_replacement_end); /* we find first byte where old and new code differs */ while (buf[first_diff] == load_area[first_diff] && first_diff < sizeof buf) { first_diff++; } /* and check, that there is some data in common, and some different */ assert(first_diff > 0 && first_diff < sizeof(buf)); rc = nacl_dyncode_modify(load_area+first_diff, buf+first_diff, sizeof(buf)-first_diff); assert(rc == 0); func = (int (*)(void)) (uintptr_t) load_area; rc = func(); assert(rc == MARKER_NEW); }
/* Check code replacement constraints */ void test_illegal_code_replacment(void) { uint8_t *load_area = allocate_code_space(1); uint8_t buf[BUF_SIZE]; int rc; int i; 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); for (i = 0; i < (sizeof(illegal_code_sections) / sizeof(struct code_section)); i++) { printf("\t%s\n", illegal_code_sections[i].name); /* write illegal replacement to the same location */ copy_and_pad_fragment(buf, sizeof(buf), illegal_code_sections[i].start, illegal_code_sections[i].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_OLD); } }
/* 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; }
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_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); }
void test_fail_on_validation_error() { void *load_area = allocate_code_space(1); uint8_t buf[32]; 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); }
void test_jump_into_super_inst_create(void) { uint8_t *load_area = allocate_code_space(1); uint8_t buf[BUF_SIZE]; int rc; /* A direct jump into a bundle is invalid. */ copy_and_pad_fragment(buf, sizeof(buf), &jump_into_super_inst_modified, &jump_into_super_inst_modified_end); rc = nacl_dyncode_create(load_area, buf, sizeof(buf)); assert(rc != 0); assert(errno == EINVAL); }
/* 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); }
/* 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); }
/* 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); }
/* Check that we can load code at the very beginning of the dynamic section. */ void test_loading_code_on_first_dynamic_page() { const unsigned int kPageMask = 0xFFFF; void *load_area = (void*)((uintptr_t)(etext + kPageMask) & ~kPageMask); 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); } }