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);
}
Exemple #2
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);
}