Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 <fcntl.h> | 5 #include <fcntl.h> |
| 6 #include <sys/resource.h> | 6 #include <sys/resource.h> |
| 7 #include <sys/stat.h> | 7 #include <sys/stat.h> |
| 8 #include <sys/time.h> | 8 #include <sys/time.h> |
| 9 #include <sys/types.h> | 9 #include <sys/types.h> |
| 10 | 10 |
| 11 #include <limits> | 11 #include <limits> |
| 12 | 12 |
| 13 #include "base/bind.h" | |
| 14 #include "base/bind_helpers.h" | |
| 13 #include "base/command_line.h" | 15 #include "base/command_line.h" |
| 14 #include "base/file_util.h" | 16 #include "base/file_util.h" |
| 15 #include "base/logging.h" | 17 #include "base/logging.h" |
| 16 #include "base/memory/singleton.h" | 18 #include "base/memory/singleton.h" |
| 17 #include "base/posix/eintr_wrapper.h" | 19 #include "base/posix/eintr_wrapper.h" |
| 18 #include "base/time.h" | 20 #include "base/time.h" |
| 19 #include "content/common/sandbox_linux.h" | 21 #include "content/common/sandbox_linux.h" |
| 20 #include "content/common/sandbox_seccomp_bpf_linux.h" | 22 #include "content/common/sandbox_seccomp_bpf_linux.h" |
| 21 #include "content/public/common/content_switches.h" | 23 #include "content/public/common/content_switches.h" |
| 22 #include "content/public/common/sandbox_linux.h" | 24 #include "content/public/common/sandbox_linux.h" |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 73 LinuxSandbox* instance = Singleton<LinuxSandbox>::get(); | 75 LinuxSandbox* instance = Singleton<LinuxSandbox>::get(); |
| 74 CHECK(instance); | 76 CHECK(instance); |
| 75 return instance; | 77 return instance; |
| 76 } | 78 } |
| 77 | 79 |
| 78 #if defined(ADDRESS_SANITIZER) && defined(OS_LINUX) | 80 #if defined(ADDRESS_SANITIZER) && defined(OS_LINUX) |
| 79 // ASan API call to notify the tool the sandbox is going to be turned on. | 81 // ASan API call to notify the tool the sandbox is going to be turned on. |
| 80 extern "C" void __sanitizer_sandbox_on_notify(void *reserved); | 82 extern "C" void __sanitizer_sandbox_on_notify(void *reserved); |
| 81 #endif | 83 #endif |
| 82 | 84 |
| 83 void LinuxSandbox::PreinitializeSandboxBegin() { | 85 void LinuxSandbox::PreinitializeSandbox() { |
| 84 CHECK(!pre_initialized_); | 86 CHECK(!pre_initialized_); |
| 85 seccomp_bpf_supported_ = false; | 87 seccomp_bpf_supported_ = false; |
| 86 #if defined(ADDRESS_SANITIZER) && defined(OS_LINUX) | 88 #if defined(ADDRESS_SANITIZER) && defined(OS_LINUX) |
| 87 // ASan needs to open some resources before the sandbox is enabled. | 89 // ASan needs to open some resources before the sandbox is enabled. |
| 88 // This should not fork, not launch threads, not open a directory. | 90 // This should not fork, not launch threads, not open a directory. |
| 89 __sanitizer_sandbox_on_notify(/*reserved*/NULL); | 91 __sanitizer_sandbox_on_notify(/*reserved*/NULL); |
| 90 #endif | 92 #endif |
| 93 | |
| 94 #if !defined(NDEBUG) | |
| 95 // Open proc_fd_ only in Debug mode so that forgetting to close it doesn't | |
| 96 // produce a sandbox escape in Release mode. | |
| 97 proc_fd_ = open("/proc", O_DIRECTORY | O_RDONLY); | |
| 98 CHECK(proc_fd_ >= 0); | |
| 99 #endif // !defined(NDEBUG) | |
| 91 // We "pre-warm" the code that detects supports for seccomp BPF. | 100 // We "pre-warm" the code that detects supports for seccomp BPF. |
| 92 // TODO(jln): Use proc_fd_ here once we're comfortable it does not create | |
| 93 // an additional security risk. | |
| 94 if (SandboxSeccompBpf::IsSeccompBpfDesired()) { | 101 if (SandboxSeccompBpf::IsSeccompBpfDesired()) { |
| 95 if (!SandboxSeccompBpf::SupportsSandbox()) { | 102 if (!SandboxSeccompBpf::SupportsSandbox()) { |
| 96 VLOG(1) << "Lacking support for seccomp-bpf sandbox."; | 103 VLOG(1) << "Lacking support for seccomp-bpf sandbox."; |
| 97 } else { | 104 } else { |
| 98 seccomp_bpf_supported_ = true; | 105 seccomp_bpf_supported_ = true; |
| 99 } | 106 } |
| 100 } | 107 } |
| 101 pre_initialized_ = true; | 108 pre_initialized_ = true; |
| 102 } | 109 } |
| 103 | 110 |
| 104 // Once we finally know our process type, we can cleanup proc_fd_. | |
| 105 void LinuxSandbox::PreinitializeSandboxFinish( | |
| 106 const std::string& process_type) { | |
| 107 CHECK(pre_initialized_); | |
| 108 // TODO(jln): move this to InitializeSandbox. | |
| 109 if (proc_fd_ >= 0) { | |
| 110 CHECK_EQ(HANDLE_EINTR(close(proc_fd_)), 0); | |
| 111 proc_fd_ = -1; | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 void LinuxSandbox::PreinitializeSandbox(const std::string& process_type) { | |
| 116 PreinitializeSandboxBegin(); | |
| 117 PreinitializeSandboxFinish(process_type); | |
| 118 } | |
| 119 | |
| 120 bool LinuxSandbox::InitializeSandbox() { | 111 bool LinuxSandbox::InitializeSandbox() { |
| 121 bool seccomp_bpf_started = false; | 112 bool seccomp_bpf_started = false; |
| 122 LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance(); | 113 LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance(); |
| 114 // We need to make absolutely sure that our sandbox is "sealed" before | |
| 115 // InitializeSandbox does exit. | |
| 116 base::ScopedClosureRunner sandbox_sealer( | |
| 117 base::Bind(&LinuxSandbox::SealSandbox, base::Unretained(linux_sandbox))); | |
| 123 const std::string process_type = | 118 const std::string process_type = |
| 124 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 119 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 125 switches::kProcessType); | 120 switches::kProcessType); |
| 126 | 121 |
| 127 // No matter what, it's always an error to call InitializeSandbox() after | 122 // No matter what, it's always an error to call InitializeSandbox() after |
| 128 // threads have been created. | 123 // threads have been created. |
| 129 if (!linux_sandbox->IsSingleThreaded()) { | 124 if (!linux_sandbox->IsSingleThreaded()) { |
| 130 std::string error_message = "InitializeSandbox() called with multiple " | 125 std::string error_message = "InitializeSandbox() called with multiple " |
| 131 "threads in process " + process_type; | 126 "threads in process " + process_type; |
| 132 // TODO(jln): change this into a CHECK() once we are more comfortable it | 127 // The GPU process is allowed to call InitializeSandbox() with threads for |
| 133 // does not trigger. | 128 // now, because it loads third party libraries. |
| 129 if (process_type != switches::kGpuProcess) | |
| 130 DCHECK(false) << error_message; | |
| 134 LOG(ERROR) << error_message; | 131 LOG(ERROR) << error_message; |
| 135 return false; | 132 return false; |
| 136 } | 133 } |
| 137 | 134 |
| 138 // Attempt to limit the future size of the address space of the process. | 135 // Attempt to limit the future size of the address space of the process. |
| 139 linux_sandbox->LimitAddressSpace(process_type); | 136 linux_sandbox->LimitAddressSpace(process_type); |
| 140 | 137 |
| 141 // First, try to enable seccomp-bpf. | 138 // First, try to enable seccomp-bpf. |
| 142 seccomp_bpf_started = linux_sandbox->StartSeccompBpf(process_type); | 139 seccomp_bpf_started = linux_sandbox->StartSeccompBpf(process_type); |
| 143 | 140 |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 158 if (seccomp_bpf_supported() && | 155 if (seccomp_bpf_supported() && |
| 159 SandboxSeccompBpf::ShouldEnableSeccompBpf(switches::kRendererProcess)) { | 156 SandboxSeccompBpf::ShouldEnableSeccompBpf(switches::kRendererProcess)) { |
| 160 // We report whether the sandbox will be activated when renderers go | 157 // We report whether the sandbox will be activated when renderers go |
| 161 // through sandbox initialization. | 158 // through sandbox initialization. |
| 162 sandbox_flags |= kSandboxLinuxSeccompBpf; | 159 sandbox_flags |= kSandboxLinuxSeccompBpf; |
| 163 } | 160 } |
| 164 | 161 |
| 165 return sandbox_flags; | 162 return sandbox_flags; |
| 166 } | 163 } |
| 167 | 164 |
| 165 // Threads are counted via /proc/self/task. This is a little hairy because of | |
| 166 // PID namespaces and existing sandboxes, so "self" must really be used instead | |
| 167 // of using the pid. | |
| 168 bool LinuxSandbox::IsSingleThreaded() const { | 168 bool LinuxSandbox::IsSingleThreaded() const { |
| 169 // TODO(jln): re-implement this properly and use our proc_fd_ if available. | 169 struct stat task_stat; |
| 170 // Possibly racy, but it's ok because this is more of a debug check to catch | 170 int fstat_ret; |
| 171 // new threaded situations arising during development. | 171 if (proc_fd_ >= 0) { |
| 172 file_util::FileEnumerator en(base::FilePath("/proc/self/task"), false, | 172 // If a handle to /proc is available, use it. This allows to bypass file |
| 173 file_util::FileEnumerator::FILES | | 173 // system restrictions. |
| 174 file_util::FileEnumerator::DIRECTORIES); | 174 fstat_ret = fstatat(proc_fd_, "self/task/", &task_stat, 0); |
| 175 bool found_file = false; | 175 } else { |
| 176 while (!en.Next().empty()) { | 176 // Otherwise, make an attempt to access the file system directly. |
| 177 if (found_file) | 177 fstat_ret = fstatat(AT_FDCWD, "/proc/self/task/", &task_stat, 0); |
| 178 return false; // Found a second match. | 178 } |
| 179 found_file = true; | 179 // In Debug mode, it's mandatory to be able to count threads to catch bugs. |
| 180 #if !defined(NDEBUG) | |
| 181 // Using DCHECK here would be incorrect. DCHECK can be enabled in non | |
| 182 // official release mode. | |
| 183 CHECK_EQ(0, fstat_ret) << "Could not count threads, the sandbox was not " | |
| 184 << "pre-initialized properly."; | |
| 185 #endif // !defined(NDEBUG) | |
| 186 if (fstat_ret) { | |
| 187 // Pretend to be monothreaded if it can't be determined (for instance the | |
| 188 // setuid sandbox is already engaged but no proc_fd_ is available). | |
| 189 return true; | |
| 180 } | 190 } |
| 181 | 191 |
| 182 // We pass the test if we found 0 files becase the setuid sandbox will | 192 // At least "..", "." and the current thread should be present. |
| 183 // prevent /proc access in some contexts. | 193 CHECK_LE(3UL, task_stat.st_nlink); |
| 184 return true; | 194 // Counting threads via /proc/self/task could be racy. For the purpose of |
| 195 // determining if the current proces is monothreaded it works: if at any | |
| 196 // time it becomes monothreaded, it'll stay so. | |
| 197 return task_stat.st_nlink == 3; | |
| 185 } | 198 } |
| 186 | 199 |
| 187 bool LinuxSandbox::seccomp_bpf_started() const { | 200 bool LinuxSandbox::seccomp_bpf_started() const { |
| 188 return seccomp_bpf_started_; | 201 return seccomp_bpf_started_; |
| 189 } | 202 } |
| 190 | 203 |
| 191 sandbox::SetuidSandboxClient* | 204 sandbox::SetuidSandboxClient* |
| 192 LinuxSandbox::setuid_sandbox_client() const { | 205 LinuxSandbox::setuid_sandbox_client() const { |
| 193 return setuid_sandbox_client_.get(); | 206 return setuid_sandbox_client_.get(); |
| 194 } | 207 } |
| 195 | 208 |
| 196 // For seccomp-bpf, we use the SandboxSeccompBpf class. | 209 // For seccomp-bpf, we use the SandboxSeccompBpf class. |
| 197 bool LinuxSandbox::StartSeccompBpf(const std::string& process_type) { | 210 bool LinuxSandbox::StartSeccompBpf(const std::string& process_type) { |
| 198 CHECK(!seccomp_bpf_started_); | 211 CHECK(!seccomp_bpf_started_); |
| 199 if (!pre_initialized_) | 212 if (!pre_initialized_) |
| 200 PreinitializeSandbox(process_type); | 213 PreinitializeSandbox(); |
| 201 if (seccomp_bpf_supported()) | 214 if (seccomp_bpf_supported()) |
| 202 seccomp_bpf_started_ = SandboxSeccompBpf::StartSandbox(process_type); | 215 seccomp_bpf_started_ = SandboxSeccompBpf::StartSandbox(process_type); |
| 203 | 216 |
| 204 if (seccomp_bpf_started_) | 217 if (seccomp_bpf_started_) |
| 205 LogSandboxStarted("seccomp-bpf"); | 218 LogSandboxStarted("seccomp-bpf"); |
| 206 | 219 |
| 207 return seccomp_bpf_started_; | 220 return seccomp_bpf_started_; |
| 208 } | 221 } |
| 209 | 222 |
| 210 bool LinuxSandbox::seccomp_bpf_supported() const { | 223 bool LinuxSandbox::seccomp_bpf_supported() const { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 242 const rlim_t kNewDataSegmentMaxSize = std::numeric_limits<int>::max(); | 255 const rlim_t kNewDataSegmentMaxSize = std::numeric_limits<int>::max(); |
| 243 | 256 |
| 244 bool limited_as = AddResourceLimit(RLIMIT_AS, address_space_limit); | 257 bool limited_as = AddResourceLimit(RLIMIT_AS, address_space_limit); |
| 245 bool limited_data = AddResourceLimit(RLIMIT_DATA, kNewDataSegmentMaxSize); | 258 bool limited_data = AddResourceLimit(RLIMIT_DATA, kNewDataSegmentMaxSize); |
| 246 return limited_as && limited_data; | 259 return limited_as && limited_data; |
| 247 #else | 260 #else |
| 248 return false; | 261 return false; |
| 249 #endif // !defined(ADDRESS_SANITIZER) | 262 #endif // !defined(ADDRESS_SANITIZER) |
| 250 } | 263 } |
| 251 | 264 |
| 265 void LinuxSandbox::SealSandbox() { | |
| 266 if (proc_fd_ >= 0) { | |
|
palmer
2013/04/30 21:21:59
Using proc_fd_ < 0 as a signal to express whether
jln (very slow on Chromium)
2013/04/30 22:09:51
No, this would not be correct. We can't close an a
| |
| 267 int ret = HANDLE_EINTR(close(proc_fd_)); | |
| 268 CHECK_EQ(0, ret); | |
| 269 proc_fd_ = -1; | |
| 270 } | |
| 271 } | |
| 272 | |
| 252 } // namespace content | 273 } // namespace content |
| 253 | 274 |
| OLD | NEW |