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