From fc85d027868235fea2ac1f22f09adeed6bec6230 Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Tue, 21 Aug 2018 20:05:45 +0200 Subject: [PATCH] feat: expose missing process APIs in sandboxed renderers (#13505) --- atom/common/api/atom_bindings.cc | 10 ++-- atom/common/api/atom_bindings.h | 3 +- atom/common/node_bindings.cc | 5 +- atom/common/node_bindings.h | 2 + .../atom_sandboxed_renderer_client.cc | 50 +++++++++++-------- .../renderer/atom_sandboxed_renderer_client.h | 5 ++ docs/api/process.md | 14 ++++++ lib/browser/rpc-server.js | 13 +++-- lib/sandboxed_renderer/init.js | 12 +++-- spec/api-browser-window-spec.js | 7 +++ spec/fixtures/module/preload-sandbox.js | 9 +++- 11 files changed, 95 insertions(+), 35 deletions(-) diff --git a/atom/common/api/atom_bindings.cc b/atom/common/api/atom_bindings.cc index 3681cfd7abb4..3dba3d63673c 100644 --- a/atom/common/api/atom_bindings.cc +++ b/atom/common/api/atom_bindings.cc @@ -59,7 +59,7 @@ void AtomBindings::BindTo(v8::Isolate* isolate, v8::Local process) { dict.SetMethod("getCreationTime", &GetCreationTime); dict.SetMethod("getSystemMemoryInfo", &GetSystemMemoryInfo); dict.SetMethod("getCPUUsage", base::Bind(&AtomBindings::GetCPUUsage, - base::Unretained(this))); + base::Unretained(metrics_.get()))); dict.SetMethod("getIOCounters", &GetIOCounters); #if defined(OS_POSIX) dict.SetMethod("setFdLimit", &base::SetFdLimit); @@ -220,17 +220,19 @@ v8::Local AtomBindings::GetSystemMemoryInfo(v8::Isolate* isolate, return dict.GetHandle(); } -v8::Local AtomBindings::GetCPUUsage(v8::Isolate* isolate) { +// static +v8::Local AtomBindings::GetCPUUsage(base::ProcessMetrics* metrics, + v8::Isolate* isolate) { mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate); dict.SetHidden("simple", true); int processor_count = base::SysInfo::NumberOfProcessors(); dict.Set("percentCPUUsage", - metrics_->GetPlatformIndependentCPUUsage() / processor_count); + metrics->GetPlatformIndependentCPUUsage() / processor_count); // NB: This will throw NOTIMPLEMENTED() on Windows // For backwards compatibility, we'll return 0 #if !defined(OS_WIN) - dict.Set("idleWakeupsPerSecond", metrics_->GetIdleWakeupsPerSecond()); + dict.Set("idleWakeupsPerSecond", metrics->GetIdleWakeupsPerSecond()); #else dict.Set("idleWakeupsPerSecond", 0); #endif diff --git a/atom/common/api/atom_bindings.h b/atom/common/api/atom_bindings.h index 5cf339939a91..ba8385c979b1 100644 --- a/atom/common/api/atom_bindings.h +++ b/atom/common/api/atom_bindings.h @@ -40,7 +40,8 @@ class AtomBindings { static v8::Local GetCreationTime(v8::Isolate* isolate); static v8::Local GetSystemMemoryInfo(v8::Isolate* isolate, mate::Arguments* args); - v8::Local GetCPUUsage(v8::Isolate* isolate); + static v8::Local GetCPUUsage(base::ProcessMetrics* metrics, + v8::Isolate* isolate); static v8::Local GetIOCounters(v8::Isolate* isolate); private: diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index a26747cf03c8..e6aded2e661a 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -15,7 +15,6 @@ #include "base/base_paths.h" #include "base/command_line.h" #include "base/environment.h" -#include "base/files/file_path.h" #include "base/path_service.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" @@ -195,6 +194,10 @@ bool NodeBindings::IsInitialized() { return g_is_initialized; } +base::FilePath::StringType NodeBindings::GetHelperResourcesPath() { + return GetResourcesPath(false).value(); +} + void NodeBindings::Initialize() { // Open node's error reporting system for browser process. node::g_standalone_mode = browser_env_ == BROWSER; diff --git a/atom/common/node_bindings.h b/atom/common/node_bindings.h index 7c8cad46cc69..5e5ec8e5300b 100644 --- a/atom/common/node_bindings.h +++ b/atom/common/node_bindings.h @@ -5,6 +5,7 @@ #ifndef ATOM_COMMON_NODE_BINDINGS_H_ #define ATOM_COMMON_NODE_BINDINGS_H_ +#include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" @@ -33,6 +34,7 @@ class NodeBindings { static NodeBindings* Create(BrowserEnvironment browser_env); static void RegisterBuiltinModules(); static bool IsInitialized(); + static base::FilePath::StringType GetHelperResourcesPath(); virtual ~NodeBindings(); diff --git a/atom/renderer/atom_sandboxed_renderer_client.cc b/atom/renderer/atom_sandboxed_renderer_client.cc index 56bedabd5629..6e29fe45a69f 100644 --- a/atom/renderer/atom_sandboxed_renderer_client.cc +++ b/atom/renderer/atom_sandboxed_renderer_client.cc @@ -16,6 +16,7 @@ #include "base/command_line.h" #include "base/files/file_path.h" #include "base/path_service.h" +#include "base/process/process_handle.h" #include "chrome/renderer/printing/print_web_view_helper.h" #include "content/public/renderer/render_frame.h" #include "native_mate/dictionary.h" @@ -96,27 +97,6 @@ v8::Local CreatePreloadScript(v8::Isolate* isolate, return func; } -void InitializeBindings(v8::Local binding, - v8::Local context) { - auto* isolate = context->GetIsolate(); - mate::Dictionary b(isolate, binding); - b.SetMethod("get", GetBinding); - b.SetMethod("createPreloadScript", CreatePreloadScript); - b.SetMethod("crash", AtomBindings::Crash); - b.SetMethod("hang", AtomBindings::Hang); - b.SetMethod("getArgv", GetArgv); - b.SetMethod("getExecPath", GetExecPath); - b.SetMethod("getHeapStatistics", &AtomBindings::GetHeapStatistics); - b.SetMethod("getProcessMemoryInfo", &AtomBindings::GetProcessMemoryInfo); - b.SetMethod("getSystemMemoryInfo", &AtomBindings::GetSystemMemoryInfo); - - // Pass in CLI flags needed to setup the renderer - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(switches::kGuestInstanceID)) - b.Set(options::kGuestInstanceID, - command_line->GetSwitchValueASCII(switches::kGuestInstanceID)); -} - class AtomSandboxedRenderFrameObserver : public AtomRenderFrameObserver { public: AtomSandboxedRenderFrameObserver(content::RenderFrame* render_frame, @@ -152,10 +132,38 @@ class AtomSandboxedRenderFrameObserver : public AtomRenderFrameObserver { AtomSandboxedRendererClient::AtomSandboxedRendererClient() { // Explicitly register electron's builtin modules. NodeBindings::RegisterBuiltinModules(); + metrics_ = base::ProcessMetrics::CreateCurrentProcessMetrics(); } AtomSandboxedRendererClient::~AtomSandboxedRendererClient() {} +void AtomSandboxedRendererClient::InitializeBindings( + v8::Local binding, + v8::Local context) { + auto* isolate = context->GetIsolate(); + mate::Dictionary b(isolate, binding); + b.SetMethod("get", GetBinding); + b.SetMethod("createPreloadScript", CreatePreloadScript); + b.SetMethod("crash", AtomBindings::Crash); + b.SetMethod("hang", AtomBindings::Hang); + b.SetMethod("getArgv", GetArgv); + b.SetMethod("getExecPath", GetExecPath); + b.SetMethod("getPid", &base::GetCurrentProcId); + b.SetMethod("getResourcesPath", &NodeBindings::GetHelperResourcesPath); + b.SetMethod("getHeapStatistics", &AtomBindings::GetHeapStatistics); + b.SetMethod("getProcessMemoryInfo", &AtomBindings::GetProcessMemoryInfo); + b.SetMethod("getSystemMemoryInfo", &AtomBindings::GetSystemMemoryInfo); + b.SetMethod("getCPUUsage", base::Bind(&AtomBindings::GetCPUUsage, + base::Unretained(metrics_.get()))); + b.SetMethod("getIOCounters", &AtomBindings::GetIOCounters); + + // Pass in CLI flags needed to setup the renderer + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kGuestInstanceID)) + b.Set(options::kGuestInstanceID, + command_line->GetSwitchValueASCII(switches::kGuestInstanceID)); +} + void AtomSandboxedRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { new AtomSandboxedRenderFrameObserver(render_frame, this); diff --git a/atom/renderer/atom_sandboxed_renderer_client.h b/atom/renderer/atom_sandboxed_renderer_client.h index 1ebf4429794f..4afef42dfd43 100644 --- a/atom/renderer/atom_sandboxed_renderer_client.h +++ b/atom/renderer/atom_sandboxed_renderer_client.h @@ -8,6 +8,7 @@ #include #include "atom/renderer/renderer_client_base.h" +#include "base/process/process_metrics.h" namespace atom { @@ -16,6 +17,8 @@ class AtomSandboxedRendererClient : public RendererClientBase { AtomSandboxedRendererClient(); ~AtomSandboxedRendererClient() override; + void InitializeBindings(v8::Local binding, + v8::Local context); void InvokeIpcCallback(v8::Handle context, const std::string& callback_name, std::vector> args); @@ -30,6 +33,8 @@ class AtomSandboxedRendererClient : public RendererClientBase { void RenderViewCreated(content::RenderView*) override; private: + std::unique_ptr metrics_; + DISALLOW_COPY_AND_ASSIGN(AtomSandboxedRendererClient); }; diff --git a/docs/api/process.md b/docs/api/process.md index 6598135d177d..1f6b20d2f25c 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -16,10 +16,19 @@ In sandboxed renderers the `process` object contains only a subset of the APIs: - `getHeapStatistics()` - `getProcessMemoryInfo()` - `getSystemMemoryInfo()` +- `getCPUUsage()` +- `getIOCounters()` - `argv` - `execPath` - `env` +- `pid` +- `arch` - `platform` +- `resourcesPath` +- `sandboxed` +- `type` +- `version` +- `versions` ## Events @@ -68,6 +77,11 @@ instead of the `--no-deprecation` command line flag. A `String` representing the path to the resources directory. +### `process.sandboxed` + +A `Boolean`. When the renderer process is sandboxed, this property is `true`, +otherwise it is `undefined`. + ### `process.throwDeprecation` A `Boolean` that controls whether or not deprecation warnings will be thrown as diff --git a/lib/browser/rpc-server.js b/lib/browser/rpc-server.js index f19e2819117f..ff9f77321de3 100644 --- a/lib/browser/rpc-server.js +++ b/lib/browser/rpc-server.js @@ -452,9 +452,14 @@ ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) { } } event.returnValue = { - preloadSrc, - preloadError, - platform: process.platform, - env: process.env + preloadSrc: preloadSrc, + preloadError: preloadError, + process: { + arch: process.arch, + platform: process.platform, + env: process.env, + version: process.version, + versions: process.versions + } } }) diff --git a/lib/sandboxed_renderer/init.js b/lib/sandboxed_renderer/init.js index 37dcefff66aa..30a3fa55216f 100644 --- a/lib/sandboxed_renderer/init.js +++ b/lib/sandboxed_renderer/init.js @@ -36,7 +36,7 @@ const loadedModules = new Map([ ]) const { - preloadSrc, preloadError, platform, env + preloadSrc, preloadError, process: processProps } = electron.ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD') require('../renderer/web-frame-init')() @@ -49,10 +49,16 @@ preloadProcess.hang = () => binding.hang() preloadProcess.getHeapStatistics = () => binding.getHeapStatistics() preloadProcess.getProcessMemoryInfo = () => binding.getProcessMemoryInfo() preloadProcess.getSystemMemoryInfo = () => binding.getSystemMemoryInfo() +preloadProcess.getCPUUsage = () => binding.getCPUUsage() +preloadProcess.getIOCounters = () => binding.getIOCounters() preloadProcess.argv = process.argv = binding.getArgv() preloadProcess.execPath = process.execPath = binding.getExecPath() -preloadProcess.platform = process.platform = platform -preloadProcess.env = process.env = env +preloadProcess.pid = process.pid = binding.getPid() +preloadProcess.resourcesPath = binding.getResourcesPath() +preloadProcess.sandboxed = true +preloadProcess.type = 'renderer' +Object.assign(preloadProcess, processProps) +Object.assign(process, processProps) process.on('exit', () => preloadProcess.emit('exit')) diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index a276f3cc0d61..f4cba12ec19d 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -1617,9 +1617,16 @@ describe('BrowserWindow module', () => { it('validates process APIs access in sandboxed renderer', (done) => { ipcMain.once('answer', function (event, test) { + assert.equal(test.pid, w.webContents.getOSProcessId()) + assert.equal(test.arch, remote.process.arch) assert.equal(test.platform, remote.process.platform) assert.deepEqual(test.env, remote.process.env) assert.equal(test.execPath, remote.process.helperExecPath) + assert.equal(test.resourcesPath, remote.process.resourcesPath) + assert.equal(test.sandboxed, true) + assert.equal(test.type, 'renderer') + assert.equal(test.version, remote.process.version) + assert.deepEqual(test.versions, remote.process.versions) done() }) remote.process.env.sandboxmain = 'foo' diff --git a/spec/fixtures/module/preload-sandbox.js b/spec/fixtures/module/preload-sandbox.js index 38d14a8be50c..6901be56d0c5 100644 --- a/spec/fixtures/module/preload-sandbox.js +++ b/spec/fixtures/module/preload-sandbox.js @@ -11,7 +11,14 @@ window.test = { env: process.env, execPath: process.execPath, - platform: process.platform + pid: process.pid, + arch: process.arch, + platform: process.platform, + resourcesPath: process.resourcesPath, + sandboxed: process.sandboxed, + type: process.type, + version: process.version, + versions: process.versions } } } else if (location.href !== 'about:blank') {