LCOV - code coverage report
Current view: directory - src/trusted/service_runtime - thread_suspension_test.cc (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 85 83 97.6 %
Date: 2014-06-18 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2012 The Native Client Authors. All rights reserved.
       3                 :  * Use of this source code is governed by a BSD-style license that can be
       4                 :  * found in the LICENSE file.
       5                 :  */
       6                 : 
       7                 : #include "native_client/src/shared/platform/nacl_log.h"
       8                 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
       9                 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
      10                 : #include "native_client/src/trusted/service_runtime/thread_suspension.h"
      11                 : #include "native_client/src/trusted/desc/nrd_all_modules.h"
      12                 : 
      13                 : #include "gtest/gtest.h"
      14                 : 
      15                 : 
      16                 : // These test cases test suspension of untrusted threads.  They use
      17                 : // mock untrusted threads, so they do not test interactions between
      18                 : // thread suspension and context switches between trusted and
      19                 : // untrusted code.  See tests/thread_suspension for similar tests that
      20                 : // operate on real untrusted threads.
      21                 : 
      22                 : 
      23                 : // These tests work with the Windows and Mac implementations of thread
      24                 : // suspension, but the Linux implementation (based on asynchronous
      25                 : // signals) requires more of NaClAppThread to be present than we mock
      26                 : // out here.  See tests/thread_suspension for a more integrationy test
      27                 : // that works on Linux.
      28                 : #if !NACL_LINUX
      29                 : 
      30               4 : class ThreadSuspensionTest : public testing::Test {
      31                 :  protected:
      32                 :   virtual void SetUp();
      33                 :   virtual void TearDown();
      34                 : };
      35                 : 
      36                 : void ThreadSuspensionTest::SetUp() {
      37               2 :   NaClNrdAllModulesInit();
      38               2 : }
      39                 : 
      40                 : void ThreadSuspensionTest::TearDown() {
      41               2 :   NaClNrdAllModulesFini();
      42               2 : }
      43                 : 
      44                 : 
      45                 : struct ThreadArgs {
      46                 :   struct NaClAppThread *natp;
      47                 :   volatile uint32_t var;
      48                 :   volatile bool should_exit;
      49                 : };
      50                 : 
      51                 : // Perform some minimal initialisation of the NaClAppThread based on
      52                 : // what we observe we need for the test.  Reusing NaClAppThreadMake()
      53                 : // here is difficult because it launches an untrusted thread.
      54               2 : static void AppThreadInitMinimal(struct NaClAppThread *natp) {
      55               2 :   memset(natp, 0xff, sizeof(*natp));
      56               2 :   natp->suspend_state = NACL_APP_THREAD_UNTRUSTED;
      57               2 :   natp->suspended_registers = NULL;
      58              10 :   ASSERT_EQ(NaClMutexCtor(&natp->suspend_mu), 1);
      59               2 : }
      60                 : 
      61                 : 
      62                 : // This test checks that after NaClUntrustedThreadsSuspendAll() has
      63                 : // returned, untrusted threads are completely suspended.  We test this
      64                 : // by running a thread that writes to a memory location.  We check
      65                 : // that the memory location does not change after
      66                 : // NaClUntrustedThreadsSuspendAll() but does change after
      67                 : // NaClUntrustedThreadsResumeAll().
      68                 : //
      69                 : // This is a regression test for
      70                 : // http://code.google.com/p/nativeclient/issues/detail?id=2557
      71                 : // The gotcha in the Windows API is that SuspendThread() can return
      72                 : // before the thread has really been suspended.
      73                 : //
      74                 : // This is technically a stress test, but it was able to reproduce the
      75                 : // problem very reliably, at least on a multicore machine.
      76                 : 
      77               1 : static void WINAPI MutatorThread(void *arg) {
      78               1 :   struct ThreadArgs *args = (struct ThreadArgs *) arg;
      79               1 :   uint32_t next_val = 0;
      80          106630 :   while (!args->should_exit) {
      81          106628 :     args->var = next_val++;
      82          106628 :   }
      83               1 : }
      84                 : 
      85               1 : static void TrySuspendingMutatorThread(struct NaClApp *nap,
      86               1 :                                        volatile uint32_t *addr) {
      87                 :   // Wait for guest program to start writing to the address.
      88           14608 :   while (*addr == 0) { /* do nothing */ }
      89                 : 
      90             202 :   for (int iteration = 0; iteration < 100; iteration++) {
      91             100 :     NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 0);
      92             100 :     uint32_t snapshot = *addr;
      93        20000200 :     for (int count = 0; count < 100000; count++) {
      94        10000000 :       uint32_t snapshot2 = *addr;
      95        10000000 :       if (snapshot2 != snapshot) {
      96               0 :         NaClLog(LOG_FATAL,
      97                 :                 "Read %i but expected %i on try %i of iteration %i\n",
      98                 :                 (int) snapshot2, (int) snapshot, count, iteration);
      99               0 :       }
     100        10000000 :     }
     101             100 :     NaClUntrustedThreadsResumeAll(nap);
     102                 :     // Wait for guest program to resume writing.
     103         1077222 :     while (*addr == snapshot) { /* do nothing */ }
     104             100 :   }
     105               1 : }
     106                 : 
     107               8 : TEST_F(ThreadSuspensionTest, TestThreadSuspendsSynchronously) {
     108               1 :   struct NaClApp app;
     109               5 :   ASSERT_EQ(NaClAppCtor(&app), 1);
     110                 : 
     111               1 :   struct NaClAppThread app_thread;
     112               1 :   AppThreadInitMinimal(&app_thread);
     113                 : 
     114               1 :   struct ThreadArgs thread_args;
     115               1 :   thread_args.natp = NULL;
     116               1 :   thread_args.var = 0;
     117               1 :   thread_args.should_exit = false;
     118               1 :   app_thread.host_thread_is_defined = 1;
     119               5 :   ASSERT_EQ(NaClThreadCreateJoinable(&app_thread.host_thread, MutatorThread,
     120                 :                                      &thread_args, NACL_KERN_STACK_SIZE), 1);
     121               6 :   ASSERT_EQ(NaClAddThread(&app, &app_thread), 0);
     122               1 :   TrySuspendingMutatorThread(&app, &thread_args.var);
     123               1 :   thread_args.should_exit = true;
     124               1 :   NaClThreadJoin(&app_thread.host_thread);
     125               2 : }
     126                 : 
     127                 : 
     128                 : // The test below checks that we do not get a deadlock when using
     129                 : // NaClUntrustedThreadsSuspendAll() on threads that cross between
     130                 : // untrusted and trusted code by invoking NaCl syscalls.
     131                 : //
     132                 : // This is a stress test.  It is not guaranteed to find a problem, but
     133                 : // it did reproduce the problem in the original buggy version of the
     134                 : // code when Sleep() calls were inserted in suitable places.
     135                 : //
     136                 : // See http://code.google.com/p/nativeclient/issues/detail?id=2569
     137                 : 
     138               1 : static void WINAPI SyscallInvokerThread(void *arg) {
     139               1 :   struct ThreadArgs *args = (struct ThreadArgs *) arg;
     140               1 :   uint32_t next_val = 0;
     141             201 :   while (!args->should_exit) {
     142             199 :     NaClAppThreadSetSuspendState(args->natp, NACL_APP_THREAD_UNTRUSTED,
     143                 :                                  NACL_APP_THREAD_TRUSTED);
     144             199 :     args->var = next_val++;
     145             199 :     NaClAppThreadSetSuspendState(args->natp, NACL_APP_THREAD_TRUSTED,
     146                 :                                  NACL_APP_THREAD_UNTRUSTED);
     147             199 :   }
     148               1 : }
     149                 : 
     150               1 : static void TrySuspendingSyscallInvokerThread(struct NaClApp *nap,
     151               1 :                                               volatile uint32_t *addr) {
     152                 :   // Wait for guest program to start writing to the address.
     153           36928 :   while (*addr == 0) { /* do nothing */ }
     154                 : 
     155             202 :   for (int iteration = 0; iteration < 100; iteration++) {
     156             100 :     NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 0);
     157             100 :     NaClUntrustedThreadsResumeAll(nap);
     158                 : 
     159                 :     // Wait for guest program to make some progress.
     160             100 :     uint32_t snapshot = *addr;
     161          708020 :     while (*addr == snapshot) { /* do nothing */ }
     162             100 :   }
     163               1 : }
     164                 : 
     165               8 : TEST_F(ThreadSuspensionTest, TestNoDeadlockInSyscallCrossing) {
     166               1 :   struct NaClApp app;
     167               5 :   ASSERT_EQ(NaClAppCtor(&app), 1);
     168                 : 
     169               1 :   struct NaClAppThread app_thread;
     170               1 :   AppThreadInitMinimal(&app_thread);
     171                 : 
     172               1 :   struct ThreadArgs thread_args;
     173               1 :   thread_args.natp = &app_thread;
     174               1 :   thread_args.var = 0;
     175               1 :   thread_args.should_exit = false;
     176               1 :   app_thread.host_thread_is_defined = 1;
     177               5 :   ASSERT_EQ(NaClThreadCreateJoinable(&app_thread.host_thread,
     178                 :                                      SyscallInvokerThread,
     179                 :                                      &thread_args, NACL_KERN_STACK_SIZE), 1);
     180               6 :   ASSERT_EQ(NaClAddThread(&app, &app_thread), 0);
     181               1 :   TrySuspendingSyscallInvokerThread(&app, &thread_args.var);
     182               1 :   thread_args.should_exit = true;
     183               1 :   NaClThreadJoin(&app_thread.host_thread);
     184               2 : }
     185                 : 
     186                 : #endif

Generated by: LCOV version 1.7