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 |