@@ -42,6 +42,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4242#include < spawn.h>
4343#include < fcntl.h>
4444#include < sys/wait.h>
45+ // POSIX: environ is the process environment, not always declared in headers.
46+ extern char **environ;
4547#ifdef __linux__
4648#include < sys/prctl.h>
4749#if defined(DAEMON_ARCH_armhf)
@@ -73,6 +75,54 @@ static Cvar::Cvar<bool> workaround_naclSystem_freebsd_disableQualification(
7375 " Disable platform qualification when running Linux NaCl loader on FreeBSD through Linuxulator" ,
7476 Cvar::NONE, true );
7577
78+ #if defined(DAEMON_NACL_BOX64_EMULATION)
79+ static Cvar::Cvar<bool > workaround_box64_disableQualification (
80+ " workaround.box64.disableQualification" ,
81+ " Disable platform qualification when running amd64 NaCl loader under Box64 emulation" ,
82+ Cvar::NONE, true );
83+
84+ static Cvar::Cvar<bool > workaround_box64_disableBootstrap (
85+ " workaround.box64.disableBootstrap" ,
86+ " Disable NaCl bootstrap helper when using Box64 emulation" ,
87+ Cvar::NONE, true );
88+
89+ static Cvar::Cvar<std::string> vm_box64_path (
90+ " vm.box64.path" ,
91+ " Path to the box64 binary for NaCl emulation (empty = search PATH)" ,
92+ Cvar::NONE, " " );
93+
94+ // Resolve box64 binary path by searching PATH if not explicitly set.
95+ static std::string ResolveBox64Path () {
96+ std::string path = vm_box64_path.Get ();
97+ if (!path.empty ()) {
98+ return path;
99+ }
100+
101+ const char * envPath = getenv (" PATH" );
102+ if (!envPath) {
103+ Sys::Error (" Box64 emulation is enabled but PATH is not set and vm.box64.path is empty." );
104+ }
105+
106+ std::string pathStr (envPath);
107+ size_t start = 0 ;
108+ while (start < pathStr.size ()) {
109+ size_t end = pathStr.find (' :' , start);
110+ if (end == std::string::npos) {
111+ end = pathStr.size ();
112+ }
113+ std::string candidate = pathStr.substr (start, end - start) + " /box64" ;
114+ if (access (candidate.c_str (), X_OK) == 0 ) {
115+ return candidate;
116+ }
117+ start = end + 1 ;
118+ }
119+
120+ Sys::Error (" Box64 emulation is enabled but 'box64' was not found in PATH. "
121+ " Install Box64 or set vm.box64.path to the full path of the box64 binary." );
122+ return " " ; // unreachable
123+ }
124+ #endif
125+
76126static Cvar::Cvar<bool > vm_nacl_qualification (
77127 " vm.nacl.qualification" ,
78128 " Enable NaCl loader platform qualification" ,
@@ -128,7 +178,7 @@ static void CheckMinAddressSysctlTooLarge()
128178}
129179
130180// Platform-specific code to load a module
131- static std::pair<Sys::OSHandle, IPC::Socket> InternalLoadModule (std::pair<IPC::Socket, IPC::Socket> pair, const char * const * args, bool reserve_mem, FS::File stderrRedirect = FS::File())
181+ static std::pair<Sys::OSHandle, IPC::Socket> InternalLoadModule (std::pair<IPC::Socket, IPC::Socket> pair, const char * const * args, bool reserve_mem, FS::File stderrRedirect = FS::File(), bool inheritEnvironment = false )
132182{
133183#ifdef _WIN32
134184 // Inherit the socket in the child process
@@ -213,6 +263,7 @@ static std::pair<Sys::OSHandle, IPC::Socket> InternalLoadModule(std::pair<IPC::S
213263 if (reserve_mem)
214264 VirtualAllocEx (processInfo.hProcess , nullptr , 1 << 30 , MEM_RESERVE, PAGE_NOACCESS);
215265#endif
266+ Q_UNUSED (inheritEnvironment);
216267
217268 ResumeThread (processInfo.hThread );
218269 CloseHandle (processInfo.hThread );
@@ -233,7 +284,13 @@ static std::pair<Sys::OSHandle, IPC::Socket> InternalLoadModule(std::pair<IPC::S
233284 }
234285
235286 pid_t pid;
236- int err = posix_spawn (&pid, args[0 ], &fileActions, nullptr , const_cast <char * const *>(args), nullptr );
287+ // By default, the child process gets an empty environment for sandboxing.
288+ // When Box64 emulation is used, the child needs to inherit the parent's
289+ // environment so Box64 can find its configuration (e.g. ~/.box64rc, HOME)
290+ // and honor settings like BOX64_DYNAREC_PERFMAP.
291+ char * emptyEnv[] = {nullptr };
292+ char ** envp = inheritEnvironment ? environ : emptyEnv;
293+ int err = posix_spawn (&pid, args[0 ], &fileActions, nullptr , const_cast <char * const *>(args), envp);
237294 posix_spawn_file_actions_destroy (&fileActions);
238295 if (err != 0 ) {
239296 Sys::Drop (" VM: Failed to spawn process: %s" , strerror (err));
@@ -255,6 +312,10 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
255312 char rootSocketRedir[32 ];
256313 std::string module , nacl_loader, irt, bootstrap, modulePath, verbosity;
257314 FS::File stderrRedirect;
315+ #if defined(DAEMON_NACL_BOX64_EMULATION)
316+ std::string box64Path;
317+ bool usingBox64 = false ;
318+ #endif
258319#if !defined(_WIN32) || defined(_WIN64)
259320 constexpr bool win32Force64Bit = false ;
260321#else
@@ -304,6 +365,34 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
304365 }
305366
306367#if defined(__linux__) || defined(__FreeBSD__)
368+ #if defined(DAEMON_NACL_BOX64_EMULATION)
369+ /* Use Box64 to run the x86_64 NaCl loader on non-x86 architectures.
370+ The bootstrap helper uses a double-exec pattern that Box64 cannot handle,
371+ so we skip it and prepend "box64" to the nacl_loader command instead. */
372+ if (!workaround_box64_disableBootstrap.Get () && vm_nacl_bootstrap.Get ()) {
373+ bootstrap = FS::Path::Build (naclPath, " nacl_helper_bootstrap" );
374+
375+ if (!FS::RawPath::FileExists (bootstrap)) {
376+ Sys::Error (" NaCl bootstrap helper not found: %s" , bootstrap);
377+ }
378+
379+ args.push_back (bootstrap.c_str ());
380+ args.push_back (nacl_loader.c_str ());
381+ args.push_back (" --r_debug=0xXXXXXXXXXXXXXXXX" );
382+ args.push_back (" --reserved_at_zero=0xXXXXXXXXXXXXXXXX" );
383+ } else {
384+ if (workaround_box64_disableBootstrap.Get ()) {
385+ Log::Notice (" Skipping NaCl bootstrap helper for Box64 emulation." );
386+ } else {
387+ Log::Warn (" Not using NaCl bootstrap helper." );
388+ }
389+ box64Path = ResolveBox64Path ();
390+ Log::Notice (" Using Box64 emulator: %s" , box64Path);
391+ args.push_back (box64Path.c_str ());
392+ args.push_back (nacl_loader.c_str ());
393+ usingBox64 = true ;
394+ }
395+ #else
307396 if (vm_nacl_bootstrap.Get ()) {
308397#if defined(DAEMON_ARCH_arm64)
309398 bootstrap = FS::Path::Build (naclPath, " nacl_helper_bootstrap-armhf" );
@@ -323,6 +412,7 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
323412 Log::Warn (" Not using NaCl bootstrap helper." );
324413 args.push_back (nacl_loader.c_str ());
325414 }
415+ #endif
326416#else
327417 Q_UNUSED (bootstrap);
328418 args.push_back (nacl_loader.c_str ());
@@ -381,6 +471,17 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
381471 enableQualification = false ;
382472 }
383473#endif
474+
475+ #if defined(DAEMON_NACL_BOX64_EMULATION)
476+ /* When running the amd64 NaCl loader under Box64, the loader's
477+ platform qualification will fail because the CPU is not actually x86_64.
478+ Disabling qualification allows the emulated loader to proceed. */
479+
480+ if (workaround_box64_disableQualification.Get ()) {
481+ Log::Warn (" Disabling NaCL platform qualification for Box64 emulation." );
482+ enableQualification = false ;
483+ }
484+ #endif
384485 }
385486 else {
386487 Log::Warn (" Not using NaCl platform qualification." );
@@ -427,7 +528,11 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
427528 Log::Notice (" Using loader args: %s" , commandLine.c_str ());
428529 }
429530
430- return InternalLoadModule (std::move (pair), args.data (), true , std::move (stderrRedirect));
531+ return InternalLoadModule (std::move (pair), args.data (), true , std::move (stderrRedirect)
532+ #if defined(DAEMON_NACL_BOX64_EMULATION)
533+ , usingBox64
534+ #endif
535+ );
431536}
432537
433538static std::pair<Sys::OSHandle, IPC::Socket> CreateNativeVM (std::pair<IPC::Socket, IPC::Socket> pair, Str::StringRef name, bool debug) {
0 commit comments