fix: launch crash when null device is disabled on Windows (#47870)

fix: fix launch crash when null device is disabled on Windows

add node flag node::ProcessInitializationFlags::kNoStdioInitialization

Co-authored-by: yangzuohui <yangzuohui@bytedance.com>
Co-authored-by: yangliu <yangliu.leo@bytedance.com>
This commit is contained in:
Zuohui Yang
2025-10-12 06:00:04 +08:00
committed by GitHub
parent cbf5c3331f
commit 16b5776b01
8 changed files with 69 additions and 6 deletions

View File

@@ -35,6 +35,8 @@
#include "shell/common/node_bindings.h"
#include "shell/common/node_includes.h"
#include "shell/common/node_util.h"
#include "shell/common/options_switches.h"
#include "shell/common/platform_util.h"
#if BUILDFLAG(IS_WIN)
#include "chrome/child/v8_crashpad_support_win.h"
@@ -153,9 +155,10 @@ int NodeMain() {
v8_crashpad_support::SetUp();
#endif
auto* command_line = base::CommandLine::ForCurrentProcess();
#if BUILDFLAG(IS_LINUX)
int pid = -1;
auto* command_line = base::CommandLine::ForCurrentProcess();
std::optional<std::string> fd_string = os_env->GetVar("CRASHDUMP_SIGNAL_FD");
std::optional<std::string> pid_string =
os_env->GetVar("CRASHPAD_HANDLER_PID");
@@ -189,14 +192,32 @@ int NodeMain() {
NodeBindings::RegisterBuiltinBindings();
// Parse Node.js cli flags and strip out disallowed options.
const std::vector<std::string> args = ElectronCommandLine::AsUtf8();
std::vector<std::string> args = ElectronCommandLine::AsUtf8();
ExitIfContainsDisallowedFlags(args);
uint64_t process_flags =
node::ProcessInitializationFlags::kNoInitializeV8 |
node::ProcessInitializationFlags::kNoInitializeNodeV8Platform;
if (command_line->HasSwitch(switches::kNoStdioInit)) {
process_flags |= node::ProcessInitializationFlags::kNoStdioInitialization;
// remove the option to avoid node error "bad option: --no-stdio-init"
std::string option = std::string("--") + switches::kNoStdioInit;
std::erase(args, option);
} else {
#if BUILDFLAG(IS_WIN)
if (!platform_util::IsNulDeviceEnabled()) {
LOG(FATAL) << "Unable to open nul device needed for initialization,"
"aborting startup. As a workaround, try starting with --"
<< switches::kNoStdioInit;
}
#endif
}
std::shared_ptr<node::InitializationResult> result =
node::InitializeOncePerProcess(
args,
{node::ProcessInitializationFlags::kNoInitializeV8,
node::ProcessInitializationFlags::kNoInitializeNodeV8Platform});
args, static_cast<node::ProcessInitializationFlags::Flags>(
process_flags));
for (const std::string& error : result->errors())
std::cerr << args[0] << ": " << error << '\n';

View File

@@ -132,6 +132,7 @@ UtilityProcessWrapper::UtilityProcessWrapper(
OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
PLOG(ERROR) << "Failed to create null handle";
Emit("error", "Failed to create null handle for ignoring stdio");
return;
}
if (io_handle == IOHandle::STDOUT) {

View File

@@ -549,7 +549,7 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
if (process_type == ::switches::kUtilityProcess ||
process_type == ::switches::kRendererProcess) {
// Copy following switches to child process.
static constexpr std::array<const char*, 9U> kCommonSwitchNames = {
static constexpr std::array<const char*, 10U> kCommonSwitchNames = {
switches::kStandardSchemes.c_str(),
switches::kEnableSandbox.c_str(),
switches::kSecureSchemes.c_str(),
@@ -558,6 +558,7 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
switches::kFetchSchemes.c_str(),
switches::kServiceWorkerSchemes.c_str(),
switches::kStreamingSchemes.c_str(),
switches::kNoStdioInit.c_str(),
switches::kCodeCacheSchemes.c_str()};
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
kCommonSwitchNames);

View File

@@ -40,6 +40,8 @@
#include "shell/common/mac/main_application_bundle.h"
#include "shell/common/node_includes.h"
#include "shell/common/node_util.h"
#include "shell/common/options_switches.h"
#include "shell/common/platform_util.h"
#include "shell/common/process_util.h"
#include "shell/common/world_ids.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
@@ -672,6 +674,19 @@ void NodeBindings::Initialize(v8::Isolate* const isolate,
if (!fuses::IsNodeOptionsEnabled())
process_flags |= node::ProcessInitializationFlags::kDisableNodeOptionsEnv;
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kNoStdioInit)) {
process_flags |= node::ProcessInitializationFlags::kNoStdioInitialization;
} else {
#if BUILDFLAG(IS_WIN)
if (!platform_util::IsNulDeviceEnabled()) {
LOG(FATAL) << "Unable to open nul device needed for initialization,"
"aborting startup. As a workaround, try starting with --"
<< switches::kNoStdioInit;
}
#endif
}
std::shared_ptr<node::InitializationResult> result =
node::InitializeOncePerProcess(
args,

View File

@@ -309,6 +309,10 @@ inline constexpr base::cstring_view kDisableNTLMv2 = "disable-ntlm-v2";
inline constexpr base::cstring_view kServiceWorkerPreload =
"service-worker-preload";
// If set, flag node::ProcessInitializationFlags::kNoStdioInitialization would
// be set for node initialization.
inline constexpr base::cstring_view kNoStdioInit = "no-stdio-init";
} // namespace switches
} // namespace electron

View File

@@ -47,6 +47,9 @@ void Beep();
#if BUILDFLAG(IS_WIN)
// SHGetFolderPath calls not covered by Chromium
bool GetFolderPath(int key, base::FilePath* result);
// Check if nul device can be used.
bool IsNulDeviceEnabled();
#endif
#if BUILDFLAG(IS_MAC)

View File

@@ -12,6 +12,8 @@
#include <comdef.h>
#include <commdlg.h>
#include <dwmapi.h>
#include <fcntl.h>
#include <io.h>
#include <objbase.h>
#include <shellapi.h>
#include <shlobj.h>
@@ -450,4 +452,15 @@ void Beep() {
MessageBeep(MB_OK);
}
bool IsNulDeviceEnabled() {
bool ret = true;
int fd = _open("nul", _O_RDWR);
if (fd < 0) {
ret = false;
} else {
_close(fd);
}
return ret;
}
} // namespace platform_util