Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(437)

Side by Side Diff: content/common/sandbox_linux.cc

Issue 14411008: Linux sandbox: more paranoid checks (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Don't assume that DCHECK will only trigger in debug mode. Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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;
Jorge Lucangeli Obes 2013/04/30 17:30:27 So you're doing both the DCHECK and the LOG to alw
jln (very slow on Chromium) 2013/04/30 18:35:20 Yep. If this fall through for any reason (DCHECK r
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
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";
Jorge Lucangeli Obes 2013/04/30 17:30:27 End with a colon.
jln (very slow on Chromium) 2013/04/30 18:35:20 Done.
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 lease "..", "." and the current thread should be present.
Jorge Lucangeli Obes 2013/04/30 17:30:27 least
jln (very slow on Chromium) 2013/04/30 18:35:20 Done.
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 currenet proces is monothreaded it works: if at any
Jorge Lucangeli Obes 2013/04/30 17:30:27 current process
jln (very slow on Chromium) 2013/04/30 18:35:20 Done.
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
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) {
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698