1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "sandbox/linux/services/thread_helpers.h" 6 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <signal.h> 10 #include <sys/types.h> 11 #include <sys/stat.h> 12 #include <unistd.h> 13 14 #include <string> 15 16 #include "base/bind.h" 17 #include "base/callback.h" 18 #include "base/files/scoped_file.h" 19 #include "base/logging.h" 20 #include "base/posix/eintr_wrapper.h" 21 #include "base/strings/string_number_conversions.h" 22 #include "base/threading/platform_thread.h" 23 #include "base/threading/thread.h" 24 #include "sandbox/linux/services/proc_util.h" 25 26 namespace sandbox { 27 28 namespace { 29 30 const char kAssertSingleThreadedError[] = 31 "Current process is not mono-threaded!"; 32 const char kAssertThreadDoesNotAppearInProcFS[] = 33 "Started thread does not appear in /proc"; 34 const char kAssertThreadDoesNotDisappearInProcFS[] = 35 "Stopped thread does not disappear in /proc"; 36 37 bool IsSingleThreadedImpl(int proc_fd) { 38 CHECK_LE(0, proc_fd); 39 struct stat task_stat; 40 int fstat_ret = fstatat(proc_fd, "self/task/", &task_stat, 0); 41 PCHECK(0 == fstat_ret); 42 43 // At least "..", "." and the current thread should be present. 44 CHECK_LE(3UL, task_stat.st_nlink); 45 // Counting threads via /proc/self/task could be racy. For the purpose of 46 // determining if the current proces is monothreaded it works: if at any 47 // time it becomes monothreaded, it'll stay so. 48 return task_stat.st_nlink == 3; 49 } 50 51 bool IsThreadPresentInProcFS(int proc_fd, 52 const std::string& thread_id_dir_str) { 53 struct stat task_stat; 54 const int fstat_ret = 55 fstatat(proc_fd, thread_id_dir_str.c_str(), &task_stat, 0); 56 if (fstat_ret < 0) { 57 PCHECK(ENOENT == errno); 58 return false; 59 } 60 return true; 61 } 62 63 bool IsNotThreadPresentInProcFS(int proc_fd, 64 const std::string& thread_id_dir_str) { 65 return !IsThreadPresentInProcFS(proc_fd, thread_id_dir_str); 66 } 67 68 // Run |cb| in a loop until it returns false. Every time |cb| runs, sleep 69 // for an exponentially increasing amount of time. |cb| is expected to return 70 // false very quickly and this will crash if it doesn't happen within ~64ms on 71 // Debug builds (2s on Release builds). 72 // This is guaranteed to not sleep more than twice as much as the bare minimum 73 // amount of time. 74 void RunWhileTrue(const base::Callback<bool(void)>& cb, const char* message) { 75 #if defined(NDEBUG) 76 // In Release mode, crash after 30 iterations, which means having spent 77 // roughly 2s in 78 // nanosleep(2) cumulatively. 79 const unsigned int kMaxIterations = 30U; 80 #else 81 // In practice, this never goes through more than a couple iterations. In 82 // debug mode, crash after 64ms (+ eventually 25 times the granularity of 83 // the clock) in nanosleep(2). This ensures that this is not becoming too 84 // slow. 85 const unsigned int kMaxIterations = 25U; 86 #endif 87 88 // Run |cb| with an exponential back-off, sleeping 2^iterations nanoseconds 89 // in nanosleep(2). 90 // Note: the clock may not allow for nanosecond granularity, in this case the 91 // first iterations would sleep a tiny bit more instead, which would not 92 // change the calculations significantly. 93 for (unsigned int i = 0; i < kMaxIterations; ++i) { 94 if (!cb.Run()) { 95 return; 96 } 97 98 // Increase the waiting time exponentially. 99 struct timespec ts = {0, 1L << i /* nanoseconds */}; 100 PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts))); 101 } 102 103 LOG(FATAL) << message << " (iterations: " << kMaxIterations << ")"; 104 105 NOTREACHED(); 106 } 107 108 bool IsMultiThreaded(int proc_fd) { 109 return !ThreadHelpers::IsSingleThreaded(proc_fd); 110 } 111 112 enum class ThreadAction { Start, Stop }; 113 114 bool ChangeThreadStateAndWatchProcFS( 115 int proc_fd, base::Thread* thread, ThreadAction action) { 116 DCHECK_LE(0, proc_fd); 117 DCHECK(thread); 118 DCHECK(action == ThreadAction::Start || action == ThreadAction::Stop); 119 120 base::Callback<bool(void)> cb; 121 const char* message; 122 123 if (action == ThreadAction::Start) { 124 // Should start the thread before calling thread_id(). 125 if (!thread->Start()) 126 return false; 127 } 128 129 const base::PlatformThreadId thread_id = thread->GetThreadId(); 130 const std::string thread_id_dir_str = 131 "self/task/" + base::IntToString(thread_id) + "/"; 132 133 if (action == ThreadAction::Stop) { 134 // The target thread should exist in /proc. 135 DCHECK(IsThreadPresentInProcFS(proc_fd, thread_id_dir_str)); 136 thread->Stop(); 137 } 138 139 // The kernel is at liberty to wake the thread id futex before updating 140 // /proc. Start() above or following Stop(), the thread is started or joined, 141 // but entries in /proc may not have been updated. 142 if (action == ThreadAction::Start) { 143 cb = base::Bind(&IsNotThreadPresentInProcFS, proc_fd, thread_id_dir_str); 144 message = kAssertThreadDoesNotAppearInProcFS; 145 } else { 146 cb = base::Bind(&IsThreadPresentInProcFS, proc_fd, thread_id_dir_str); 147 message = kAssertThreadDoesNotDisappearInProcFS; 148 } 149 RunWhileTrue(cb, message); 150 151 DCHECK_EQ(action == ThreadAction::Start, 152 IsThreadPresentInProcFS(proc_fd, thread_id_dir_str)); 153 154 return true; 155 } 156 157 } // namespace 158 159 // static 160 bool ThreadHelpers::IsSingleThreaded(int proc_fd) { 161 DCHECK_LE(0, proc_fd); 162 return IsSingleThreadedImpl(proc_fd); 163 } 164 165 // static 166 bool ThreadHelpers::IsSingleThreaded() { 167 base::ScopedFD task_fd(ProcUtil::OpenProc()); 168 return IsSingleThreaded(task_fd.get()); 169 } 170 171 // static 172 void ThreadHelpers::AssertSingleThreaded(int proc_fd) { 173 DCHECK_LE(0, proc_fd); 174 const base::Callback<bool(void)> cb = base::Bind(&IsMultiThreaded, proc_fd); 175 RunWhileTrue(cb, kAssertSingleThreadedError); 176 } 177 178 void ThreadHelpers::AssertSingleThreaded() { 179 base::ScopedFD task_fd(ProcUtil::OpenProc()); 180 AssertSingleThreaded(task_fd.get()); 181 } 182 183 // static 184 bool ThreadHelpers::StartThreadAndWatchProcFS(int proc_fd, 185 base::Thread* thread) { 186 return ChangeThreadStateAndWatchProcFS(proc_fd, thread, ThreadAction::Start); 187 } 188 189 // static 190 bool ThreadHelpers::StopThreadAndWatchProcFS(int proc_fd, 191 base::Thread* thread) { 192 return ChangeThreadStateAndWatchProcFS(proc_fd, thread, ThreadAction::Stop); 193 } 194 195 // static 196 const char* ThreadHelpers::GetAssertSingleThreadedErrorMessageForTests() { 197 return kAssertSingleThreadedError; 198 } 199 200 } // namespace sandbox 201