Skip to content

Commit ec5de79

Browse files
yangzuohuiyangliu-bytedance
andcommitted
fix: fix launch crash when null device is disabled on Windows
add node flag node::ProcessInitializationFlags::kNoStdioInitialization Co-authored-by: yangliu <yangliu.leo@bytedance.com>
1 parent 03aad28 commit ec5de79

File tree

8 files changed

+68
-5
lines changed

8 files changed

+68
-5
lines changed

docs/api/command-line-switches.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ Disables the Chromium [sandbox](https://www.chromium.org/developers/design-docum
179179
Forces renderer process and Chromium helper processes to run un-sandboxed.
180180
Should only be used for testing.
181181

182+
### --no-stdio-init
183+
184+
Disable stdio initialization during node initialization.
185+
Used to avoid node initialization crash when the nul device is disabled on Windows platform.
186+
182187
### --proxy-bypass-list=`hosts`
183188

184189
Instructs Electron to bypass the proxy server for the given semi-colon-separated

shell/app/node_main.cc

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
#include "shell/common/node_bindings.h"
3636
#include "shell/common/node_includes.h"
3737
#include "shell/common/node_util.h"
38+
#include "shell/common/options_switches.h"
39+
#include "shell/common/platform_util.h"
3840

3941
#if BUILDFLAG(IS_WIN)
4042
#include "chrome/child/v8_crashpad_support_win.h"
@@ -189,14 +191,33 @@ int NodeMain() {
189191
NodeBindings::RegisterBuiltinBindings();
190192

191193
// Parse Node.js cli flags and strip out disallowed options.
192-
const std::vector<std::string> args = ElectronCommandLine::AsUtf8();
194+
std::vector<std::string> args = ElectronCommandLine::AsUtf8();
193195
ExitIfContainsDisallowedFlags(args);
194196

197+
uint64_t process_flags =
198+
node::ProcessInitializationFlags::kNoInitializeV8 |
199+
node::ProcessInitializationFlags::kNoInitializeNodeV8Platform;
200+
201+
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
202+
if (command_line->HasSwitch(switches::kNoStdioInit)) {
203+
process_flags |= node::ProcessInitializationFlags::kNoStdioInitialization;
204+
// remove the option to avoid node error "bad option: --no-stdio-init"
205+
std::string option = std::string("--") + switches::kNoStdioInit;
206+
std::erase(args, option);
207+
} else {
208+
#if BUILDFLAG(IS_WIN)
209+
if (!platform_util::IsNulDeviceEnabled()) {
210+
LOG(FATAL) << "Unable to open nul device needed for initialization,"
211+
"aborting startup. As a workaround, try starting with --"
212+
<< switches::kNoStdioInit;
213+
}
214+
#endif
215+
}
216+
195217
std::shared_ptr<node::InitializationResult> result =
196218
node::InitializeOncePerProcess(
197-
args,
198-
{node::ProcessInitializationFlags::kNoInitializeV8,
199-
node::ProcessInitializationFlags::kNoInitializeNodeV8Platform});
219+
args, static_cast<node::ProcessInitializationFlags::Flags>(
220+
process_flags));
200221

201222
for (const std::string& error : result->errors())
202223
std::cerr << args[0] << ": " << error << '\n';

shell/browser/api/electron_api_utility_process.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ UtilityProcessWrapper::UtilityProcessWrapper(
133133
OPEN_EXISTING, 0, nullptr);
134134
if (handle == INVALID_HANDLE_VALUE) {
135135
PLOG(ERROR) << "Failed to create null handle";
136+
Emit("error", "Failed to create null handle for ignoring stdio");
136137
return;
137138
}
138139
if (io_handle == IOHandle::STDOUT) {

shell/browser/electron_browser_client.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
533533
if (process_type == ::switches::kUtilityProcess ||
534534
process_type == ::switches::kRendererProcess) {
535535
// Copy following switches to child process.
536-
static constexpr std::array<const char*, 9U> kCommonSwitchNames = {
536+
static constexpr std::array<const char*, 10U> kCommonSwitchNames = {
537537
switches::kStandardSchemes.c_str(),
538538
switches::kEnableSandbox.c_str(),
539539
switches::kSecureSchemes.c_str(),
@@ -542,6 +542,7 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
542542
switches::kFetchSchemes.c_str(),
543543
switches::kServiceWorkerSchemes.c_str(),
544544
switches::kStreamingSchemes.c_str(),
545+
switches::kNoStdioInit.c_str(),
545546
switches::kCodeCacheSchemes.c_str()};
546547
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
547548
kCommonSwitchNames);

shell/common/node_bindings.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
#include "shell/common/mac/main_application_bundle.h"
4040
#include "shell/common/node_includes.h"
4141
#include "shell/common/node_util.h"
42+
#include "shell/common/options_switches.h"
43+
#include "shell/common/platform_util.h"
4244
#include "shell/common/process_util.h"
4345
#include "shell/common/world_ids.h"
4446
#include "third_party/blink/public/web/web_local_frame.h"
@@ -617,6 +619,19 @@ void NodeBindings::Initialize(v8::Isolate* const isolate,
617619
if (!fuses::IsNodeOptionsEnabled())
618620
process_flags |= node::ProcessInitializationFlags::kDisableNodeOptionsEnv;
619621

622+
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
623+
if (command_line->HasSwitch(switches::kNoStdioInit)) {
624+
process_flags |= node::ProcessInitializationFlags::kNoStdioInitialization;
625+
} else {
626+
#if BUILDFLAG(IS_WIN)
627+
if (!platform_util::IsNulDeviceEnabled()) {
628+
LOG(FATAL) << "Unable to open nul device needed for initialization,"
629+
"aborting startup. As a workaround, try starting with --"
630+
<< switches::kNoStdioInit;
631+
}
632+
#endif
633+
}
634+
620635
std::shared_ptr<node::InitializationResult> result =
621636
node::InitializeOncePerProcess(
622637
args,

shell/common/options_switches.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,10 @@ inline constexpr base::cstring_view kDisableNTLMv2 = "disable-ntlm-v2";
306306
inline constexpr base::cstring_view kServiceWorkerPreload =
307307
"service-worker-preload";
308308

309+
// If set, flag node::ProcessInitializationFlags::kNoStdioInitialization would
310+
// be set for node initialization.
311+
inline constexpr base::cstring_view kNoStdioInit = "no-stdio-init";
312+
309313
} // namespace switches
310314

311315
} // namespace electron

shell/common/platform_util.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ void Beep();
4747
#if BUILDFLAG(IS_WIN)
4848
// SHGetFolderPath calls not covered by Chromium
4949
bool GetFolderPath(int key, base::FilePath* result);
50+
51+
// Check if nul device can be used.
52+
bool IsNulDeviceEnabled();
5053
#endif
5154

5255
#if BUILDFLAG(IS_MAC)

shell/common/platform_util_win.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <comdef.h>
1313
#include <commdlg.h>
1414
#include <dwmapi.h>
15+
#include <fcntl.h>
16+
#include <io.h>
1517
#include <objbase.h>
1618
#include <shellapi.h>
1719
#include <shlobj.h>
@@ -450,4 +452,15 @@ void Beep() {
450452
MessageBeep(MB_OK);
451453
}
452454

455+
bool IsNulDeviceEnabled() {
456+
bool ret = true;
457+
int fd = _open("nul", _O_RDWR);
458+
if (fd < 0) {
459+
ret = false;
460+
} else {
461+
_close(fd);
462+
}
463+
return ret;
464+
}
465+
453466
} // namespace platform_util

0 commit comments

Comments
 (0)