Loading a Native Client module in sel_ldr:TBD
Loading a Native Client module in Chrome
- The browser loads a page that contains an embed tag with “application/x-nacl-srpc” MIME type
- Renderer process: RenderView::CreatePluginDelegate (chrome/renderer/render_view.cc) gets called from WebKit code - this is a request to instantiate a plugin for a specific MIME type. This is where we check whether NaCl is enabled and block the plugin initialization process if necessary.
- Renderer process:The plugin’s NP_Initialize function gets called
- Renderer process: Downloading the NaCl module
- NPP_NewStream is called when the browser starts downloading the Native Client module. This gets forwarded to PluginNpapi::NewStream (native_client\src\trusted\plugin\npapi\plugin_npapi.cc) that returns NP_NORMAL, meaning that the plugin is willing to receive the binary data chunk by chunk. We cannot use NP_ASFILEONLY in Chrome, since our plugin runs in the renderer process that is sandboxed, so we wouldn’t be able to open the file.
- Chrome invokes NPP_Write for each chunk of the binary data. The plugin writes the data into a StreamShmBuffer object.
- When the download is completed (successfully or not), the browser invokes NPP_DestroyStream. Our handler then invokes Plugin::Load (native_client/src/trusted/plugin/srpc/plugin.cc) and passes it a pointer to the StreamShmBuffer object that contains the nexe binary.
- Renderer process: loading the NaCl module in Plugin::Load:
- Checks that the nexe file origin is whitelisted (we don’t allow loading NaCl modules from local storage)
- Checks that the data in the buffer is a legal ELF binary. If the download process has failed we will usually find out about it here and stop the load process.
- Creates a new plugin::ServiceRuntime object that abstracts a Native Client loader instance.
- Calls ServiceRuntime::Start that in turn:
- Creates a new nacl::SelLdrLauncher object. The arguments provided are the URL for the nexe and a file descriptor (usually 5) for IMC channel that will be used for initializing the communication between the plugin and the loader process (more details below). We will refer to this channel as channel5 in this document.
- Calls nacl::SelLdrLauncher::Start method that calls the LaunchNaClProcess function (using the launch_nacl_process function pointer that is initialized during the renderer process startup - see the call to RegisterInternalNaClPlugin.). LaunchNaClProcess sends the ViewHostMsg_LaunchNaCl IPC message to the browser process, asking to launch a new loader process.
- Browser process: Launching the loader process - the following happens every time a ViewHostMsg_LaunchNaCl is received from the renderer (the message is handled in RenderMessageFilter::OnLaunchNaCl, chrome/browser/renderer_host/render_message_filter.cc)
- Create a connected socket pair for channel5.
- Create a Chrome IPC channel - it is required to allow the browser process to communicate with the child process. The channel ID is passed to the child process on the command line.
- Launch the loader process:
- On 64-bit Windows: ask the NaClBrokerService to launch a new loader (see NaCl-Chrome integration for 64-bit Windows document for details)
- On other systems, use BrowserChildProcessHost::Launch to launch the loader process directly
- Once the process is launched, the NaClProcessHost::OnProcessLaunched callback is invoked by the launcher. The browser then sends a reply to the ViewHostMsg_LaunchNaCl message. The reply contains a handle to one end of the channel5, the loader process ID and a handle to the loader process.
- After sending the reply to the renderer, the browser sends a NaClProcessMsg_Start message to the loader process. This message contains a handle to the second side channel5 and the descriptor that was sent by the renderer (5).
- From this moment on, the plugin (in the renderer process) and the loader each have a handle to one side of channel5 and can communicate directly.
- Loader process: The NaClProcessMsg_Start handler just calls SelMain() - sel_ldr’s main function that loads the NaCl module:
- Associates the handle received in the message with the descriptor that is also provided by the message (5)
- Creates a bound socket and:
- Binds its BoundSocket side to descriptor 3
- Binds its SocketAddress to descriptor 4
- Sends the SocketAddress as the first message on channel5
- It then waits for the plugin to connect to the bound socket (calls AcceptConn on the BoundSocket at descriptor 3). When the plugin connects, the new channel becomes the NaClSecureCommandChannel that the plugin can use to send commands to the loader using SRPC (see NaClSecureChannelThread function for the list of supported commands). This channel is secure in the sense that it is private to the service runtime and is never available to untrusted NaCl modules.
- The loader than blocks waiting for the start_module SRPC call
- Renderer process: the following is done in the ServiceRuntime::InitCommunication function:
- Reads the SocketAddress from channel5 (this causes the renderer’s IO thread to block until the loader process starts and goes through all the steps that are described above).
- Calls Connect using the SocketAddress, thus getting a handle to the secure command channel.
- Sends the NaCl module to the loader process using the load_module command on the secure command channel (the data itself is passed using shared memory).
- On Windows: initializes the handle passing protocol (allows IMC to pass handles between processes inside Chrome sandbox)
- Sends the start_module command
- Once again calls Connect on the SocketAddress that was received on channel 5 - the resulting channel will be used to communicate with the untrusted module.
- Loader process:
- When the start_module SRPC call is received, the loader proceeds and starts up the main thread of the untrusted application.
- This includes executing Native Client crt0 startup code (see the architecture-specific assembly files in native_client/src/untrusted/stubs).
- One significant call in that sequence is __srpc_init: it spawns the SRPC acceptor thread, that waits for connections on the BoundSocket at descriptor 3 and starts an SRPC handler thread for each connection. We expect that the first connection the acceptor thread handles will come from the plugin code (as a result of the plugin calling Connect on the SocketAdress for the second time)
Loading a Native Client module in Firefox:TBD