Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(281)

Unified Diff: tests/syscall_return_regs/syscall_return_regs_test.c

Issue 11185060: Add a test to check the initial register state for a new thread (Closed) Base URL: svn://svn.chromium.org/native_client/trunk/src/native_client
Patch Set: Use longer name Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tests/syscall_return_regs/nacl.scons ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tests/syscall_return_regs/syscall_return_regs_test.c
diff --git a/tests/syscall_return_regs/syscall_return_regs_test.c b/tests/syscall_return_regs/syscall_return_regs_test.c
index cd420c812b1230c4ddee0eb00180ee4371b819a1..b7ab72a942efb2223aa7cd403af9854350d52a64 100644
--- a/tests/syscall_return_regs/syscall_return_regs_test.c
+++ b/tests/syscall_return_regs/syscall_return_regs_test.c
@@ -5,16 +5,64 @@
*/
#include <assert.h>
+#include <sched.h>
#include <setjmp.h>
#include <stdio.h>
+#include <string.h>
#include "native_client/src/untrusted/nacl/syscall_bindings_trampoline.h"
+#include "native_client/src/untrusted/valgrind/dynamic_annotations.h"
#include "native_client/tests/common/register_set.h"
struct NaClSignalContext g_expected_regs;
jmp_buf g_return_jmp_buf;
+char g_stack[0x1000];
+/* We use 'volatile' because we spin waiting for this variable to be set. */
+volatile int32_t g_stack_in_use;
+volatile struct NaClSignalContext g_initial_thread_regs;
+
+
+/*
+ * Modify expected_regs to reflect how the current implementations of
+ * NaClSwitch() leave the register state on entry to untrusted code.
+ */
+void SetNaClSwitchExpectations(struct NaClSignalContext *expected_regs) {
+#if defined(__i386__) || defined(__x86_64__)
+ /*
+ * The context switch contains an "xorl %rN, %rN" to zero %rN which
+ * also has the effect of resetting flags (at least those recorded
+ * by REGS_SAVER_FUNC) to the following:
+ * bit 0: CF=0 (always set to 0 by xor)
+ * bit 2: PF=1 (bottom 8 bits of result have even number of 1s)
+ * bit 6: ZF=1 (set since result of xor is zero)
+ * bit 7: SF=0 (top bit of result)
+ * bit 11: OF=0 (always set to 0 by xor)
+ */
+ expected_regs->flags = (1 << 2) | (1 << 6);
+#endif
+
+#if defined(__i386__)
+ /* The current implementation sets %edx to the return address. */
+ expected_regs->edx = expected_regs->prog_ctr;
+#elif defined(__x86_64__)
+ /* The current implementation sets %rcx to the return address. */
+ expected_regs->rcx = expected_regs->prog_ctr;
+ /* NaCl always sets %rdi (argument 1) as if it is calling _start. */
+ expected_regs->rdi = (uintptr_t) expected_regs->stack_ptr + 8;
+#elif defined(__arm__)
+ /* The current implementation sets r1 to the return address. */
+ expected_regs->r1 = expected_regs->prog_ctr;
+ /*
+ * When starting a new thread, NaCl sets r0 (argument 1) as if it is
+ * calling _start.
+ */
+ expected_regs->r0 = expected_regs->stack_ptr;
+#endif
+}
+
+
REGS_SAVER_FUNC(ContinueAfterSyscall, CheckRegistersAfterSyscall);
void CheckRegistersAfterSyscall(struct NaClSignalContext *regs) {
@@ -45,37 +93,16 @@ void TestSyscall(uintptr_t syscall_addr) {
g_expected_regs = call_regs;
RegsUnsetNonCalleeSavedRegisters(&g_expected_regs);
-
-#if defined(__i386__) || defined(__x86_64__)
- /*
- * The context switch contains an "xorl %rN, %rN" to zero %rN
- * which also has the effect of resetting flags (at least those
- * recorded by REGS_SAVER_FUNC) to the following:
- * bit 0: CF=0 (always set to 0 by xor)
- * bit 2: PF=1 (bottom 8 bits of result have even number of 1s)
- * bit 6: ZF=1 (set since result of xor is zero)
- * bit 7: SF=0 (top bit of result)
- * bit 11: OF=0 (always set to 0 by xor)
- */
- g_expected_regs.flags = (1 << 2) | (1 << 6);
-#endif
+ SetNaClSwitchExpectations(&g_expected_regs);
if (!setjmp(g_return_jmp_buf)) {
#if defined(__i386__)
- /* The current implementation sets %edx to the return address. */
- g_expected_regs.edx = (uintptr_t) ContinueAfterSyscall;
-
call_regs.eax = syscall_addr;
ASM_WITH_REGS(
&call_regs,
"push $ContinueAfterSyscall\n" /* Push return address */
"nacljmp %%eax\n");
#elif defined(__x86_64__)
- /* The current implementation sets %rcx to the return address. */
- g_expected_regs.rcx = call_regs.r15 + (uintptr_t) ContinueAfterSyscall;
- /* NaCl always sets %rdi (argument 1) as if it is calling _start. */
- g_expected_regs.rdi = (uintptr_t) call_regs.stack_ptr + 8;
-
/*
* This fast path syscall happens to preserve various registers,
* but that is obviously not guaranteed by the ABI.
@@ -97,9 +124,6 @@ void TestSyscall(uintptr_t syscall_addr) {
"push $ContinueAfterSyscall\n" /* Push return address */
"nacljmp %%eax, %%r15\n");
#elif defined(__arm__)
- /* The current implementation sets r1 to the return address. */
- g_expected_regs.r1 = (uintptr_t) ContinueAfterSyscall;
-
call_regs.r1 = syscall_addr; /* Scratch register */
call_regs.lr = (uintptr_t) ContinueAfterSyscall; /* Return address */
ASM_WITH_REGS(
@@ -113,6 +137,58 @@ void TestSyscall(uintptr_t syscall_addr) {
}
}
+
+REGS_SAVER_FUNC(ThreadFuncWrapper, ThreadFunc);
+
+void ThreadFunc(struct NaClSignalContext *regs) {
+ g_initial_thread_regs = *regs;
+ ANNOTATE_CONDVAR_SIGNAL(&g_stack_in_use);
+ NACL_SYSCALL(thread_exit)((int32_t *) &g_stack_in_use);
+ assert(!"Should not reach here");
+}
+
+/*
+ * This tests that when a new thread is created, untrusted code is
+ * entered with well-defined register state. None of the registers
+ * should come from uninitialised values.
+ */
+void TestInitialRegsAtThreadEntry() {
+ char *stack_top = g_stack + sizeof(g_stack);
+ uintptr_t aligned_stack_top =
+ ((uintptr_t) stack_top & ~NACL_STACK_ALIGN_MASK)
+ - NACL_STACK_PAD_BELOW_ALIGN;
+ /*
+ * We do not care about TLS for this test, but sel_ldr rejects a
+ * zero tls argument, so use an arbitrary non-zero value.
+ */
+ char *tls = (char *) 0x1000;
+ g_stack_in_use = 1;
+ int rc = NACL_SYSCALL(thread_create)((void *) (uintptr_t) ThreadFuncWrapper,
+ stack_top, tls, 0);
+ assert(rc == 0);
+ /* Spin until the thread exits. */
+ while (g_stack_in_use) {
+ sched_yield();
+ }
+ ANNOTATE_CONDVAR_WAIT(&g_stack_in_use);
+ struct NaClSignalContext actual_regs = g_initial_thread_regs;
+
+ struct NaClSignalContext expected_regs;
+ /* By default, we expect registers to be initialised to zero. */
+ memset(&expected_regs, 0, sizeof(expected_regs));
+ expected_regs.prog_ctr = (uintptr_t) ThreadFuncWrapper;
+ expected_regs.stack_ptr = aligned_stack_top;
+ RegsApplySandboxConstraints(&expected_regs);
+ SetNaClSwitchExpectations(&expected_regs);
+#if defined(__x86_64__)
+ /* NaCl happens to initialise %rbp to be the same as %rsp. */
+ expected_regs.rbp = expected_regs.stack_ptr;
+#endif
+
+ RegsAssertEqual(&actual_regs, &expected_regs);
+}
+
+
int main() {
printf("Testing null syscall...\n");
TestSyscall((uintptr_t) NACL_SYSCALL(null));
@@ -124,5 +200,8 @@ int main() {
printf("Testing tls_get syscall...\n");
TestSyscall((uintptr_t) NACL_SYSCALL(tls_get));
+ printf("Testing initial register state at thread entry...\n");
+ TestInitialRegsAtThreadEntry();
+
return 0;
}
« no previous file with comments | « tests/syscall_return_regs/nacl.scons ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698