/* * This is a test for a bug that occurred only on Mac OS X on x86-32. * If we modify the registers of a suspended thread on x86-32 Mac, we * force the thread to return to untrusted code via the special * trusted routine NaClSwitchRemainingRegsViaECX(). If we later * suspend the thread while it is still executing this routine, we * need to return the untrusted register state that the routine is * attempting to restore -- we test that here. * * Suspending a thread while it is still blocked by a fault is the * easiest way to test this, since this allows us to test suspension * while the thread is at the start of * NaClSwitchRemainingRegsViaECX(). This is also how the debug stub * runs into this problem. */ void TestGettingRegistersInMacSwitchRemainingRegs(struct NaClApp *nap) { struct NaClSignalContext *expected_regs = StartGuestWithSharedMemory(nap); struct NaClSignalContext regs; struct NaClAppThread *natp; int signal; WaitForThreadToFault(nap); NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1); natp = GetOnlyThread(nap); NaClAppThreadGetSuspendedRegisters(natp, ®s); RegsAssertEqual(®s, expected_regs); /* * On Mac OS X on x86-32, this changes the underlying Mac thread's * state so that the thread will execute * NaClSwitchRemainingRegsViaECX(). */ NaClAppThreadSetSuspendedRegisters(natp, ®s); /* * Resume the thread without unblocking it and then re-suspend it. * This causes osx/thread_suspension.c to re-fetch the register * state from the Mac kernel. */ NaClUntrustedThreadsResumeAll(nap); NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1); ASSERT_EQ(GetOnlyThread(nap), natp); /* We should get the same register state as before. */ NaClAppThreadGetSuspendedRegisters(natp, ®s); RegsAssertEqual(®s, expected_regs); /* * Clean up: Skip over the instruction that faulted and let the * thread run to completion. */ regs.prog_ctr += kBreakInstructionSize; NaClAppThreadSetSuspendedRegisters(natp, ®s); ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &signal), 1); NaClUntrustedThreadsResumeAll(nap); CHECK(NaClWaitForMainThreadToExit(nap) == 0); }
void TestReceivingFault(struct NaClApp *nap) { struct NaClSignalContext *expected_regs = StartGuestWithSharedMemory(nap); struct NaClSignalContext regs; struct NaClAppThread *natp; int signal = 0; int dummy_signal; WaitForThreadToFault(nap); NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1); natp = GetOnlyThread(nap); ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &signal), 1); /* * TODO(mseaborn): Move ExceptionToSignal() out of debug_stub and * enable this check on Windows too. */ if (!NACL_WINDOWS) { ASSERT_EQ(signal, kBreakInstructionSignal); } /* Check that faulted_thread_count is updated correctly. */ ASSERT_EQ(nap->faulted_thread_count, 0); /* * Check that NaClAppThreadUnblockIfFaulted() returns false when * called on a thread that is not blocked. */ ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &dummy_signal), 0); NaClAppThreadGetSuspendedRegisters(natp, ®s); RegsAssertEqual(®s, expected_regs); /* Skip over the instruction that faulted. */ regs.prog_ctr += kBreakInstructionSize; NaClAppThreadSetSuspendedRegisters(natp, ®s); TestSingleStepping(natp); NaClUntrustedThreadsResumeAll(nap); CHECK(NaClWaitForMainThreadToExit(nap) == 0); }