/* 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 }
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 } } }
void set_registers_and_stop(void) { struct NaClSignalContext regs; memset(®s, 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(®s, "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(®s, "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(®s, "b fault_addr\n"); #else # error Update set_registers_and_stop for other architectures #endif }