blob: 1746390e84176e08add63a17a9adb68c2c5daaed [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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
3// found in the LICENSE file.
4
5#include <fcntl.h>
6#include <sys/resource.h>
7#include <sys/stat.h>
8#include <sys/time.h>
9#include <sys/types.h>
10
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000011#include <limits>
12
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010013#include "base/bind.h"
14#include "base/bind_helpers.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000015#include "base/command_line.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000016#include "base/logging.h"
17#include "base/memory/singleton.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000018#include "base/posix/eintr_wrapper.h"
Ben Murdocheb525c52013-07-10 11:40:50 +010019#include "base/time/time.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000020#include "content/common/sandbox_linux.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000021#include "content/common/sandbox_seccomp_bpf_linux.h"
22#include "content/public/common/content_switches.h"
23#include "content/public/common/sandbox_linux.h"
24#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
25
26namespace {
27
28void LogSandboxStarted(const std::string& sandbox_name) {
29 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
30 const std::string process_type =
31 command_line.GetSwitchValueASCII(switches::kProcessType);
32 const std::string activated_sandbox =
33 "Activated " + sandbox_name + " sandbox for process type: " +
34 process_type + ".";
35#if defined(OS_CHROMEOS)
36 LOG(WARNING) << activated_sandbox;
37#else
38 VLOG(1) << activated_sandbox;
39#endif
40}
41
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000042bool AddResourceLimit(int resource, rlim_t limit) {
43 struct rlimit old_rlimit;
44 if (getrlimit(resource, &old_rlimit))
45 return false;
46 // Make sure we don't raise the existing limit.
47 const struct rlimit new_rlimit = {
48 std::min(old_rlimit.rlim_cur, limit),
49 std::min(old_rlimit.rlim_max, limit)
50 };
51 int rc = setrlimit(resource, &new_rlimit);
52 return rc == 0;
53}
54
Ben Murdocheb525c52013-07-10 11:40:50 +010055bool IsRunningTSAN() {
56#if defined(THREAD_SANITIZER)
57 return true;
58#else
59 return false;
60#endif
61}
62
Torne (Richard Coles)58218062012-11-14 11:43:16 +000063} // namespace
64
65namespace content {
66
67LinuxSandbox::LinuxSandbox()
68 : proc_fd_(-1),
69 seccomp_bpf_started_(false),
70 pre_initialized_(false),
Torne (Richard Coles)58218062012-11-14 11:43:16 +000071 seccomp_bpf_supported_(false),
72 setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) {
73 if (setuid_sandbox_client_ == NULL) {
74 LOG(FATAL) << "Failed to instantiate the setuid sandbox client.";
75 }
76}
77
78LinuxSandbox::~LinuxSandbox() {
79}
80
81LinuxSandbox* LinuxSandbox::GetInstance() {
82 LinuxSandbox* instance = Singleton<LinuxSandbox>::get();
83 CHECK(instance);
84 return instance;
85}
86
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000087#if defined(ADDRESS_SANITIZER) && defined(OS_LINUX)
88// ASan API call to notify the tool the sandbox is going to be turned on.
89extern "C" void __sanitizer_sandbox_on_notify(void *reserved);
90#endif
91
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010092void LinuxSandbox::PreinitializeSandbox() {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000093 CHECK(!pre_initialized_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000094 seccomp_bpf_supported_ = false;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000095#if defined(ADDRESS_SANITIZER) && defined(OS_LINUX)
96 // ASan needs to open some resources before the sandbox is enabled.
97 // This should not fork, not launch threads, not open a directory.
98 __sanitizer_sandbox_on_notify(/*reserved*/NULL);
99#endif
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100100
101#if !defined(NDEBUG)
102 // Open proc_fd_ only in Debug mode so that forgetting to close it doesn't
103 // produce a sandbox escape in Release mode.
104 proc_fd_ = open("/proc", O_DIRECTORY | O_RDONLY);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100105 CHECK_GE(proc_fd_, 0);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100106#endif // !defined(NDEBUG)
107 // We "pre-warm" the code that detects supports for seccomp BPF.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000108 if (SandboxSeccompBpf::IsSeccompBpfDesired()) {
109 if (!SandboxSeccompBpf::SupportsSandbox()) {
110 VLOG(1) << "Lacking support for seccomp-bpf sandbox.";
111 } else {
112 seccomp_bpf_supported_ = true;
113 }
114 }
115 pre_initialized_ = true;
116}
117
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100118bool LinuxSandbox::InitializeSandbox() {
119 bool seccomp_bpf_started = false;
120 LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance();
121 // We need to make absolutely sure that our sandbox is "sealed" before
122 // InitializeSandbox does exit.
123 base::ScopedClosureRunner sandbox_sealer(
124 base::Bind(&LinuxSandbox::SealSandbox, base::Unretained(linux_sandbox)));
125 const std::string process_type =
126 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
127 switches::kProcessType);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000128
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100129 // No matter what, it's always an error to call InitializeSandbox() after
130 // threads have been created.
131 if (!linux_sandbox->IsSingleThreaded()) {
132 std::string error_message = "InitializeSandbox() called with multiple "
133 "threads in process " + process_type;
Ben Murdocheb525c52013-07-10 11:40:50 +0100134 // TSAN starts a helper thread. So we don't start the sandbox and don't
135 // even report an error about it.
136 if (IsRunningTSAN())
137 return false;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100138 // The GPU process is allowed to call InitializeSandbox() with threads for
139 // now, because it loads third party libraries.
140 if (process_type != switches::kGpuProcess)
Ben Murdochbb1529c2013-08-08 10:24:53 +0100141 CHECK(false) << error_message;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100142 LOG(ERROR) << error_message;
143 return false;
144 }
145
146 // Attempt to limit the future size of the address space of the process.
147 linux_sandbox->LimitAddressSpace(process_type);
148
149 // First, try to enable seccomp-bpf.
150 seccomp_bpf_started = linux_sandbox->StartSeccompBpf(process_type);
151
152 return seccomp_bpf_started;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000153}
154
155int LinuxSandbox::GetStatus() const {
156 CHECK(pre_initialized_);
157 int sandbox_flags = 0;
158 if (setuid_sandbox_client_->IsSandboxed()) {
159 sandbox_flags |= kSandboxLinuxSUID;
160 if (setuid_sandbox_client_->IsInNewPIDNamespace())
161 sandbox_flags |= kSandboxLinuxPIDNS;
162 if (setuid_sandbox_client_->IsInNewNETNamespace())
163 sandbox_flags |= kSandboxLinuxNetNS;
164 }
165
166 if (seccomp_bpf_supported() &&
167 SandboxSeccompBpf::ShouldEnableSeccompBpf(switches::kRendererProcess)) {
168 // We report whether the sandbox will be activated when renderers go
169 // through sandbox initialization.
170 sandbox_flags |= kSandboxLinuxSeccompBpf;
171 }
172
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000173 return sandbox_flags;
174}
175
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100176// Threads are counted via /proc/self/task. This is a little hairy because of
177// PID namespaces and existing sandboxes, so "self" must really be used instead
178// of using the pid.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000179bool LinuxSandbox::IsSingleThreaded() const {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100180 struct stat task_stat;
181 int fstat_ret;
182 if (proc_fd_ >= 0) {
183 // If a handle to /proc is available, use it. This allows to bypass file
184 // system restrictions.
185 fstat_ret = fstatat(proc_fd_, "self/task/", &task_stat, 0);
186 } else {
187 // Otherwise, make an attempt to access the file system directly.
188 fstat_ret = fstatat(AT_FDCWD, "/proc/self/task/", &task_stat, 0);
189 }
190 // In Debug mode, it's mandatory to be able to count threads to catch bugs.
191#if !defined(NDEBUG)
192 // Using DCHECK here would be incorrect. DCHECK can be enabled in non
193 // official release mode.
194 CHECK_EQ(0, fstat_ret) << "Could not count threads, the sandbox was not "
195 << "pre-initialized properly.";
196#endif // !defined(NDEBUG)
197 if (fstat_ret) {
198 // Pretend to be monothreaded if it can't be determined (for instance the
199 // setuid sandbox is already engaged but no proc_fd_ is available).
200 return true;
201 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000202
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100203 // At least "..", "." and the current thread should be present.
204 CHECK_LE(3UL, task_stat.st_nlink);
205 // Counting threads via /proc/self/task could be racy. For the purpose of
206 // determining if the current proces is monothreaded it works: if at any
207 // time it becomes monothreaded, it'll stay so.
208 return task_stat.st_nlink == 3;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000209}
210
211bool LinuxSandbox::seccomp_bpf_started() const {
212 return seccomp_bpf_started_;
213}
214
215sandbox::SetuidSandboxClient*
216 LinuxSandbox::setuid_sandbox_client() const {
217 return setuid_sandbox_client_.get();
218}
219
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000220// For seccomp-bpf, we use the SandboxSeccompBpf class.
221bool LinuxSandbox::StartSeccompBpf(const std::string& process_type) {
222 CHECK(!seccomp_bpf_started_);
223 if (!pre_initialized_)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100224 PreinitializeSandbox();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000225 if (seccomp_bpf_supported())
226 seccomp_bpf_started_ = SandboxSeccompBpf::StartSandbox(process_type);
227
228 if (seccomp_bpf_started_)
229 LogSandboxStarted("seccomp-bpf");
230
231 return seccomp_bpf_started_;
232}
233
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000234bool LinuxSandbox::seccomp_bpf_supported() const {
235 CHECK(pre_initialized_);
236 return seccomp_bpf_supported_;
237}
238
239bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) {
240 (void) process_type;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000241#if !defined(ADDRESS_SANITIZER)
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000242 CommandLine* command_line = CommandLine::ForCurrentProcess();
243 if (command_line->HasSwitch(switches::kNoSandbox)) {
244 return false;
245 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000246
247 // Limit the address space to 4GB.
248 // This is in the hope of making some kernel exploits more complex and less
249 // reliable. It also limits sprays a little on 64-bit.
250 rlim_t address_space_limit = std::numeric_limits<uint32_t>::max();
251#if defined(__LP64__)
252 // On 64 bits, V8 and possibly others will reserve massive memory ranges and
253 // rely on on-demand paging for allocation. Unfortunately, even
254 // MADV_DONTNEED ranges count towards RLIMIT_AS so this is not an option.
255 // See crbug.com/169327 for a discussion.
256 // For now, increase limit to 16GB for renderer and worker processes to
257 // accomodate.
258 if (process_type == switches::kRendererProcess ||
259 process_type == switches::kWorkerProcess) {
260 address_space_limit = 1L << 34;
261 }
262#endif // defined(__LP64__)
263
264 // On all platforms, add a limit to the brk() heap that would prevent
265 // allocations that can't be index by an int.
266 const rlim_t kNewDataSegmentMaxSize = std::numeric_limits<int>::max();
267
268 bool limited_as = AddResourceLimit(RLIMIT_AS, address_space_limit);
269 bool limited_data = AddResourceLimit(RLIMIT_DATA, kNewDataSegmentMaxSize);
270 return limited_as && limited_data;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000271#else
272 return false;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000273#endif // !defined(ADDRESS_SANITIZER)
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000274}
275
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100276void LinuxSandbox::SealSandbox() {
277 if (proc_fd_ >= 0) {
278 int ret = HANDLE_EINTR(close(proc_fd_));
279 CHECK_EQ(0, ret);
280 proc_fd_ = -1;
281 }
282}
283
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000284} // namespace content
285