/* Set registers to known values and enter a NaCl syscall. */
static void SyscallRegisterSetterThread(struct SuspendTestShm *test_shm) {
  struct NaClSignalContext call_regs;
  char stack[0x10000];

  RegsFillTestValues(&call_regs, /* seed= */ 0);
  call_regs.stack_ptr = (uintptr_t) stack + sizeof(stack);
  call_regs.prog_ctr = (uintptr_t) ContinueAfterSyscall;
  RegsApplySandboxConstraints(&call_regs);

  /*
   * call_regs are the registers we set on entry to the syscall.
   * expected_regs are the registers that should be reported by
   * NaClAppThreadGetSuspendedRegisters().  Since not all registers
   * are saved when entering a syscall, expected_regs will be the same
   * as call_regs but with various registers zeroed out.
   */
  test_shm->expected_regs = call_regs;
  RegsUnsetNonCalleeSavedRegisters(&test_shm->expected_regs);

  uintptr_t syscall_addr = (uintptr_t) NACL_SYSCALL(test_syscall_1);
  if (!setjmp(return_jmp_buf)) {
#if defined(__i386__)
    test_shm->expected_regs.stack_ptr -= 4;  /* Account for argument */
    call_regs.eax = syscall_addr;
    call_regs.ecx = (uintptr_t) test_shm;  /* Scratch register */
    ASM_WITH_REGS(
        &call_regs,
        "push %%ecx\n"  /* Push syscall argument */
        "push $ContinueAfterSyscall\n"  /* Push return address */
        "nacljmp %%eax\n");
#elif defined(__x86_64__)
    call_regs.rax = syscall_addr;
    call_regs.rdi = (uintptr_t) test_shm;  /* Set syscall argument */
    ASM_WITH_REGS(
        &call_regs,
        "push $ContinueAfterSyscall\n"  /* Push return address */
        "nacljmp %%eax, %%r15\n");
#elif defined(__arm__)
    call_regs.r0 = (uintptr_t) test_shm;  /* Set syscall argument */
    call_regs.r1 = syscall_addr;  /* Scratch register */
    call_regs.lr = (uintptr_t) ContinueAfterSyscall;
    ASM_WITH_REGS(
        &call_regs,
        "bic r1, r1, #0xf000000f\n"
        "bx r1\n");
#elif defined(__mips__)
    call_regs.a0 = (uintptr_t) test_shm;  /* Set syscall argument */
    call_regs.t9 = syscall_addr;  /* Scratch register */
    call_regs.return_addr = (uintptr_t) ContinueAfterSyscall;
    ASM_WITH_REGS(
        &call_regs,
        "and $t9, $t9, $t6\n"
        "jr $t9\n"
        "nop\n");
#else
# error Unsupported architecture
#endif
    assert(!"Should not reach here");
  }
}
/* This tests a NaCl syscall that takes no arguments. */
void TestSyscall(uintptr_t syscall_addr) {
  struct NaClSignalContext call_regs;
  char stack[0x10000];

  RegsFillTestValues(&call_regs, /* seed= */ 0);
  call_regs.stack_ptr = (uintptr_t) stack + sizeof(stack);
  call_regs.prog_ctr = (uintptr_t) ContinueAfterSyscall;
  RegsApplySandboxConstraints(&call_regs);

  g_expected_regs = call_regs;
  RegsUnsetNonCalleeSavedRegisters(&g_expected_regs);
  SetNaClSwitchExpectations(&g_expected_regs);

  if (!setjmp(g_return_jmp_buf)) {
#if defined(__i386__)
    call_regs.eax = syscall_addr;
    ASM_WITH_REGS(
        &call_regs,
        "push $ContinueAfterSyscall\n"  /* Push return address */
        "nacljmp %%eax\n");
#elif defined(__x86_64__)
    /*
     * This fast path syscall happens to preserve various registers,
     * but that is obviously not guaranteed by the ABI.
     */
    if (syscall_addr == (uintptr_t) NACL_SYSCALL(tls_get) ||
        syscall_addr == (uintptr_t) NACL_SYSCALL(second_tls_get)) {
      /* Undo some effects of RegsUnsetNonCalleeSavedRegisters(). */
      g_expected_regs.rsi = call_regs.rsi;
      g_expected_regs.rdi = call_regs.rdi;
      g_expected_regs.r8 = call_regs.r8;
      g_expected_regs.r9 = call_regs.r9;
      g_expected_regs.r10 = call_regs.r10;
      /*
       * The current implementation clobbers %rcx with the
       * non-%r15-extended return address.
       */
      g_expected_regs.rcx = (uint32_t) g_expected_regs.prog_ctr;
    }

    call_regs.rax = syscall_addr;
    ASM_WITH_REGS(
        &call_regs,
        "push $ContinueAfterSyscall\n"  /* Push return address */
        "nacljmp %%eax, %%r15\n");
#elif defined(__arm__)
    call_regs.r1 = syscall_addr;  /* Scratch register */
    call_regs.lr = (uintptr_t) ContinueAfterSyscall;  /* Return address */
    ASM_WITH_REGS(
        &call_regs,
        "bic r1, r1, #0xf000000f\n"
        "bx r1\n");
#else
# error Unsupported architecture
#endif
    assert(!"Should not reach here");
  }
}
static void RegisterSetterThread(struct SuspendTestShm *test_shm) {
  struct NaClSignalContext *regs = &test_shm->expected_regs;
  char stack[0x10000];

  RegsFillTestValues(regs);
  regs->stack_ptr = (uintptr_t) stack + sizeof(stack);
  regs->prog_ctr = (uintptr_t) spin_instruction;
  RegsApplySandboxConstraints(regs);
  thread_test_shm = test_shm;

  /*
   * Set registers to known test values and then spin.  We do not
   * block by entering a NaCl syscall because that would disturb the
   * register state.
   */
  test_shm->continue_after_suspension_func =
      (uintptr_t) ContinueAfterSuspension;
  assert(offsetof(struct SuspendTestShm, var) == 0);
#if defined(__i386__)
  regs->eax = (uintptr_t) test_shm;
  ASM_WITH_REGS(
      regs,
      /* Align to ensure no NOPs are inserted in the code that follows. */
      ".p2align 5\n"
      /* Set "test_shm->var = test_shm" to indicate that we are ready. */
      "movl %%eax, (%%eax)\n"
      "spin_instruction:\n"
      "jmp spin_instruction\n");
#elif defined(__x86_64__)
  regs->rax = (uintptr_t) test_shm;
  ASM_WITH_REGS(
      regs,
      /* Align to ensure no NOPs are inserted in the code that follows. */
      ".p2align 5\n"
      /* Set "test_shm->var = test_shm" to indicate that we are ready. */
      "movl %%eax, %%nacl:(%%r15, %%rax)\n"
      "spin_instruction:\n"
      "jmp spin_instruction\n");
#elif defined(__arm__)
  regs->r0 = (uintptr_t) test_shm;
  ASM_WITH_REGS(
      regs,
      /* Align to ensure no NOPs are inserted in the code that follows. */
      ".p2align 4\n"
      /* Set "test_shm->var = test_shm" to indicate that we are ready. */
      "bic r0, r0, #0xc0000000\n"
      "str r0, [r0]\n"
      "spin_instruction:\n"
      "b spin_instruction\n");
#else
# error Unsupported architecture
#endif
  assert(!"Should not reach here");
}
int main(int argc, char **argv) {
  if (argc != 2) {
    fprintf(stderr, "Expected 1 argument: <memory-address>\n");
    return 1;
  }

  char *end;
  struct NaClSignalContext *expected_regs =
      (struct NaClSignalContext *) strtoul(argv[1], &end, 0);
  assert(*end == '\0');

  struct NaClSignalContext call_regs;
  char stack[0x10000];

  RegsFillTestValues(&call_regs);
  call_regs.stack_ptr = (uintptr_t) stack + sizeof(stack);
  call_regs.prog_ctr = (uintptr_t) SyscallReturnAddress;
  RegsApplySandboxConstraints(&call_regs);
  RegsUnsetNonCalleeSavedRegisters(&call_regs);

  uintptr_t syscall_addr = NACL_SYSCALL_ADDR(NACL_sys_test_syscall_1);
#if defined(__i386__)
  call_regs.esi = syscall_addr;
  *expected_regs = call_regs;
  ASM_WITH_REGS(
      &call_regs,
      "SyscallLoop:\n"
      "naclcall %%esi\n"
      "SyscallReturnAddress:\n"
      "jmp SyscallLoop\n");
#elif defined(__x86_64__)
  call_regs.r12 = syscall_addr;
  *expected_regs = call_regs;
  ASM_WITH_REGS(
      &call_regs,
      "SyscallLoop:\n"
      /* Call via a temporary register so as not to modify %r12. */
      "mov %%r12d, %%eax\n"
      "naclcall %%eax, %%r15\n"
      "SyscallReturnAddress:\n"
      "jmp SyscallLoop\n");
#else
# error Unsupported architecture
#endif
}
예제 #5
0
void test_stack_alignment(void) {
  char stack[0x1000];
  int offset;
  for (offset = 0; offset < 64; offset++) {
    RegsFillTestValues(&g_regs);
    g_regs.stack_ptr = (uintptr_t) stack + sizeof(stack) - offset;
    RegsApplySandboxConstraints(&g_regs);
    if (!setjmp(g_jmp_buf)) {
#if defined(__i386__) || defined(__x86_64__)
      ASM_WITH_REGS(&g_regs, "jmp CheckStackAlignmentEntry");
#elif defined(__arm__)
      ASM_WITH_REGS(&g_regs, "b CheckStackAlignmentEntry");
#else
# error Unsupported architecture
#endif
    }
  }
}
예제 #6
0
void set_registers_and_stop(void) {
  struct NaClSignalContext regs;
  memset(&regs, 0, sizeof(regs));

  /*
   * We set most registers to fixed values before faulting, so that we
   * can test that the debug stub successfully returns the same
   * values.
   */
#if defined(__i386__)
  regs.eax = 0x11000022;
  regs.ebx = 0x22000033;
  regs.ecx = 0x33000044;
  regs.edx = 0x44000055;
  regs.esi = 0x55000066;
  regs.edi = 0x66000077;
  regs.ebp = 0x77000088;
  regs.stack_ptr = 0x88000099;
  ASM_WITH_REGS(&regs, "jmp fault_addr\n");
#elif defined(__x86_64__)
  regs.rax = 0x1100000000000022;
  regs.rbx = 0x2200000000000033;
  regs.rcx = 0x3300000000000044;
  regs.rdx = 0x4400000000000055;
  regs.rsi = 0x5500000000000066;
  regs.rdi = 0x6600000000000077;
  regs.r8  = 0x7700000000000088;
  regs.r9  = 0x8800000000000099;
  regs.r10 = 0x99000000000000aa;
  regs.r11 = 0xaa000000000000bb;
  regs.r12 = 0xbb000000000000cc;
  regs.r13 = 0xcc000000000000dd;
  regs.r14 = 0xdd000000000000ee;
  /*
   * These stack pointer test values need to be 32-bit, since the r15
   * base address gets added to them.
   */
  regs.stack_ptr = 0x12300321;
  regs.rbp = 0x23400432;
  ASM_WITH_REGS(&regs, "jmp fault_addr\n");
#elif defined(__arm__)
  regs.r0 = 0x00000001;
  regs.r1 = 0x10000002;
  regs.r2 = 0x20000003;
  regs.r3 = 0x30000004;
  regs.r4 = 0x40000005;
  regs.r5 = 0x50000006;
  regs.r6 = 0x60000007;
  regs.r7 = 0x70000008;
  regs.r8 = 0x80000009;
  /*
   * Skip r9 because it is not supposed to be settable or readable by
   * untrusted code.
   */
  regs.r10 = 0xa000000b;
  regs.r11 = 0xb000000c;
  regs.r12 = 0xc000000d;
  /* stack_ptr's test value must be within the sandbox address space. */
  regs.stack_ptr = 0x12345678;
  regs.lr = 0xe000000f;
  regs.cpsr = (1 << 29) | (1 << 27); /* C and Q flags */
  ASM_WITH_REGS(&regs, "b fault_addr\n");
#else
# error Update set_registers_and_stop for other architectures
#endif
}