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 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 11 #include <limits> 12 13 #include "base/bind.h" 14 #include "base/bind_helpers.h" 15 #include "base/command_line.h" 16 #include "base/logging.h" 17 #include "base/memory/singleton.h" 18 #include "base/posix/eintr_wrapper.h" 19 #include "base/time/time.h" 20 #include "content/common/sandbox_linux.h" 21 #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 26 namespace { 27 28 void 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 42 bool 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 55 bool IsRunningTSAN() { 56 #if defined(THREAD_SANITIZER) 57 return true; 58 #else 59 return false; 60 #endif 61 } 62 63 } // namespace 64 65 namespace content { 66 67 LinuxSandbox::LinuxSandbox() 68 : proc_fd_(-1), 69 seccomp_bpf_started_(false), 70 pre_initialized_(false), 71 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 78 LinuxSandbox::~LinuxSandbox() { 79 } 80 81 LinuxSandbox* LinuxSandbox::GetInstance() { 82 LinuxSandbox* instance = Singleton<LinuxSandbox>::get(); 83 CHECK(instance); 84 return instance; 85 } 86 87 #if defined(ADDRESS_SANITIZER) && defined(OS_LINUX) 88 // ASan API call to notify the tool the sandbox is going to be turned on. 89 extern "C" void __sanitizer_sandbox_on_notify(void *reserved); 90 #endif 91 92 void LinuxSandbox::PreinitializeSandbox() { 93 CHECK(!pre_initialized_); 94 seccomp_bpf_supported_ = false; 95 #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 100 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); 105 CHECK_GE(proc_fd_, 0); 106 #endif // !defined(NDEBUG) 107 // We "pre-warm" the code that detects supports for seccomp BPF. 108 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 118 bool 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); 128 129 // 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; 134 // 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; 138 // 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) 141 CHECK(false) << error_message; 142 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; 153 } 154 155 int 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 173 return sandbox_flags; 174 } 175 176 // 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. 179 bool LinuxSandbox::IsSingleThreaded() const { 180 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 } 202 203 // 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; 209 } 210 211 bool LinuxSandbox::seccomp_bpf_started() const { 212 return seccomp_bpf_started_; 213 } 214 215 sandbox::SetuidSandboxClient* 216 LinuxSandbox::setuid_sandbox_client() const { 217 return setuid_sandbox_client_.get(); 218 } 219 220 // For seccomp-bpf, we use the SandboxSeccompBpf class. 221 bool LinuxSandbox::StartSeccompBpf(const std::string& process_type) { 222 CHECK(!seccomp_bpf_started_); 223 if (!pre_initialized_) 224 PreinitializeSandbox(); 225 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 234 bool LinuxSandbox::seccomp_bpf_supported() const { 235 CHECK(pre_initialized_); 236 return seccomp_bpf_supported_; 237 } 238 239 bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) { 240 (void) process_type; 241 #if !defined(ADDRESS_SANITIZER) 242 CommandLine* command_line = CommandLine::ForCurrentProcess(); 243 if (command_line->HasSwitch(switches::kNoSandbox)) { 244 return false; 245 } 246 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; 271 #else 272 return false; 273 #endif // !defined(ADDRESS_SANITIZER) 274 } 275 276 void 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 284 } // namespace content 285 286