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> |
11 #include <sys/stat.h> | 11 #include <sys/stat.h> |
12 #include <unistd.h> | 12 #include <unistd.h> |
13 | 13 |
14 #include <string> | 14 #include <string> |
15 | 15 |
16 #include "base/basictypes.h" | 16 #include "base/basictypes.h" |
| 17 #include "base/bind.h" |
| 18 #include "base/callback.h" |
| 19 #include "base/files/scoped_file.h" |
17 #include "base/logging.h" | 20 #include "base/logging.h" |
18 #include "base/posix/eintr_wrapper.h" | 21 #include "base/posix/eintr_wrapper.h" |
19 #include "base/strings/string_number_conversions.h" | 22 #include "base/strings/string_number_conversions.h" |
20 #include "base/threading/platform_thread.h" | 23 #include "base/threading/platform_thread.h" |
21 #include "base/threading/thread.h" | 24 #include "base/threading/thread.h" |
| 25 #include "sandbox/linux/services/proc_util.h" |
22 | 26 |
23 namespace sandbox { | 27 namespace sandbox { |
24 | 28 |
25 namespace { | 29 namespace { |
26 | 30 |
| 31 const char kAssertSingleThreadedError[] = |
| 32 "Current process is not mono-threaded!"; |
| 33 |
27 bool IsSingleThreadedImpl(int proc_self_task) { | 34 bool IsSingleThreadedImpl(int proc_self_task) { |
28 CHECK_LE(0, proc_self_task); | 35 CHECK_LE(0, proc_self_task); |
29 struct stat task_stat; | 36 struct stat task_stat; |
30 int fstat_ret = fstat(proc_self_task, &task_stat); | 37 int fstat_ret = fstat(proc_self_task, &task_stat); |
31 PCHECK(0 == fstat_ret); | 38 PCHECK(0 == fstat_ret); |
32 | 39 |
33 // At least "..", "." and the current thread should be present. | 40 // At least "..", "." and the current thread should be present. |
34 CHECK_LE(3UL, task_stat.st_nlink); | 41 CHECK_LE(3UL, task_stat.st_nlink); |
35 // Counting threads via /proc/self/task could be racy. For the purpose of | 42 // Counting threads via /proc/self/task could be racy. For the purpose of |
36 // determining if the current proces is monothreaded it works: if at any | 43 // determining if the current proces is monothreaded it works: if at any |
37 // time it becomes monothreaded, it'll stay so. | 44 // time it becomes monothreaded, it'll stay so. |
38 return task_stat.st_nlink == 3; | 45 return task_stat.st_nlink == 3; |
39 } | 46 } |
40 | 47 |
| 48 bool IsThreadPresentInProcFS(int proc_self_task, |
| 49 const std::string& thread_id_dir_str) { |
| 50 struct stat task_stat; |
| 51 const int fstat_ret = |
| 52 fstatat(proc_self_task, thread_id_dir_str.c_str(), &task_stat, 0); |
| 53 if (fstat_ret < 0) { |
| 54 PCHECK(ENOENT == errno); |
| 55 return false; |
| 56 } |
| 57 return true; |
| 58 } |
| 59 |
| 60 // Run |cb| in a loop until it returns false. Every time |cb| runs, sleep |
| 61 // for an exponentially increasing amount of time. |cb| is expected to return |
| 62 // false very quickly and this will crash if it doesn't happen within ~64ms on |
| 63 // Debug builds (2s on Release builds). |
| 64 // This is guaranteed to not sleep more than twice as much as the bare minimum |
| 65 // amount of time. |
| 66 void RunWhileTrue(const base::Callback<bool(void)>& cb) { |
| 67 #if defined(NDEBUG) |
| 68 // In Release mode, crash after 30 iterations, which means having spent |
| 69 // roughly 2s in |
| 70 // nanosleep(2) cumulatively. |
| 71 const unsigned int kMaxIterations = 30U; |
| 72 #else |
| 73 // In practice, this never goes through more than a couple iterations. In |
| 74 // debug mode, crash after 64ms (+ eventually 25 times the granularity of |
| 75 // the clock) in nanosleep(2). This ensures that this is not becoming too |
| 76 // slow. |
| 77 const unsigned int kMaxIterations = 25U; |
| 78 #endif |
| 79 |
| 80 // Run |cb| with an exponential back-off, sleeping 2^iterations nanoseconds |
| 81 // in nanosleep(2). |
| 82 // Note: the clock may not allow for nanosecond granularity, in this case the |
| 83 // first iterations would sleep a tiny bit more instead, which would not |
| 84 // change the calculations significantly. |
| 85 for (unsigned int i = 0; i < kMaxIterations; ++i) { |
| 86 if (!cb.Run()) { |
| 87 return; |
| 88 } |
| 89 |
| 90 // Increase the waiting time exponentially. |
| 91 struct timespec ts = {0, 1L << i /* nanoseconds */}; |
| 92 PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts))); |
| 93 } |
| 94 |
| 95 LOG(FATAL) << kAssertSingleThreadedError << " (iterations: " << kMaxIterations |
| 96 << ")"; |
| 97 |
| 98 NOTREACHED(); |
| 99 } |
| 100 |
| 101 bool IsMultiThreaded(int proc_self_task) { |
| 102 return !ThreadHelpers::IsSingleThreaded(proc_self_task); |
| 103 } |
| 104 |
41 } // namespace | 105 } // namespace |
42 | 106 |
| 107 // static |
43 bool ThreadHelpers::IsSingleThreaded(int proc_self_task) { | 108 bool ThreadHelpers::IsSingleThreaded(int proc_self_task) { |
44 DCHECK_LE(-1, proc_self_task); | 109 DCHECK_LE(0, proc_self_task); |
45 if (-1 == proc_self_task) { | 110 return IsSingleThreadedImpl(proc_self_task); |
46 const int task_fd = | |
47 open("/proc/self/task/", O_RDONLY | O_DIRECTORY | O_CLOEXEC); | |
48 PCHECK(0 <= task_fd); | |
49 const bool result = IsSingleThreadedImpl(task_fd); | |
50 PCHECK(0 == IGNORE_EINTR(close(task_fd))); | |
51 return result; | |
52 } else { | |
53 return IsSingleThreadedImpl(proc_self_task); | |
54 } | |
55 } | 111 } |
56 | 112 |
| 113 // static |
| 114 bool ThreadHelpers::IsSingleThreaded() { |
| 115 base::ScopedFD task_fd(ProcUtil::OpenProcSelfTask()); |
| 116 return IsSingleThreaded(task_fd.get()); |
| 117 } |
| 118 |
| 119 // static |
| 120 void ThreadHelpers::AssertSingleThreaded(int proc_self_task) { |
| 121 DCHECK_LE(0, proc_self_task); |
| 122 const base::Callback<bool(void)> cb = |
| 123 base::Bind(&IsMultiThreaded, proc_self_task); |
| 124 RunWhileTrue(cb); |
| 125 } |
| 126 |
| 127 void ThreadHelpers::AssertSingleThreaded() { |
| 128 base::ScopedFD task_fd(ProcUtil::OpenProcSelfTask()); |
| 129 AssertSingleThreaded(task_fd.get()); |
| 130 } |
| 131 |
| 132 // static |
57 bool ThreadHelpers::StopThreadAndWatchProcFS(int proc_self_task, | 133 bool ThreadHelpers::StopThreadAndWatchProcFS(int proc_self_task, |
58 base::Thread* thread) { | 134 base::Thread* thread) { |
59 DCHECK_LE(0, proc_self_task); | 135 DCHECK_LE(0, proc_self_task); |
60 DCHECK(thread); | 136 DCHECK(thread); |
61 const base::PlatformThreadId thread_id = thread->thread_id(); | 137 const base::PlatformThreadId thread_id = thread->thread_id(); |
62 const std::string thread_id_dir_str = base::IntToString(thread_id) + "/"; | 138 const std::string thread_id_dir_str = base::IntToString(thread_id) + "/"; |
63 | 139 |
64 // The kernel is at liberty to wake the thread id futex before updating | 140 // The kernel is at liberty to wake the thread id futex before updating |
65 // /proc. Following Stop(), the thread is joined, but entries in /proc may | 141 // /proc. Following Stop(), the thread is joined, but entries in /proc may |
66 // not have been updated. | 142 // not have been updated. |
67 thread->Stop(); | 143 thread->Stop(); |
68 | 144 |
69 unsigned int iterations = 0; | 145 const base::Callback<bool(void)> cb = |
70 bool thread_present_in_procfs = true; | 146 base::Bind(&IsThreadPresentInProcFS, proc_self_task, thread_id_dir_str); |
71 // Poll /proc with an exponential back-off, sleeping 2^iterations nanoseconds | |
72 // in nanosleep(2). | |
73 // Note: the clock may not allow for nanosecond granularity, in this case the | |
74 // first iterations would sleep a tiny bit more instead, which would not | |
75 // change the calculations significantly. | |
76 while (thread_present_in_procfs) { | |
77 struct stat task_stat; | |
78 const int fstat_ret = | |
79 fstatat(proc_self_task, thread_id_dir_str.c_str(), &task_stat, 0); | |
80 if (fstat_ret < 0) { | |
81 PCHECK(ENOENT == errno); | |
82 // The thread disappeared from /proc, we're done. | |
83 thread_present_in_procfs = false; | |
84 break; | |
85 } | |
86 // Increase the waiting time exponentially. | |
87 struct timespec ts = {0, 1L << iterations /* nanoseconds */}; | |
88 PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts))); | |
89 ++iterations; | |
90 | 147 |
91 // Crash after 30 iterations, which means having spent roughly 2s in | 148 RunWhileTrue(cb); |
92 // nanosleep(2) cumulatively. | |
93 CHECK_GT(30U, iterations); | |
94 // In practice, this never goes through more than a couple iterations. In | |
95 // debug mode, crash after 64ms (+ eventually 25 times the granularity of | |
96 // the clock) in nanosleep(2). | |
97 DCHECK_GT(25U, iterations); | |
98 } | |
99 | 149 |
100 return true; | 150 return true; |
101 } | 151 } |
102 | 152 |
| 153 // static |
| 154 const char* ThreadHelpers::GetAssertSingleThreadedErrorMessageForTests() { |
| 155 return kAssertSingleThreadedError; |
| 156 } |
| 157 |
103 } // namespace sandbox | 158 } // namespace sandbox |
OLD | NEW |