diff options
author | mdempsky@chromium.org <mdempsky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-08 01:26:28 +0000 |
---|---|---|
committer | mdempsky@chromium.org <mdempsky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-08 01:26:28 +0000 |
commit | 53cbbe25aba92ee69a80f28f3e3c2201e1d87d98 (patch) | |
tree | 273509ef85f2e275a426e835acf73d04a32aa7f0 | |
parent | e8d007a131358658444af55a899b55771d958c5c (diff) | |
download | chromium_src-53cbbe25aba92ee69a80f28f3e3c2201e1d87d98.zip chromium_src-53cbbe25aba92ee69a80f28f3e3c2201e1d87d98.tar.gz chromium_src-53cbbe25aba92ee69a80f28f3e3c2201e1d87d98.tar.bz2 |
Use RecvMsgWithPid to find real PID for zygote children
The new PID discovery protocol now works as follows:
1. The ZygoteHost allocates a UNIX socket pair and includes one end
with its fork request to the zygote process.
2. After forking, the zygote child process sends a message over the
UNIX socket pair.
3. The ZygoteHost receives the message and a PID for the sender (i.e.,
child), and writes the PID back over the control socket to the zygote.
If the zygote fails to fork a child, its end of the socket pair will
be closed, and the ZygoteHost will receive EOF instead of a message.
4. In the non-NaCl case, the zygote writes the child's PID over a pipe
for the child to receive. (In the NaCl case, this pipe is no longer
used and no PID value is sent to the NaCl child process.)
5. Finally, the zygote sends its normal pickled response back to the
ZygoteHost.
Two manual tests to make sure this works:
1. Make sure that the "End Process" button in the "Task Manager" dialog
still terminates renderer and NaCl processes correctly under Linux.
2. Make sure that Linux Chrome release builds gracefully fail (i.e.,
don't crash) when nacl_helper is missing at chrome startup and the user
navigates to a page that uses NaCl; e.g.,
http://gonativeclient.appspot.com/demo/ and click "Game of Life".
BUG=357670
TEST=manual: see above
NOTRY=true
Review URL: https://codereview.chromium.org/269543014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269011 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | components/nacl/loader/nacl_helper_linux.cc | 44 | ||||
-rw-r--r-- | content/browser/zygote_host/zygote_host_impl_linux.cc | 55 | ||||
-rw-r--r-- | content/common/child_process_sandbox_support_impl_linux.cc | 8 | ||||
-rw-r--r-- | content/common/zygote_commands_linux.h | 10 | ||||
-rw-r--r-- | content/public/common/child_process_sandbox_support_linux.h | 4 | ||||
-rw-r--r-- | content/public/common/zygote_fork_delegate_linux.h | 10 | ||||
-rw-r--r-- | content/zygote/zygote_linux.cc | 212 | ||||
-rw-r--r-- | content/zygote/zygote_linux.h | 10 |
8 files changed, 210 insertions, 143 deletions
diff --git a/components/nacl/loader/nacl_helper_linux.cc b/components/nacl/loader/nacl_helper_linux.cc index eaf6204..e170c52 100644 --- a/components/nacl/loader/nacl_helper_linux.cc +++ b/components/nacl/loader/nacl_helper_linux.cc @@ -36,6 +36,7 @@ #include "components/nacl/loader/nacl_listener.h" #include "components/nacl/loader/nonsfi/irt_exception_handling.h" #include "components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h" +#include "content/public/common/child_process_sandbox_support_linux.h" #include "content/public/common/content_descriptors.h" #include "content/public/common/zygote_fork_delegate_linux.h" #include "crypto/nss_util.h" @@ -114,41 +115,24 @@ void ChildNaClLoaderInit(ScopedVector<base::ScopedFD> child_fds, bool uses_nonsfi_mode, nacl::NaClSandbox* nacl_sandbox, const std::string& channel_id) { - bool validack = false; - base::ProcessId real_pid; - // Wait until the parent process has discovered our PID. We - // should not fork any child processes (which the seccomp - // sandbox does) until then, because that can interfere with the - // parent's discovery of our PID. - const ssize_t nread = HANDLE_EINTR( - read(child_fds[content::ZygoteForkDelegate::kParentFDIndex]->get(), - &real_pid, - sizeof(real_pid))); - if (static_cast<size_t>(nread) == sizeof(real_pid)) { - // Make sure the parent didn't accidentally send us our real PID. - // We don't want it to be discoverable anywhere in our address space - // when we start running untrusted code. - CHECK(real_pid == 0); - - CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kProcessChannelID, channel_id); - validack = true; - } else { - if (nread < 0) - perror("read"); - LOG(ERROR) << "read returned " << nread; - } + DCHECK(child_fds.size() > + std::max(content::ZygoteForkDelegate::kPIDOracleFDIndex, + content::ZygoteForkDelegate::kBrowserFDIndex)); + + // Ping the PID oracle socket. + CHECK(content::SendZygoteChildPing( + child_fds[content::ZygoteForkDelegate::kPIDOracleFDIndex]->get())); + CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kProcessChannelID, channel_id); + + // Save the browser socket and close the rest. base::ScopedFD browser_fd( child_fds[content::ZygoteForkDelegate::kBrowserFDIndex]->Pass()); child_fds.clear(); - if (validack) { - BecomeNaClLoader( - browser_fd.Pass(), system_info, uses_nonsfi_mode, nacl_sandbox); - } else { - LOG(ERROR) << "Failed to synch with zygote"; - } + BecomeNaClLoader( + browser_fd.Pass(), system_info, uses_nonsfi_mode, nacl_sandbox); _exit(1); } diff --git a/content/browser/zygote_host/zygote_host_impl_linux.cc b/content/browser/zygote_host/zygote_host_impl_linux.cc index 8a28f46..9d63ad9 100644 --- a/content/browser/zygote_host/zygote_host_impl_linux.cc +++ b/content/browser/zygote_host/zygote_host_impl_linux.cc @@ -294,6 +294,12 @@ pid_t ZygoteHostImpl::ForkRequest( DCHECK(init_); Pickle pickle; + int raw_socks[2]; + PCHECK(0 == socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_socks)); + base::ScopedFD my_sock(raw_socks[0]); + base::ScopedFD peer_sock(raw_socks[1]); + CHECK(UnixDomainSocket::EnableReceiveProcessId(my_sock.get())); + pickle.WriteInt(kZygoteCommandFork); pickle.WriteString(process_type); pickle.WriteInt(argv.size()); @@ -301,12 +307,19 @@ pid_t ZygoteHostImpl::ForkRequest( i = argv.begin(); i != argv.end(); ++i) pickle.WriteString(*i); - pickle.WriteInt(mapping.size()); + // Fork requests contain one file descriptor for the PID oracle, and one + // more for each file descriptor mapping for the child process. + const size_t num_fds_to_send = 1 + mapping.size(); + pickle.WriteInt(num_fds_to_send); std::vector<int> fds; - // Scoped pointers cannot be stored in containers, so we have to use a - // linked_ptr. - std::vector<linked_ptr<base::ScopedFD> > autodelete_fds; + ScopedVector<base::ScopedFD> autoclose_fds; + + // First FD to send is peer_sock. + fds.push_back(peer_sock.get()); + autoclose_fds.push_back(new base::ScopedFD(peer_sock.Pass())); + + // The rest come from mapping. for (std::vector<FileDescriptorInfo>::const_iterator i = mapping.begin(); i != mapping.end(); ++i) { pickle.WriteUInt32(i->id); @@ -314,16 +327,46 @@ pid_t ZygoteHostImpl::ForkRequest( if (i->fd.auto_close) { // Auto-close means we need to close the FDs after they have been passed // to the other process. - linked_ptr<base::ScopedFD> ptr(new base::ScopedFD(fds.back())); - autodelete_fds.push_back(ptr); + autoclose_fds.push_back(new base::ScopedFD(i->fd.fd)); } } + // Sanity check that we've populated |fds| correctly. + DCHECK_EQ(num_fds_to_send, fds.size()); + pid_t pid; { base::AutoLock lock(control_lock_); if (!SendMessage(pickle, &fds)) return base::kNullProcessHandle; + autoclose_fds.clear(); + + { + char buf[sizeof(kZygoteChildPingMessage) + 1]; + ScopedVector<base::ScopedFD> recv_fds; + base::ProcessId real_pid; + + ssize_t n = UnixDomainSocket::RecvMsgWithPid( + my_sock.get(), buf, sizeof(buf), &recv_fds, &real_pid); + if (n != sizeof(kZygoteChildPingMessage) || + 0 != memcmp(buf, + kZygoteChildPingMessage, + sizeof(kZygoteChildPingMessage))) { + // Zygote children should still be trustworthy when they're supposed to + // ping us, so something's broken if we don't receive a valid ping. + LOG(ERROR) << "Did not receive ping from zygote child"; + NOTREACHED(); + real_pid = -1; + } + my_sock.reset(); + + // Always send PID back to zygote. + Pickle pid_pickle; + pid_pickle.WriteInt(kZygoteCommandForkRealPID); + pid_pickle.WriteInt(real_pid); + if (!SendMessage(pid_pickle, NULL)) + return base::kNullProcessHandle; + } // Read the reply, which pickles the PID and an optional UMA enumeration. static const unsigned kMaxReplyLength = 2048; diff --git a/content/common/child_process_sandbox_support_impl_linux.cc b/content/common/child_process_sandbox_support_impl_linux.cc index a810e5c4..c0edcfc 100644 --- a/content/common/child_process_sandbox_support_impl_linux.cc +++ b/content/common/child_process_sandbox_support_impl_linux.cc @@ -14,6 +14,7 @@ #include "base/posix/unix_domain_socket_linux.h" #include "base/sys_byteorder.h" #include "content/common/sandbox_linux/sandbox_linux.h" +#include "content/common/zygote_commands_linux.h" #include "third_party/WebKit/public/platform/linux/WebFontFamily.h" #include "third_party/WebKit/public/platform/linux/WebFontRenderStyle.h" @@ -181,4 +182,11 @@ bool GetFontTable(int fd, uint32_t table_tag, off_t offset, return true; } +bool SendZygoteChildPing(int fd) { + return UnixDomainSocket::SendMsg(fd, + kZygoteChildPingMessage, + sizeof(kZygoteChildPingMessage), + std::vector<int>()); +} + } // namespace content diff --git a/content/common/zygote_commands_linux.h b/content/common/zygote_commands_linux.h index 9be0462..ca5a1d5 100644 --- a/content/common/zygote_commands_linux.h +++ b/content/common/zygote_commands_linux.h @@ -18,6 +18,10 @@ static const char kZygoteBootMessage[] = "ZYGOTE_BOOT"; // is ready to go. static const char kZygoteHelloMessage[] = "ZYGOTE_OK"; +// Message sent by zygote children to the browser so the browser can discover +// the sending child's process ID. +static const char kZygoteChildPingMessage[] = "CHILD_PING"; + // Maximum allowable length for messages sent to the zygote. const size_t kZygoteMaxMessageLength = 8192; @@ -38,7 +42,11 @@ enum { kZygoteCommandGetTerminationStatus = 2, // Read a bitmask of kSandboxLinux* - kZygoteCommandGetSandboxStatus = 3 + kZygoteCommandGetSandboxStatus = 3, + + // Not a real zygote command, but a subcommand used during the zygote fork + // protocol. Sends the child's PID as seen from the browser process. + kZygoteCommandForkRealPID = 4 }; } // namespace content diff --git a/content/public/common/child_process_sandbox_support_linux.h b/content/public/common/child_process_sandbox_support_linux.h index 9e1cde2..58dc3ea 100644 --- a/content/public/common/child_process_sandbox_support_linux.h +++ b/content/public/common/child_process_sandbox_support_linux.h @@ -50,6 +50,10 @@ CONTENT_EXPORT int MatchFontWithFallback( CONTENT_EXPORT bool GetFontTable(int fd, uint32_t table_tag, off_t offset, uint8_t* output, size_t* output_length); +// Sends a zygote child "ping" message to browser process via socket |fd|. +// Returns true on success. +CONTENT_EXPORT bool SendZygoteChildPing(int fd); + }; // namespace content #endif // CONTENT_PUBLIC_COMMON_CHILD_PROCESS_SANDBOX_SUPPORT_LINUX_H_ diff --git a/content/public/common/zygote_fork_delegate_linux.h b/content/public/common/zygote_fork_delegate_linux.h index 39833c5..66f99cd 100644 --- a/content/public/common/zygote_fork_delegate_linux.h +++ b/content/public/common/zygote_fork_delegate_linux.h @@ -49,11 +49,11 @@ class ZygoteForkDelegate { enum { // Used to pass in the descriptor for talking to the Browser kBrowserFDIndex, - // The next two are used in the protocol for discovering the - // child processes real PID from within the SUID sandbox. See - // http://code.google.com/p/chromium/wiki/LinuxZygote - kDummyFDIndex, - kParentFDIndex, + // The PID oracle is used in the protocol for discovering the + // child process's real PID from within the SUID sandbox. + // The child process is required to write to the socket after + // successfully forking. + kPIDOracleFDIndex, kNumPassedFDs // Number of FDs in the vector passed to Fork(). }; diff --git a/content/zygote/zygote_linux.cc b/content/zygote/zygote_linux.cc index 67b071c..5f922a4 100644 --- a/content/zygote/zygote_linux.cc +++ b/content/zygote/zygote_linux.cc @@ -50,6 +50,30 @@ int LookUpFd(const base::GlobalDescriptors::Mapping& fd_mapping, uint32_t key) { return -1; } +void CreatePipe(base::ScopedFD* read_pipe, base::ScopedFD* write_pipe) { + int raw_pipe[2]; + PCHECK(0 == pipe(raw_pipe)); + read_pipe->reset(raw_pipe[0]); + write_pipe->reset(raw_pipe[1]); +} + +void KillAndReap(pid_t pid, bool use_helper) { + if (use_helper) { + // Helper children may be forked in another PID namespace, so |pid| might + // be meaningless to us; or we just might not be able to directly send it + // signals. So we can't kill it. + // Additionally, we're not its parent, so we can't reap it anyway. + // TODO(mdempsky): Extend the ZygoteForkDelegate API to handle this. + LOG(WARNING) << "Unable to kill or reap helper children"; + return; + } + + // Kill the child process in case it's not already dead, so we can safely + // perform a blocking wait. + PCHECK(0 == kill(pid, SIGKILL)); + PCHECK(pid == HANDLE_EINTR(waitpid(pid, NULL, 0))); +} + } // namespace Zygote::Zygote(int sandbox_flags, @@ -161,6 +185,13 @@ bool Zygote::HandleRequestFromBrowser(int fd) { case kZygoteCommandGetSandboxStatus: HandleGetSandboxStatus(fd, pickle, iter); return false; + case kZygoteCommandForkRealPID: + // This shouldn't happen in practice, but some failure paths in + // HandleForkRequest (e.g., if ReadArgsAndFork fails during depickling) + // could leave this command pending on the socket. + LOG(ERROR) << "Unexpected real PID message from browser"; + NOTREACHED(); + return false; default: NOTREACHED(); break; @@ -295,6 +326,7 @@ void Zygote::HandleGetTerminationStatus(int fd, int Zygote::ForkWithRealPid(const std::string& process_type, const base::GlobalDescriptors::Mapping& fd_mapping, const std::string& channel_id, + base::ScopedFD pid_oracle, std::string* uma_name, int* uma_sample, int* uma_boundary_value) { @@ -302,50 +334,38 @@ int Zygote::ForkWithRealPid(const std::string& process_type, uma_name, uma_sample, uma_boundary_value)); - int dummy_fd; - ino_t dummy_inode; - int pipe_fds[2] = { -1, -1 }; - base::ProcessId pid = 0; - - dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); - if (dummy_fd < 0) { - LOG(ERROR) << "Failed to create dummy FD"; - goto error; - } - if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) { - LOG(ERROR) << "Failed to get inode for dummy FD"; - goto error; - } - if (pipe(pipe_fds) != 0) { - LOG(ERROR) << "Failed to create pipe"; - goto error; - } + base::ScopedFD read_pipe, write_pipe; + base::ProcessId pid = 0; if (use_helper) { - std::vector<int> fds; int ipc_channel_fd = LookUpFd(fd_mapping, kPrimaryIPCChannel); if (ipc_channel_fd < 0) { DLOG(ERROR) << "Failed to find kPrimaryIPCChannel in FD mapping"; - goto error; + return -1; } + std::vector<int> fds; fds.push_back(ipc_channel_fd); // kBrowserFDIndex - fds.push_back(dummy_fd); // kDummyFDIndex - fds.push_back(pipe_fds[0]); // kParentFDIndex + fds.push_back(pid_oracle.get()); // kPIDOracleFDIndex pid = helper_->Fork(process_type, fds, channel_id); + + // Helpers should never return in the child process. + CHECK_NE(pid, 0); } else { + CreatePipe(&read_pipe, &write_pipe); pid = fork(); } - if (pid < 0) { - goto error; - } else if (pid == 0) { + + if (pid == 0) { // In the child process. - close(pipe_fds[1]); + write_pipe.reset(); + + // Ping the PID oracle socket so the browser can find our PID. + CHECK(SendZygoteChildPing(pid_oracle.get())); + + // Now read back our real PID from the zygote. base::ProcessId real_pid; - // Wait until the parent process has discovered our PID. We - // should not fork any child processes (which the seccomp - // sandbox does) until then, because that can interfere with the - // parent's discovery of our PID. - if (!base::ReadFromFD(pipe_fds[0], reinterpret_cast<char*>(&real_pid), + if (!base::ReadFromFD(read_pipe.get(), + reinterpret_cast<char*>(&real_pid), sizeof(real_pid))) { LOG(FATAL) << "Failed to synchronise with parent zygote process"; } @@ -361,78 +381,64 @@ int Zygote::ForkWithRealPid(const std::string& process_type, base::debug::TraceLog::GetInstance()->SetProcessID( static_cast<int>(real_pid)); #endif - close(pipe_fds[0]); - close(dummy_fd); return 0; - } else { - // In the parent process. - close(dummy_fd); - dummy_fd = -1; - close(pipe_fds[0]); - pipe_fds[0] = -1; - base::ProcessId real_pid; - if (UsingSUIDSandbox()) { - uint8_t reply_buf[512]; - Pickle request; - request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE); - request.WriteUInt64(dummy_inode); - - const ssize_t r = UnixDomainSocket::SendRecvMsg( - GetSandboxFD(), reply_buf, sizeof(reply_buf), NULL, - request); - if (r == -1) { - LOG(ERROR) << "Failed to get child process's real PID"; - goto error; - } - - Pickle reply(reinterpret_cast<char*>(reply_buf), r); - PickleIterator iter(reply); - if (!reply.ReadInt(&iter, &real_pid)) - goto error; - if (real_pid <= 0) { - // METHOD_GET_CHILD_WITH_INODE failed. Did the child die already? - LOG(ERROR) << "METHOD_GET_CHILD_WITH_INODE failed"; - goto error; - } - } else { - // If no SUID sandbox is involved then no pid translation is - // necessary. - real_pid = pid; - } + } - // Now set-up this process to be tracked by the Zygote. - if (process_info_map_.find(real_pid) != process_info_map_.end()) { - LOG(ERROR) << "Already tracking PID " << real_pid; - NOTREACHED(); - } - process_info_map_[real_pid].internal_pid = pid; - process_info_map_[real_pid].started_from_helper = use_helper; + // In the parent process. + read_pipe.reset(); + pid_oracle.reset(); + + // Always receive a real PID from the zygote host, though it might + // be invalid (see below). + base::ProcessId real_pid; + { + ScopedVector<base::ScopedFD> recv_fds; + char buf[kZygoteMaxMessageLength]; + const ssize_t len = UnixDomainSocket::RecvMsg( + kZygoteSocketPairFd, buf, sizeof(buf), &recv_fds); + CHECK_GT(len, 0); + CHECK(recv_fds.empty()); + + Pickle pickle(buf, len); + PickleIterator iter(pickle); + + int kind; + CHECK(pickle.ReadInt(&iter, &kind)); + CHECK(kind == kZygoteCommandForkRealPID); + CHECK(pickle.ReadInt(&iter, &real_pid)); + } + + // Fork failed. + if (pid < 0) { + return -1; + } + + // If we successfully forked a child, but it crashed without sending + // a message to the browser, the browser won't have found its PID. + if (real_pid < 0) { + KillAndReap(pid, use_helper); + return -1; + } - // If we're using a helper, we still need to let the child process know - // we've discovered its real PID, but we don't actually reveal the PID. - const base::ProcessId pid_for_child = use_helper ? 0 : real_pid; + // If we're not using a helper, send the PID back to the child process. + if (!use_helper) { ssize_t written = - HANDLE_EINTR(write(pipe_fds[1], &pid_for_child, sizeof(pid_for_child))); - if (written != sizeof(pid_for_child)) { - LOG(ERROR) << "Failed to synchronise with child process"; - goto error; + HANDLE_EINTR(write(write_pipe.get(), &real_pid, sizeof(real_pid))); + if (written != sizeof(real_pid)) { + KillAndReap(pid, use_helper); + return -1; } - close(pipe_fds[1]); - return real_pid; } - error: - if (pid > 0) { - if (waitpid(pid, NULL, WNOHANG) == -1) - LOG(ERROR) << "Failed to wait for process"; + // Now set-up this process to be tracked by the Zygote. + if (process_info_map_.find(real_pid) != process_info_map_.end()) { + LOG(ERROR) << "Already tracking PID " << real_pid; + NOTREACHED(); } - if (dummy_fd >= 0) - close(dummy_fd); - if (pipe_fds[0] >= 0) - close(pipe_fds[0]); - if (pipe_fds[1] >= 0) - close(pipe_fds[1]); - return -1; + process_info_map_[real_pid].internal_pid = pid; + process_info_map_[real_pid].started_from_helper = use_helper; + + return real_pid; } base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle, @@ -469,7 +475,13 @@ base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle, if (numfds != static_cast<int>(fds.size())) return -1; - for (int i = 0; i < numfds; ++i) { + // First FD is the PID oracle socket. + if (fds.size() < 1) + return -1; + base::ScopedFD pid_oracle(fds[0]->Pass()); + + // Remaining FDs are for the global descriptor mapping. + for (int i = 1; i < numfds; ++i) { base::GlobalDescriptors::Key key; if (!pickle.ReadUInt32(&iter, &key)) return -1; @@ -480,8 +492,12 @@ base::ProcessId Zygote::ReadArgsAndFork(const Pickle& pickle, static_cast<uint32_t>(kSandboxIPCChannel), GetSandboxFD())); // Returns twice, once per process. - base::ProcessId child_pid = ForkWithRealPid(process_type, mapping, channel_id, - uma_name, uma_sample, + base::ProcessId child_pid = ForkWithRealPid(process_type, + mapping, + channel_id, + pid_oracle.Pass(), + uma_name, + uma_sample, uma_boundary_value); if (!child_pid) { // This is the child process. diff --git a/content/zygote/zygote_linux.h b/content/zygote/zygote_linux.h index 4aa2f03..8e1996c 100644 --- a/content/zygote/zygote_linux.h +++ b/content/zygote/zygote_linux.h @@ -75,12 +75,16 @@ class Zygote { // This is equivalent to fork(), except that, when using the SUID sandbox, it // returns the real PID of the child process as it appears outside the - // sandbox, rather than returning the PID inside the sandbox. Optionally, it - // fills in uma_name et al with a report the helper wants to make via - // UMA_HISTOGRAM_ENUMERATION. + // sandbox, rather than returning the PID inside the sandbox. The child's + // real PID is determined by having it call content::SendZygoteChildPing(int) + // using the |pid_oracle| descriptor. + // Finally, when using a ZygoteForkDelegate helper, |uma_name|, |uma_sample|, + // and |uma_boundary_value| may be set if the helper wants to make a UMA + // report via UMA_HISTOGRAM_ENUMERATION. int ForkWithRealPid(const std::string& process_type, const base::GlobalDescriptors::Mapping& fd_mapping, const std::string& channel_id, + base::ScopedFD pid_oracle, std::string* uma_name, int* uma_sample, int* uma_boundary_value); |