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 // A mini-zygote specifically for Native Client. 6 7 #include "components/nacl/common/nacl_helper_linux.h" 8 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <link.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <sys/socket.h> 15 #include <sys/stat.h> 16 #include <sys/types.h> 17 18 #include <string> 19 #include <vector> 20 21 #include "base/at_exit.h" 22 #include "base/command_line.h" 23 #include "base/json/string_escape.h" 24 #include "base/logging.h" 25 #include "base/message_loop/message_loop.h" 26 #include "base/posix/eintr_wrapper.h" 27 #include "base/posix/global_descriptors.h" 28 #include "base/posix/unix_domain_socket_linux.h" 29 #include "base/rand_util.h" 30 #include "components/nacl/loader/nacl_listener.h" 31 #include "components/nacl/loader/nacl_sandbox_linux.h" 32 #include "crypto/nss_util.h" 33 #include "ipc/ipc_descriptors.h" 34 #include "ipc/ipc_switches.h" 35 #include "sandbox/linux/services/libc_urandom_override.h" 36 37 namespace { 38 39 // The child must mimic the behavior of zygote_main_linux.cc on the child 40 // side of the fork. See zygote_main_linux.cc:HandleForkRequest from 41 // if (!child) { 42 void BecomeNaClLoader(const std::vector<int>& child_fds, 43 size_t prereserved_sandbox_size, 44 int number_of_cores) { 45 VLOG(1) << "NaCl loader: setting up IPC descriptor"; 46 // don't need zygote FD any more 47 if (HANDLE_EINTR(close(kNaClZygoteDescriptor)) != 0) 48 LOG(ERROR) << "close(kNaClZygoteDescriptor) failed."; 49 bool sandbox_initialized = InitializeBpfSandbox(); 50 if (!sandbox_initialized) { 51 LOG(ERROR) << "Could not initialize NaCl's second " 52 << "layer sandbox (seccomp-bpf)."; 53 } 54 base::GlobalDescriptors::GetInstance()->Set(kPrimaryIPCChannel, 55 child_fds[kNaClBrowserFDIndex]); 56 57 base::MessageLoopForIO main_message_loop; 58 NaClListener listener; 59 listener.set_prereserved_sandbox_size(prereserved_sandbox_size); 60 listener.set_number_of_cores(number_of_cores); 61 listener.Listen(); 62 _exit(0); 63 } 64 65 // Some of this code was lifted from 66 // content/browser/zygote_main_linux.cc:ForkWithRealPid() 67 void HandleForkRequest(const std::vector<int>& child_fds, 68 size_t prereserved_sandbox_size, 69 int number_of_cores) { 70 VLOG(1) << "nacl_helper: forking"; 71 pid_t childpid = fork(); 72 if (childpid < 0) { 73 perror("fork"); 74 LOG(ERROR) << "*** HandleForkRequest failed\n"; 75 // fall through to parent case below 76 } else if (childpid == 0) { // In the child process. 77 bool validack = false; 78 const size_t kMaxReadSize = 1024; 79 char buffer[kMaxReadSize]; 80 // Wait until the parent process has discovered our PID. We 81 // should not fork any child processes (which the seccomp 82 // sandbox does) until then, because that can interfere with the 83 // parent's discovery of our PID. 84 const int nread = HANDLE_EINTR(read(child_fds[kNaClParentFDIndex], buffer, 85 kMaxReadSize)); 86 const std::string switch_prefix = std::string("--") + 87 switches::kProcessChannelID + std::string("="); 88 const size_t len = switch_prefix.length(); 89 90 if (nread < 0) { 91 perror("read"); 92 LOG(ERROR) << "read returned " << nread; 93 } else if (nread > static_cast<int>(len)) { 94 if (switch_prefix.compare(0, len, buffer, 0, len) == 0) { 95 VLOG(1) << "NaCl loader is synchronised with Chrome zygote"; 96 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 97 switches::kProcessChannelID, 98 std::string(&buffer[len], nread - len)); 99 validack = true; 100 } 101 } 102 if (HANDLE_EINTR(close(child_fds[kNaClDummyFDIndex])) != 0) 103 LOG(ERROR) << "close(child_fds[kNaClDummyFDIndex]) failed"; 104 if (HANDLE_EINTR(close(child_fds[kNaClParentFDIndex])) != 0) 105 LOG(ERROR) << "close(child_fds[kNaClParentFDIndex]) failed"; 106 if (validack) { 107 BecomeNaClLoader(child_fds, prereserved_sandbox_size, number_of_cores); 108 } else { 109 LOG(ERROR) << "Failed to synch with zygote"; 110 } 111 // NOTREACHED 112 return; 113 } 114 // I am the parent. 115 // First, close the dummy_fd so the sandbox won't find me when 116 // looking for the child's pid in /proc. Also close other fds. 117 for (size_t i = 0; i < child_fds.size(); i++) { 118 if (HANDLE_EINTR(close(child_fds[i])) != 0) 119 LOG(ERROR) << "close(child_fds[i]) failed"; 120 } 121 VLOG(1) << "nacl_helper: childpid is " << childpid; 122 // Now tell childpid to the Chrome zygote. 123 if (HANDLE_EINTR(send(kNaClZygoteDescriptor, 124 &childpid, sizeof(childpid), MSG_EOR)) 125 != sizeof(childpid)) { 126 LOG(ERROR) << "*** send() to zygote failed"; 127 } 128 } 129 130 // This is a poor man's check on whether we are sandboxed. 131 bool IsSandboxed() { 132 int proc_fd = open("/proc/self/exe", O_RDONLY); 133 if (proc_fd >= 0) { 134 HANDLE_EINTR(close(proc_fd)); 135 return false; 136 } 137 return true; 138 } 139 140 } // namespace 141 142 static const char kNaClHelperReservedAtZero[] = "reserved_at_zero"; 143 static const char kNaClHelperRDebug[] = "r_debug"; 144 145 // Since we were started by nacl_helper_bootstrap rather than in the 146 // usual way, the debugger cannot figure out where our executable 147 // or the dynamic linker or the shared libraries are in memory, 148 // so it won't find any symbols. But we can fake it out to find us. 149 // 150 // The zygote passes --r_debug=0xXXXXXXXXXXXXXXXX. 151 // nacl_helper_bootstrap replaces the Xs with the address of its _r_debug 152 // structure. The debugger will look for that symbol by name to 153 // discover the addresses of key dynamic linker data structures. 154 // Since all it knows about is the original main executable, which 155 // is the bootstrap program, it finds the symbol defined there. The 156 // dynamic linker's structure is somewhere else, but it is filled in 157 // after initialization. The parts that really matter to the 158 // debugger never change. So we just copy the contents of the 159 // dynamic linker's structure into the address provided by the option. 160 // Hereafter, if someone attaches a debugger (or examines a core dump), 161 // the debugger will find all the symbols in the normal way. 162 static void CheckRDebug(char* argv0) { 163 std::string r_debug_switch_value = 164 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kNaClHelperRDebug); 165 if (!r_debug_switch_value.empty()) { 166 char* endp; 167 uintptr_t r_debug_addr = strtoul(r_debug_switch_value.c_str(), &endp, 0); 168 if (r_debug_addr != 0 && *endp == '\0') { 169 r_debug* bootstrap_r_debug = reinterpret_cast<r_debug*>(r_debug_addr); 170 *bootstrap_r_debug = _r_debug; 171 172 // Since the main executable (the bootstrap program) does not 173 // have a dynamic section, the debugger will not skip the 174 // first element of the link_map list as it usually would for 175 // an executable or PIE that was loaded normally. But the 176 // dynamic linker has set l_name for the PIE to "" as is 177 // normal for the main executable. So the debugger doesn't 178 // know which file it is. Fill in the actual file name, which 179 // came in as our argv[0]. 180 link_map* l = _r_debug.r_map; 181 if (l->l_name[0] == '\0') 182 l->l_name = argv0; 183 } 184 } 185 } 186 187 // The zygote passes --reserved_at_zero=0xXXXXXXXXXXXXXXXX. 188 // nacl_helper_bootstrap replaces the Xs with the amount of prereserved 189 // sandbox memory. 190 // 191 // CheckReservedAtZero parses the value of the argument reserved_at_zero 192 // and returns the amount of prereserved sandbox memory. 193 static size_t CheckReservedAtZero() { 194 size_t prereserved_sandbox_size = 0; 195 std::string reserved_at_zero_switch_value = 196 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 197 kNaClHelperReservedAtZero); 198 if (!reserved_at_zero_switch_value.empty()) { 199 char* endp; 200 prereserved_sandbox_size = 201 strtoul(reserved_at_zero_switch_value.c_str(), &endp, 0); 202 if (*endp != '\0') 203 LOG(ERROR) << "Could not parse reserved_at_zero argument value of " 204 << reserved_at_zero_switch_value; 205 } 206 return prereserved_sandbox_size; 207 } 208 209 #if defined(ADDRESS_SANITIZER) 210 // Do not install the SIGSEGV handler in ASan. This should make the NaCl 211 // platform qualification test pass. 212 static const char kAsanDefaultOptionsNaCl[] = "handle_segv=0"; 213 214 // Override the default ASan options for the NaCl helper. 215 // __asan_default_options should not be instrumented, because it is called 216 // before ASan is initialized. 217 extern "C" 218 __attribute__((no_address_safety_analysis)) 219 const char* __asan_default_options() { 220 return kAsanDefaultOptionsNaCl; 221 } 222 #endif 223 224 int main(int argc, char* argv[]) { 225 CommandLine::Init(argc, argv); 226 base::AtExitManager exit_manager; 227 base::RandUint64(); // acquire /dev/urandom fd before sandbox is raised 228 // Allows NSS to fopen() /dev/urandom. 229 sandbox::InitLibcUrandomOverrides(); 230 #if defined(USE_NSS) 231 // Configure NSS for use inside the NaCl process. 232 // The fork check has not caused problems for NaCl, but this appears to be 233 // best practice (see other places LoadNSSLibraries is called.) 234 crypto::DisableNSSForkCheck(); 235 // Without this line on Linux, HMAC::Init will instantiate a singleton that 236 // in turn attempts to open a file. Disabling this behavior avoids a ~70 ms 237 // stall the first time HMAC is used. 238 crypto::ForceNSSNoDBInit(); 239 // Load shared libraries before sandbox is raised. 240 // NSS is needed to perform hashing for validation caching. 241 crypto::LoadNSSLibraries(); 242 #endif 243 std::vector<int> empty; // for SendMsg() calls 244 size_t prereserved_sandbox_size = CheckReservedAtZero(); 245 int number_of_cores = sysconf(_SC_NPROCESSORS_ONLN); 246 247 CheckRDebug(argv[0]); 248 249 // Check that IsSandboxed() works. We should not be sandboxed at this point. 250 CHECK(!IsSandboxed()) << "Unexpectedly sandboxed!"; 251 252 // Send the zygote a message to let it know we are ready to help 253 if (!UnixDomainSocket::SendMsg(kNaClZygoteDescriptor, 254 kNaClHelperStartupAck, 255 sizeof(kNaClHelperStartupAck), empty)) { 256 LOG(ERROR) << "*** send() to zygote failed"; 257 } 258 259 while (true) { 260 int badpid = -1; 261 std::vector<int> fds; 262 static const unsigned kMaxMessageLength = 2048; 263 char buf[kMaxMessageLength]; 264 const ssize_t msglen = UnixDomainSocket::RecvMsg(kNaClZygoteDescriptor, 265 &buf, sizeof(buf), &fds); 266 // If the Zygote has started handling requests, we should be sandboxed via 267 // the setuid sandbox. 268 if (!IsSandboxed()) { 269 LOG(ERROR) << "NaCl helper process running without a sandbox!\n" 270 << "Most likely you need to configure your SUID sandbox " 271 << "correctly"; 272 } 273 if (msglen == 0 || (msglen == -1 && errno == ECONNRESET)) { 274 // EOF from the browser. Goodbye! 275 _exit(0); 276 } else if (msglen < 0) { 277 LOG(ERROR) << "nacl_helper: receive from zygote failed, errno = " 278 << errno; 279 } else if (msglen == sizeof(kNaClForkRequest) - 1 && 280 memcmp(buf, kNaClForkRequest, msglen) == 0) { 281 if (kNaClParentFDIndex + 1 == fds.size()) { 282 HandleForkRequest(fds, prereserved_sandbox_size, number_of_cores); 283 continue; // fork succeeded. Note: child does not return 284 } else { 285 LOG(ERROR) << "nacl_helper: unexpected number of fds, got " 286 << fds.size(); 287 } 288 } else { 289 LOG(ERROR) << "nacl_helper unrecognized request: " 290 << base::GetDoubleQuotedJson(std::string(buf, buf + msglen)); 291 _exit(-1); 292 } 293 // if fork fails, send PID=-1 to zygote 294 if (!UnixDomainSocket::SendMsg(kNaClZygoteDescriptor, &badpid, 295 sizeof(badpid), empty)) { 296 LOG(ERROR) << "*** send() to zygote failed"; 297 } 298 } 299 CHECK(false); // This routine must not return 300 } 301