Bundle and mount Linux kernel headers in WSL2 distros#40551
Conversation
|
/azp run wsl-github-pr |
|
Azure Pipelines successfully started running 1 pipeline(s). |
The Microsoft.WSL.Kernel nuget package ships UAPI headers alongside the kernel image. Stage those headers next to the kernel binary at build time and mount them read-only into every WSL2 distro via a 9p share at the standard Debian/Ubuntu paths (/usr/src/linux-headers-$(uname -r) plus a /lib/modules/$(uname -r)/build symlink). A new kernelHeaders= .wslconfig setting follows the same pattern as kernelModules: empty defaults to the bundled headers, "none" disables, or a custom path can be supplied. Surfaced in the wslsettings Developer page alongside the existing kernel/kernelModules controls. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- mini_init: drop redundant comments and the LOG_ERROR for MountPlan9 (UtilMount already logs via WIL); promote uname() to THROW_LAST_ERROR_IF. - distro init: drop the explanatory block comment, drop the "source" symlink (only "build" is the universal entry point queried by out-of-tree tooling), and use THROW_LAST_ERROR_IF for uname() under the existing CATCH_LOG(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The PR-feedback commit dropped creation of the
`/lib/modules/<release>/source` symlink (only `/build` is needed),
but a few sites still referenced the removed symlink:
- test/windows/UnitTests.cpp: KernelHeaders test still asserted
`test -L /lib/modules/$(uname -r)/source`, causing the unit test
to fail.
- src/linux/init/main.cpp: stale `{build,source} symlinks` comment.
- src/windows/service/exe/WslCoreVm.cpp: stale `/build and /source
symlinks` comment.
- doc/docs/technical-documentation/boot-process.md: stale
`{build,source}` reference in the boot-process docs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7e4cba5 to
57ce9b2
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds first-class support for shipping Linux kernel UAPI headers with WSL and making them available inside every WSL2 distro by default (mirroring existing kernel modules plumbing). It introduces a new .wslconfig setting (wsl2.kernelHeaders) plus service/guest/UI changes to stage, package, share, mount, and validate the headers.
Changes:
- Build/MSI: stage and package a
linux-headers/directory alongside the kernel and include it in the MSI install undertools/linux-headers. - Service/Config/UI: add a new
wsl2.kernelHeaderssetting, expose it in wslsettings, and validate it alongside existing custom kernel/kernelModules settings. - Guest init: add a new plan9 share and mount flow that places headers under
/usr/src/linux-headers-$(uname -r)/includeand creates/lib/modules/$(uname -r)/buildsymlink.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| UserConfig.cmake.sample | Adds dev-mode symlink staging for linux-headers into WSL_DEV_BINARY_PATH. |
| CMakeLists.txt | Defines WSL_KERNEL_HEADERS_PATH for dev builds to point at the staged headers directory. |
| msipackage/package.wix.in | Packages tools/linux-headers into the MSI (non-dev installs) and wires it into the main feature. |
| src/windows/common/WslCoreConfig.h | Adds wsl2.kernelHeaders config key and includes it in config serialization/reflection. |
| src/windows/common/WslCoreConfig.cpp | Parses wsl2.kernelHeaders, applies policy override, and validates safe-mode behavior. |
| src/windows/inc/WslCoreConfigInterface.h | Extends the public config-entry enum with KernelHeadersPath. |
| src/windows/libwsl/WslCoreConfigInterface.cpp | Adds get/set support for KernelHeadersPath through the libwsl config interface. |
| src/windows/service/exe/WslCoreVm.h | Adds state to track whether kernel headers should be mounted. |
| src/windows/service/exe/WslCoreVm.cpp | Resolves/validates headers path, adds a read-only plan9 share, and passes a mount flag to mini-init. |
| src/shared/inc/lxfsshares.h | Defines the new plan9 share name (kernel_headers). |
| src/shared/inc/lxinitshared.h | Adds env var names and a new MountKernelHeaders field to the mini-init config message. |
| src/linux/init/main.cpp | Mounts the kernel headers plan9 share and passes a target mount path to distro init via env vars. |
| src/linux/init/config.cpp | Moves the temporary headers mount to /usr/src/linux-headers-…/include and creates /lib/modules/.../build symlink. |
| test/windows/Common.h | Adds kernelHeaders to the test config defaults structure. |
| test/windows/Common.cpp | Emits kernelHeaders= into generated .wslconfig test content. |
| test/windows/UnitTests.cpp | Adds a KernelHeaders WSL2 unit test for default mount + error cases. |
| src/windows/wslsettings/LibWsl.cs | Adds KernelHeadersPath entry to the managed config enum used by the UI. |
| src/windows/wslsettings/Contracts/Services/IWslConfigService.cs | Marks KernelHeadersPath as a string-valued config setting in the UI layer. |
| src/windows/wslsettings/ViewModels/Settings/DeveloperViewModel.cs | Adds CustomKernelHeadersPath binding to the Developer page VM. |
| src/windows/wslsettings/Views/Settings/DeveloperPage.xaml | Adds the Developer page expander for kernel headers path editing/browsing. |
| src/windows/wslsettings/Views/Settings/DeveloperPage.xaml.cs | Adds a folder picker handler for selecting a headers directory. |
| src/windows/wslsettings/Helpers/RuntimeHelper.cs | Adds a WinUI folder picker helper (PickSingleFolderAsync). |
| src/windows/wslsettings/Views/Settings/SettingsApplyHelper.cs | Adds display-name mapping for the new kernel headers setting in pending-changes UI. |
| localization/strings/en-US/Resources.resw | Adds user-facing strings for validation errors and the new settings UI text. |
| doc/docs/technical-documentation/boot-process.md | Documents the new “mount bundled kernel headers” guest configuration behavior. |
Comments suppressed due to low confidence (2)
src/windows/service/exe/WslCoreVm.cpp:294
- If the bundled headers directory is missing/corrupt, this code will throw MessageCustomKernelHeadersNotFound(), which says the path was specified via 'wsl2.kernelHeaders' in the user’s .wslconfig. In the default-kernel + empty setting case the user didn’t specify anything, so the error is misleading and also blocks WSL startup. Consider distinguishing user-specified vs default-resolved paths (or treating missing bundled headers as a non-fatal warning that disables the mount).
if (m_vmConfig.KernelHeadersPath.empty())
{
if (m_defaultKernel)
{
#ifdef WSL_KERNEL_HEADERS_PATH
m_vmConfig.KernelHeadersPath = std::wstring(TEXT(WSL_KERNEL_HEADERS_PATH));
#else
m_vmConfig.KernelHeadersPath = m_installPath / LXSS_TOOLS_DIRECTORY / L"linux-headers";
#endif
}
}
else if (m_defaultKernel)
{
THROW_HR_WITH_USER_ERROR(WSL_E_CUSTOM_KERNEL_NOT_FOUND, Localization::MessageMismatchedKernelHeadersError());
}
if (!m_vmConfig.KernelHeadersPath.empty())
{
if (!wsl::windows::common::filesystem::FileExists(m_vmConfig.KernelHeadersPath.c_str()))
{
THROW_HR_WITH_USER_ERROR(
WSL_E_CUSTOM_KERNEL_NOT_FOUND,
Localization::MessageCustomKernelHeadersNotFound(
wsl::windows::common::helpers::GetWslConfigPath(m_userToken.get()), m_vmConfig.KernelHeadersPath.c_str()));
}
m_mountKernelHeaders = true;
}
src/windows/service/exe/WslCoreVm.cpp:292
- KernelHeadersPath is expected to be a directory tree, but this validation only checks GetFileAttributes != INVALID (via FileExists), so a regular file path would pass and later Plan9 sharing/mounting may fail. Consider validating that the path exists and is a directory (FILE_ATTRIBUTE_DIRECTORY) to fail fast with a clearer user error.
if (!m_vmConfig.KernelHeadersPath.empty())
{
if (!wsl::windows::common::filesystem::FileExists(m_vmConfig.KernelHeadersPath.c_str()))
{
THROW_HR_WITH_USER_ERROR(
WSL_E_CUSTOM_KERNEL_NOT_FOUND,
Localization::MessageCustomKernelHeadersNotFound(
wsl::windows::common::helpers::GetWslConfigPath(m_userToken.get()), m_vmConfig.KernelHeadersPath.c_str()));
}
| if (ConfigMessage->MountKernelHeaders) | ||
| { | ||
| utsname UnameBuffer{}; | ||
| THROW_LAST_ERROR_IF(uname(&UnameBuffer) < 0); |
There was a problem hiding this comment.
nit: config.cpp also seems to have a uname syscall being made to get the release. We should try to share the result instead of making another syscall, even though it is a cheaper syscall
| if (m_mountKernelHeaders) | ||
| { | ||
| constexpr auto flags = (hcs::Plan9ShareFlags::ReadOnly | hcs::Plan9ShareFlags::AllowOptions); | ||
| wsl::windows::common::hcs::AddPlan9Share( |
There was a problem hiding this comment.
why not use the same addShare lambda that is being used above for GPU shares?
|
A few minor comments, LGTM otherwise |
ptrivedi
left a comment
There was a problem hiding this comment.
a couple of minor comments, but will approve
Summary
Bundles the Linux kernel UAPI headers shipped in the
Microsoft.WSL.Kernelnuget package and mounts them read-only into every WSL2 distro by default. Pattern mirrors the existingkernelModulesplumbing.What it does
linux-headers/next to the kernel image in the dev binary path (CMakeLists.txt,UserConfig.cmake.sample) and packages them in the MSI (msipackage/package.wix.in).kernelHeaders=setting in.wslconfig(empty = bundled default, or a custom path). Validated alongsidekernel/kernelModulesinWslCoreVm./usr/src/linux-headers-$(uname -r)with a/lib/modules/$(uname -r)/buildsymlink so out-of-tree module builds and tools that key offuname -rfind them.Testing
KernelHeadersTAEF unit test covers default-mount, custom path, and validation errors./usr/src/linux-headers-*populated andmake headers_checkworks against it.