Skip to content

fix(init): prevent binfmt_misc handler loss on distro termination#14443

Draft
yeelam-gordon wants to merge 5 commits intomicrosoft:masterfrom
yeelam-gordon:workitem/13885-fix-binfmt-config-cleared-on-termination
Draft

fix(init): prevent binfmt_misc handler loss on distro termination#14443
yeelam-gordon wants to merge 5 commits intomicrosoft:masterfrom
yeelam-gordon:workitem/13885-fix-binfmt-config-cleared-on-termination

Conversation

@yeelam-gordon
Copy link

@yeelam-gordon yeelam-gordon commented Mar 16, 2026

Summary of the Pull Request

This pull request enhances the way WSL registers and protects its binfmt_misc interpreter, especially for WSL2 (VM) environments. The changes introduce a more robust and systemd-friendly approach to managing binfmt configuration files and service overrides, and add comprehensive unit tests to verify correct behavior across various scenarios.

Binfmt_misc registration and systemd integration improvements:

  • Added a new macro, BINFMT_INTEROP_REGISTRATION_STRING_VM, to generate the binfmt registration string with both the 'F' (fix-binary) and 'P' (preserve Argv[0]) flags for WSL2, ensuring the interpreter is accessible across namespaces and chroots.
  • Updated binfmt registration in main.cpp to use the new macro, aligning the registration string with WSL2 requirements.

Systemd override and configuration file handling:

  • Refactored the logic in init.cpp to:
    • Write the binfmt registration string to /run/binfmt.d/WSLInterop.conf.
    • Install systemd override files for systemd-binfmt.service, binfmt-support.service, and proc-sys-fs-binfmt_misc.mount to ensure proper registration and prevent accidental removal or unmounting during shutdown.

Unit test enhancements:

  • Added new tests in UnitTests.cpp to validate the creation and contents of the generated binfmt configuration and override files, and to check their correct removal and regeneration in response to systemd service restarts and configuration changes.

PR Checklist

Validation Steps Performed

  • CMake configure + build of init and wsltests targets succeeded
  • Unit tests verify generated binfmt files and overrides
  • protectBinfmt=false path preserved

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Fixes the issue where terminating a WSL2 distro with systemd clears
binfmt_misc handlers across all running distros. Changes:
- Add mount unit override to prevent binfmt_misc unmount during shutdown
- Fix FP vs P flag inconsistency in WSLInterop registration
- Use declarative /run/binfmt.d/ approach for handler persistence

Fixes microsoft#13885

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 16, 2026 10:53
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a WSL2/systemd shutdown interaction where terminating one distro could clear binfmt_misc handlers (including WSLInterop) for other running distros, breaking Windows interop.

Changes:

  • Generate a declarative systemd-binfmt config in /run/binfmt.d/WSLInterop.conf for persistent WSLInterop registration.
  • Add systemd generator drop-in overrides to prevent systemd-binfmt stop actions and keep binfmt_misc mounted during shutdown.
  • Align the WSLInterop binfmt registration string to consistently use :FP.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
test/windows/UnitTests.cpp Adds assertions that the generated /run/binfmt.d config and systemd generator overrides exist (and stay consistent across restarts/reloads), and that protectBinfmt=false disables generation.
src/linux/init/main.cpp Switches BINFMT_REGISTER_STRING to use the shared registration macro (now consistent with :FP).
src/linux/init/init.cpp Implements generation of /run/binfmt.d/WSLInterop.conf and generator drop-ins for systemd-binfmt and the binfmt_misc mount unit when protectBinfmt is enabled.
src/linux/init/binfmt.h Updates the shared binfmt registration macro to use :FP (fixing prior inconsistency).

You can also share your feedback on Copilot code review. Take the survey.

Introduce BINFMT_INTEROP_REGISTRATION_STRING_VM with ':FP' flags for
WSL2-only paths (mini_init, systemd generator), while keeping the
original BINFMT_INTEROP_REGISTRATION_STRING with ':P' for WSL1 (lxcore)
which does not support the 'F' (fix-binary) flag.

- binfmt.h: Add _VM variant macro with ':FP', restore base macro to ':P'
- main.cpp: Use _VM macro for mini_init registration (WSL2 only)
- init.cpp: Use _VM macro for /run/binfmt.d/ config (systemd, WSL2 only)
- config.cpp: Unchanged, continues using ':P' for WSL1 registration

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@yeelam-gordon
Copy link
Author

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prevents binfmt_misc handlers (notably WSLInterop) from being cleared across other running WSL2 distros when one systemd-enabled distro is terminated, by changing how WSL registers/preserves the handler and how systemd shutdown interacts with the binfmt_misc mount.

Changes:

  • Switch WSL interop registration in VM paths to use an explicit :FP flag combination and centralize the VM registration string.
  • Generate /run/binfmt.d/WSLInterop.conf and systemd generator drop-ins to avoid handler removal during shutdown and keep binfmt_misc mounted late into shutdown.
  • Extend Windows unit tests to validate the generated config/drop-in files and their persistence across restarts/daemon-reload.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/linux/init/binfmt.h Introduces VM-specific binfmt registration string (:FP) and documents flag semantics/constraints.
src/linux/init/main.cpp Updates VM-side registration to use the new VM registration string macro.
src/linux/init/init.cpp Generates /run/binfmt.d config and new systemd generator drop-ins to prevent shutdown unmount/handler removal when protectBinfmt=true.
test/windows/UnitTests.cpp Adds assertions validating the generated binfmt config and generator drop-ins across restarts/service reloads and the protectBinfmt=false path.

The pure declarative approach via /run/binfmt.d/ does not handle conflicting
binfmt configs. When a distro installs its own binfmt handler with the same
name (WSLInterop), systemd-binfmt processes files alphabetically, potentially
letting the conflicting handler win.

Add ExecStartPost to the systemd-binfmt service override that forcefully
unregisters any conflicting handler and re-registers WSL's after
systemd-binfmt's normal ExecStart completes. This combines declarative config
for proper re-registration with imperative override for guaranteed priority.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@yeelam-gordon
Copy link
Author

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copilot AI review requested due to automatic review settings March 24, 2026 01:58
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment on lines +368 to 400
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);

Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/run/systemd/generator is regenerated on daemon-reload, but /run/binfmt.d/WSLInterop.conf is written outside the generator tree and is never removed when protectBinfmt or interopEnabled are later turned off. If a user flips protectBinfmt=false in /etc/wsl.conf and runs systemctl daemon-reload (or restarts systemd-binfmt), the stale /run/binfmt.d/WSLInterop.conf will continue to override distro binfmt config. Consider deleting /run/binfmt.d/WSLInterop.conf (and optionally the directory if empty) when the feature is disabled, so config changes take effect without requiring a full distro restart.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

binfmt config cleared on all distro when distro with systemd is terminated

3 participants