Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/linux/init/binfmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ Module Name:
#define LX_INIT_BINFMT_NAME "WSLInterop"
#define BINFMT_MISC_MOUNT_TARGET "/proc/sys/fs/binfmt_misc"
#define BINFMT_MISC_REGISTER_FILE BINFMT_MISC_MOUNT_TARGET "/register"

//
// N.B. The 'P' flag preserves Argv[0]. The 'F' flag (fix-binary) opens the interpreter at
// registration time, making it available across mount namespaces and chroot environments.
// WSL1 (lxcore) does not support the 'F' flag, so it must only be used in WSL2 (VM) paths.
//

#define BINFMT_INTEROP_REGISTRATION_STRING(Name) ":" Name ":M::MZ::" LX_INIT_PATH ":P"
#define BINFMT_INTEROP_REGISTRATION_STRING_VM(Name) ":" Name ":M::MZ::" LX_INIT_PATH ":FP"

int CreateNtProcess(int Argc, char* Argv[]);
44 changes: 37 additions & 7 deletions src/linux/init/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,34 +365,64 @@ ExecStart=/bin/mount -o bind,ro,X-mount.mkdir -t none /mnt/wslg/.X11-unix /tmp/.

if (interopEnabled && protectBinfmt)
{
// N.B. ExecStop is required to prevent distributions from removing the WSL binfmt entry on shutdown.
auto systemdBinfmtContent = std::format(
R"(# Note: This file is generated by WSL to prevent binfmt.d from overriding WSL's binfmt interpreter.
constexpr auto* binfmtConfigDirectory = "/run/binfmt.d";
constexpr auto* binfmtConfigPath = "/run/binfmt.d/WSLInterop.conf";
const auto binfmtConfigContent = std::format("{}\n", BINFMT_INTEROP_REGISTRATION_STRING_VM(LX_INIT_BINFMT_NAME));
const auto serviceOverrideContent = std::format(
R"(# Note: This file is generated by WSL to prevent distributions from removing the WSL binfmt entry on shutdown.
# To disable this unit, add the following to /etc/wsl.conf:
# [boot]
# protectBinfmt=false

[Service]
ExecStop=
ExecStart=/bin/sh -c '(echo -1 > {}/{}) ; (echo "{}" > {})' )",
ExecStartPost=/bin/sh -c '(echo -1 > {}/{} 2>/dev/null || true) ; echo "{}" > {}'
)",
BINFMT_MISC_MOUNT_TARGET,
LX_INIT_BINFMT_NAME,
BINFMT_INTEROP_REGISTRATION_STRING(LX_INIT_BINFMT_NAME),
BINFMT_INTEROP_REGISTRATION_STRING_VM(LX_INIT_BINFMT_NAME),
BINFMT_MISC_REGISTER_FILE);
constexpr auto* mountOverrideContent = R"(# Note: This file is generated by WSL to keep binfmt_misc mounted during shutdown.
# To disable this unit, add the following to /etc/wsl.conf:
# [boot]
# protectBinfmt=false

[Unit]
DefaultDependencies=no
Before=umount.target

[Mount]
Options=nosuid,nodev,noexec
)";

THROW_LAST_ERROR_IF(UtilMkdirPath(binfmtConfigDirectory, 0755) < 0);
THROW_LAST_ERROR_IF(WriteToFile(binfmtConfigPath, binfmtConfigContent.c_str()) < 0);

Comment thread
yeelam-gordon marked this conversation as resolved.
// Install the override for systemd-binfmt.service.
{
auto overrideFolder = std::format("{}/systemd-binfmt.service.d", installPath);
THROW_LAST_ERROR_IF(UtilMkdirPath(overrideFolder.c_str(), 0755) < 0);
THROW_LAST_ERROR_IF(WriteToFile(std::format("{}/override.conf", overrideFolder).c_str(), systemdBinfmtContent.c_str()) < 0);
THROW_LAST_ERROR_IF(WriteToFile(std::format("{}/override.conf", overrideFolder).c_str(), serviceOverrideContent.c_str()) < 0);
}

// Install the override for binfmt-support.service.
{
auto overrideFolder = std::format("{}/binfmt-support.service.d", installPath);
THROW_LAST_ERROR_IF(UtilMkdirPath(overrideFolder.c_str(), 0755) < 0);
THROW_LAST_ERROR_IF(WriteToFile(std::format("{}/override.conf", overrideFolder).c_str(), systemdBinfmtContent.c_str()) < 0);
THROW_LAST_ERROR_IF(WriteToFile(std::format("{}/override.conf", overrideFolder).c_str(), serviceOverrideContent.c_str()) < 0);
}

// Install the override for proc-sys-fs-binfmt_misc.mount.
{
auto overrideFolder = std::format("{}/proc-sys-fs-binfmt_misc.mount.d", installPath);
THROW_LAST_ERROR_IF(UtilMkdirPath(overrideFolder.c_str(), 0755) < 0);
THROW_LAST_ERROR_IF(WriteToFile(std::format("{}/override.conf", overrideFolder).c_str(), mountOverrideContent) < 0);
}
Comment thread
yeelam-gordon marked this conversation as resolved.
}
else
{
// Clean up stale binfmt config if the feature was previously enabled.
unlink("/run/binfmt.d/WSLInterop.conf");
Comment thread
yeelam-gordon marked this conversation as resolved.
}

return 0;
Expand Down
2 changes: 1 addition & 1 deletion src/linux/init/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Module Name:
#include "SocketChannel.h"

#define BSDTAR_PATH "/usr/bin/bsdtar"
#define BINFMT_REGISTER_STRING ":" LX_INIT_BINFMT_NAME ":M::MZ::" LX_INIT_PATH ":FP\n"
#define BINFMT_REGISTER_STRING BINFMT_INTEROP_REGISTRATION_STRING_VM(LX_INIT_BINFMT_NAME) "\n"
#define BINFMT_PATH PROCFS_PATH "/sys/fs/binfmt_misc"
#define CHRONY_CONF_PATH ETC_PATH "/chrony.conf"
#define CHRONYD_PATH "/sbin/chronyd"
Expand Down
23 changes: 23 additions & 0 deletions test/windows/UnitTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,28 +375,51 @@ class UnitTests
VERIFY_ARE_EQUAL(cmdOutput, L"ok\r\n");
};

auto validateGeneratedBinfmtFiles = []() {
auto [binfmtConfig, _] = LxsstuLaunchWslAndCaptureOutput(L"cat /run/binfmt.d/WSLInterop.conf");
VERIFY_ARE_EQUAL(binfmtConfig, L":WSLInterop:M::MZ::/init:FP\n");

auto [serviceOverride, __] =
LxsstuLaunchWslAndCaptureOutput(L"cat /run/systemd/generator/systemd-binfmt.service.d/override.conf");
VERIFY_IS_TRUE(serviceOverride.find(L"ExecStop=") != std::wstring::npos);
VERIFY_IS_TRUE(serviceOverride.find(L"ExecStartPost=") != std::wstring::npos);

auto [mountOverride, ___] =
Comment thread
yeelam-gordon marked this conversation as resolved.
LxsstuLaunchWslAndCaptureOutput(L"cat /run/systemd/generator/proc-sys-fs-binfmt_misc.mount.d/override.conf");
VERIFY_IS_TRUE(mountOverride.find(L"DefaultDependencies=no") != std::wstring::npos);
VERIFY_IS_TRUE(mountOverride.find(L"Before=umount.target") != std::wstring::npos);
};

validateGeneratedBinfmtFiles();
validateBinfmt();

// Validate that this still works after restarting the distribution.
TerminateDistribution();
validateGeneratedBinfmtFiles();
validateBinfmt();

// Validate that stopping or restarting systemd-binfmt doesn't break interop.
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"systemctl stop systemd-binfmt.service"), 0u);
validateBinfmt();

VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"systemctl restart systemd-binfmt.service"), 0u);
validateGeneratedBinfmtFiles();
validateBinfmt();

// Validate that the unit is regenerated after a daemon-reload.
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"systemctl daemon-reload && systemctl restart systemd-binfmt.service"), 0u);
validateGeneratedBinfmtFiles();
validateBinfmt();
}

{
// Enable systemd (restarts distro).
auto cleanupSystemd = EnableSystemd("protectBinfmt=false");

VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"test -e /run/binfmt.d/WSLInterop.conf"), 1u);
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"test -e /run/systemd/generator/systemd-binfmt.service.d/override.conf"), 1u);
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"test -e /run/systemd/generator/proc-sys-fs-binfmt_misc.mount.d/override.conf"), 1u);

// Validate that WSL's binfmt interpreter is overridden
auto [output, _] = LxsstuLaunchWslAndCaptureOutput(L"cmd.exe /c echo ok");
VERIFY_IS_TRUE(wsl::shared::string::IsEqual(output, L"/mnt/c/Windows/system32/cmd.exe cmd.exe /c echo ok\n", true));
Expand Down