Skip to content

Commit

Permalink
VirtualMachine: add workaround cvars for not disabling platform quali…
Browse files Browse the repository at this point in the history
…fication on arm64 or FreeBSD, and more

- Add workaround cvars for not disabling platform qualification on arm64 or FreeBSD.
- Run nacl_loader on FreeBSD Linuxulator the same way it runs on Linux (using the bootstrap helper).
- Add generic cvars to disable platforme qualification and bootstrap helper.
  • Loading branch information
illwieckz committed Oct 1, 2024
1 parent 0407cd8 commit e2dedcc
Showing 1 changed file with 112 additions and 36 deletions.
148 changes: 112 additions & 36 deletions src/engine/framework/VirtualMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "qcommon/qcommon.h"
#include "qcommon/sys.h"
#include "VirtualMachine.h"
#include "CvarSystem.h"

#ifdef _WIN32
#include <windows.h>
Expand All @@ -44,6 +45,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sys/wait.h>
#ifdef __linux__
#include <sys/prctl.h>
#if defined(DAEMON_ARCH_armhf)
#include <sys/utsname.h>
#endif
#endif
#endif

Expand All @@ -55,6 +59,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 0x2000
#endif

static Cvar::Cvar<bool> workaround_naclArchitecture_arm64_disableQualification(
"workaround.naclArchitecture.arm64.disableQualification",
"Disable platform qualification when running armhf NaCl loader on arm64 Linux",
Cvar::NONE, true);

static Cvar::Cvar<bool> workaround_naclSystem_freebsd_disableQualification(
"workaround.naclSystem.freebsd.disableQualification",
"Disable platform qualification when running Linux NaCl loader on FreeBSD through Linuxulator",
Cvar::NONE, true);

static Cvar::Cvar<bool> vm_nacl_qualification(
"vm.nacl.qualification",
"Enable NaCl loader platform qualification",
Cvar::INIT, true);

static Cvar::Cvar<bool> vm_nacl_bootstrap(
"vm.nacl.bootstrap",
"Use NaCl bootstrap helper",
Cvar::INIT, true);

namespace VM {

// https://github.com/Unvanquished/Unvanquished/issues/944#issuecomment-744454772
Expand Down Expand Up @@ -199,6 +223,11 @@ static std::pair<Sys::OSHandle, IPC::Socket> InternalLoadModule(std::pair<IPC::S
}

static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket, IPC::Socket> pair, Str::StringRef name, bool debug, bool extract, int debugLoader) {
Cvar::Latch(workaround_naclArchitecture_arm64_disableQualification);
Cvar::Latch(workaround_naclSystem_freebsd_disableQualification);
Cvar::Latch(vm_nacl_qualification);
Cvar::Latch(vm_nacl_bootstrap);

CheckMinAddressSysctlTooLarge();
const std::string& libPath = FS::GetLibPath();
#ifdef NACL_RUNTIME_PATH
Expand Down Expand Up @@ -233,67 +262,114 @@ static std::pair<Sys::OSHandle, IPC::Socket> CreateNaClVM(std::pair<IPC::Socket,
Sys::Drop("VM: Failed to extract VM module %s: %s", module, err.what());
}
modulePath = FS::Path::Build(FS::GetHomePath(), module);
} else
} else {
modulePath = FS::Path::Build(libPath, module);
}

// Generate command line
Q_snprintf(rootSocketRedir, sizeof(rootSocketRedir), "%d:%d", ROOT_SOCKET_FD, (int)(intptr_t)pair.second.GetHandle());
irt = FS::Path::Build(naclPath, win32Force64Bit ? "irt_core-amd64.nexe" : "irt_core-" XSTRING(NACL_ARCH_STRING) ".nexe");
nacl_loader = FS::Path::Build(naclPath, win32Force64Bit ? "nacl_loader-amd64" EXE_EXT : "nacl_loader" EXE_EXT);
if (!FS::RawPath::FileExists(modulePath))
Log::Warn("VM module file not found: %s", modulePath);
if (!FS::RawPath::FileExists(nacl_loader))
Log::Warn("NaCl loader not found: %s", nacl_loader);
if (!FS::RawPath::FileExists(irt))
Log::Warn("NaCl integrated runtime not found: %s", irt);
#ifdef __linux__
#if defined(DAEMON_ARCH_arm64)

if (!FS::RawPath::FileExists(modulePath)) {
Sys::Error("VM module file not found: %s", modulePath);
}

if (!FS::RawPath::FileExists(nacl_loader)) {
Sys::Error("NaCl loader not found: %s", nacl_loader);
}

if (!FS::RawPath::FileExists(irt)) {
Sys::Error("NaCl integrated runtime not found: %s", irt);
}

#if defined(__linux__) || defined(__FreeBSD__)
if (vm_nacl_bootstrap.Get()) {
#if defined(DAEMON_ARCH_arm64)
bootstrap = FS::Path::Build(naclPath, "nacl_helper_bootstrap-armhf");
#else
#else
bootstrap = FS::Path::Build(naclPath, "nacl_helper_bootstrap");
#endif
if (!FS::RawPath::FileExists(bootstrap))
Log::Warn("NaCl bootstrap helper not found: %s", bootstrap);
args.push_back(bootstrap.c_str());
#endif

if (!FS::RawPath::FileExists(bootstrap)) {
Sys::Error("NaCl bootstrap helper not found: %s", bootstrap);
}

args.push_back(bootstrap.c_str());
args.push_back(nacl_loader.c_str());
args.push_back("--r_debug=0xXXXXXXXXXXXXXXXX");
args.push_back("--reserved_at_zero=0xXXXXXXXXXXXXXXXX");
} else {
Log::Warn("Not using NaCl bootstrap helper.");
args.push_back(nacl_loader.c_str());
}
#else
Q_UNUSED(bootstrap);
args.push_back(nacl_loader.c_str());
args.push_back("--r_debug=0xXXXXXXXXXXXXXXXX");
args.push_back("--reserved_at_zero=0xXXXXXXXXXXXXXXXX");
#endif

#if defined(DAEMON_ARCH_arm64) || defined(DAEMON_ARCH_armhf)
/* This is required to run on Raspberry Pi 4,
otherwise nexe loading fails with this message:
bool enableQualification = vm_nacl_qualification.Get();

Error while loading "sgame-armhf.nexe": CPU model is not supported
if (enableQualification) {
#if defined(DAEMON_ARCH_arm64) || defined(DAEMON_ARCH_armhf)
if (workaround_naclArchitecture_arm64_disableQualification.Get()) {
#if defined(DAEMON_ARCH_arm64)
bool onArm64 = true;
#elif defined(DAEMON_ARCH_armhf)
bool onArm64 = false;

From nacl_loader --help we can read:
struct utsname buf;
if (!uname(&buf)) {
onArm64 = !strcmp(buf.machine, "aarch64");
}
#endif

-Q disable platform qualification (dangerous!)
/* This is required to run armhf NaCl loader on arm64 kernel
otherwise nexe loading fails with this message:
When this option is enabled, nacl_loader will print:
> Error while loading "sgame-armhf.nexe": CPU model is not supported
PLATFORM QUALIFICATION DISABLED BY -Q - Native Client's sandbox will be unreliable!
From nacl_loader --help we can read:
But the nexe will load and run. */
> -Q disable platform qualification (dangerous!)
args.push_back("-Q");
#endif
#else
Q_UNUSED(bootstrap);
args.push_back(nacl_loader.c_str());
When this option is enabled, nacl_loader will print:
> PLATFORM QUALIFICATION DISABLED BY -Q - Native Client's sandbox will be unreliable!
#if defined(__FreeBSD__)
But the nexe will load and run. */

if (onArm64) {
Log::Warn("Disabling NaCL platform qualification on arm64 kernel architecture.");
enableQualification = false;
}
}
#endif

#if defined(__FreeBSD__)
/* While it is possible to build a native FreeBSD engine, the only available NaCl loader
is the Linux one, which can run on Linuxulator (the FreeBSD Linux compatibility layer).
The Linux NaCl loader binary fails to qualify the platform and aborts with this message:
Bus error (core dumped)
> Bus error (core dumped)
The Linux NaCl loader runs properly on Linuxulator when we disable the qualification.
It also works without nacl_helper_bootstrap. */
The Linux NaCl loader runs properly on Linuxulator when we disable the qualification. */

args.push_back("-Q");
#endif
if (workaround_naclSystem_freebsd_disableQualification.Get()) {
Log::Warn("Disabling NaCL platform qualification on FreeBSD system.");
enableQualification = false;
}
#endif
}
else {
Log::Warn("Not using NaCl platform qualification.");
}

if (!enableQualification) {
args.push_back("-Q");
}

if (debug) {
args.push_back("-g");
}
Expand Down

0 comments on commit e2dedcc

Please sign in to comment.