2025-05-15 12:09:45 -07:00
/*++
Copyright ( c ) Microsoft . All rights reserved .
Module Name :
PolicyTests . cpp
Abstract :
This file contains test cases for WSL policies .
- - */
# include "precomp.h"
# include "Common.h"
# include "registry.hpp"
# include "wslpolicies.h"
using namespace wsl : : windows : : policies ;
using namespace wsl : : windows : : common : : registry ;
class PolicyTest
{
WSL_TEST_CLASS ( PolicyTest )
bool m_initialized = false ;
TEST_CLASS_SETUP ( TestClassSetup )
{
const auto policies = OpenKey ( HKEY_LOCAL_MACHINE , ROOT_POLICIES_KEY , KEY_CREATE_SUB_KEY , 0 ) ;
VERIFY_IS_TRUE ( ! ! policies ) ;
const auto wslPolicies = CreateKey ( policies . get ( ) , L " WSL " ) ;
VERIFY_IS_TRUE ( ! ! wslPolicies ) ;
VERIFY_ARE_EQUAL ( LxsstuInitialize ( FALSE ) , TRUE ) ;
m_initialized = true ;
return true ;
}
TEST_CLASS_CLEANUP ( TestClassCleanup )
{
if ( m_initialized )
{
LxsstuUninitialize ( FALSE ) ;
}
return true ;
}
static auto SetPolicy ( LPCWSTR Name , DWORD Value )
{
return RegistryKeyChange ( HKEY_LOCAL_MACHINE , c_registryKey , Name , Value ) ;
}
static void ValidateWarnings ( const std : : wstring & expectedWarnings , bool pattern = false )
{
auto [ output , warnings ] = LxsstuLaunchWslAndCaptureOutput ( L " echo ok " ) ;
VERIFY_ARE_EQUAL ( L " ok \n " , output ) ;
if ( pattern )
{
if ( ! PathMatchSpec ( warnings . c_str ( ) , expectedWarnings . c_str ( ) ) )
{
LogError ( " Warning '%ls' didn't match pattern '%ls' " , warnings . c_str ( ) , expectedWarnings . c_str ( ) ) ;
VERIFY_FAIL ( ) ;
}
}
else
{
VERIFY_ARE_EQUAL ( expectedWarnings , warnings ) ;
}
} ;
TEST_METHOD ( MountPolicyAllowed )
{
SKIP_TEST_ARM64 ( ) ;
WSL2_TEST_ONLY ( ) ;
auto revert = SetPolicy ( c_allowDiskMount , 1 ) ;
ValidateOutput (
L " --mount DoesNotExist " ,
L " Failed to attach disk 'DoesNotExist' to WSL2: The system cannot find the file specified. \r \n "
L " Error code: Wsl/Service/AttachDisk/MountDisk/HCS/ERROR_FILE_NOT_FOUND \r \n " ) ;
}
TEST_METHOD ( MountPolicyDisabled )
{
SKIP_TEST_ARM64 ( ) ;
WSL2_TEST_ONLY ( ) ;
auto revert = SetPolicy ( c_allowDiskMount , 0 ) ;
ValidateOutput (
L " --mount DoesNotExist " ,
L " wsl.exe --mount is disabled by the computer policy. \r \n Error code: Wsl/Service/WSL_E_DISK_MOUNT_DISABLED \r \n " ) ;
}
void ValidatePolicy ( LPCWSTR Name , LPCWSTR Config , LPCWSTR ExpectedWarnings , const std : : function < void ( DWORD ) > & Validate = [ ] ( auto ) { } )
{
WslConfigChange config ( LxssGenerateTestConfig ( ) + Config ) ;
// Validate behavior with policy allowed
{
auto revert = SetPolicy ( Name , 1 ) ;
WslShutdown ( ) ;
ValidateWarnings ( L " " ) ; // Expect no warnings
Validate ( 1 ) ;
}
// Validate behavior with policy disabled
{
auto revert = SetPolicy ( Name , 0 ) ;
WslShutdown ( ) ;
ValidateWarnings ( ExpectedWarnings ) ;
Validate ( 0 ) ;
}
// Validate behavior with an invalid policy value
{
auto revert = SetPolicy ( Name , 12 ) ;
WslShutdown ( ) ;
ValidateWarnings ( L " " ) ;
Validate ( 12 ) ;
}
}
TEST_METHOD ( KernelCommandLine )
{
WSL2_TEST_ONLY ( ) ;
auto validate = [ ] ( DWORD policyValue ) {
auto [ commandLine , _ ] = LxsstuLaunchWslAndCaptureOutput ( L " cat /proc/cmdline " ) ;
if ( policyValue = = 0 )
{
VERIFY_IS_FALSE ( commandLine . find ( L " dummy-cmd-arg " ) ! = std : : wstring : : npos ) ;
}
else
{
VERIFY_IS_TRUE ( commandLine . find ( L " dummy-cmd-arg " ) ! = std : : wstring : : npos ) ;
}
} ;
ValidatePolicy (
c_allowCustomKernelCommandLineUserSetting ,
L " kernelCommandLine=dummy-cmd-arg " ,
L " wsl: The .wslconfig setting 'wsl2.kernelCommandLine' is disabled by the computer policy. \r \n " ,
validate ) ;
}
TEST_METHOD ( NestedVirtualization )
{
SKIP_TEST_ARM64 ( ) ;
WSL2_TEST_ONLY ( ) ;
WINDOWS_11_TEST_ONLY ( ) ;
ValidatePolicy (
c_allowNestedVirtualizationUserSetting ,
L " nestedVirtualization=true " ,
L " wsl: The .wslconfig setting 'wsl2.nestedVirtualization' is disabled by the computer policy. \r \n " ) ;
}
TEST_METHOD ( KernelDebugging )
{
WSL2_TEST_ONLY ( ) ;
WINDOWS_11_TEST_ONLY ( ) ;
ValidatePolicy (
c_allowKernelDebuggingUserSetting ,
L " kernelDebugPort=1234 " ,
L " wsl: The .wslconfig setting 'wsl2.kernelDebugPort' is disabled by the computer policy. \r \n " ) ;
}
TEST_METHOD ( CustomKernel )
{
WSL2_TEST_ONLY ( ) ;
const std : : wstring wslConfigPath = wsl : : windows : : common : : helpers : : GetWslConfigPath ( ) ;
const std : : wstring nonExistentFile = L " DoesNotExist " ;
WslConfigChange config ( LxssGenerateTestConfig ( { . kernel = nonExistentFile . c_str ( ) , . kernelModules = nonExistentFile . c_str ( ) } ) ) ;
{
auto revert = SetPolicy ( c_allowCustomKernelUserSetting , 1 ) ;
WslShutdown ( ) ;
ValidateOutput (
L " echo ok " ,
std : : format (
L " {} \r \n Error code: Wsl/Service/CreateInstance/CreateVm/WSL_E_CUSTOM_KERNEL_NOT_FOUND \r \n " ,
wsl : : shared : : Localization : : MessageCustomKernelNotFound ( wslConfigPath , nonExistentFile ) ) ) ;
}
// Disable the custom kernel policy and validate that the expected warnings are shown.
{
auto revert = SetPolicy ( c_allowCustomKernelUserSetting , 0 ) ;
WslShutdown ( ) ;
const auto kernelWarning =
2025-05-21 13:48:14 -04:00
std : : format ( L " wsl: {} \r \n " , wsl : : shared : : Localization : : MessageSettingOverriddenByPolicy ( L " wsl2.kernel " ) ) ;
2025-05-15 12:09:45 -07:00
const auto modulesWarning =
2025-05-21 13:48:14 -04:00
std : : format ( L " wsl: {} \r \n " , wsl : : shared : : Localization : : MessageSettingOverriddenByPolicy ( L " wsl2.kernelModules " ) ) ;
2025-05-15 12:09:45 -07:00
ValidateWarnings ( std : : format ( L " {}{} " , kernelWarning , modulesWarning ) ) ;
config . Update ( LxssGenerateTestConfig ( { . kernel = nonExistentFile . c_str ( ) } ) ) ;
ValidateWarnings ( kernelWarning ) ;
config . Update ( LxssGenerateTestConfig ( { . kernelModules = nonExistentFile . c_str ( ) } ) ) ;
ValidateWarnings ( modulesWarning ) ;
}
}
TEST_METHOD ( CustomSystemDistro )
{
WSL2_TEST_ONLY ( ) ;
WslConfigChange config ( LxssGenerateTestConfig ( ) + L " systemDistro=DoesNotExist " ) ;
const std : : wstring wslConfigPath = wsl : : windows : : common : : helpers : : GetWslConfigPath ( ) ;
{
auto revert = SetPolicy ( c_allowCustomSystemDistroUserSetting , 1 ) ;
WslShutdown ( ) ;
ValidateOutput (
L " echo ok " ,
L " The custom system distribution specified in " + wslConfigPath +
L " was not found or is not the correct format. \r \n Error code: "
L " Wsl/Service/CreateInstance/CreateVm/WSL_E_CUSTOM_SYSTEM_DISTRO_ERROR \r \n " ) ;
}
{
auto revert = SetPolicy ( c_allowCustomSystemDistroUserSetting , 0 ) ;
WslShutdown ( ) ;
ValidateWarnings ( L " wsl: The .wslconfig setting 'wsl2.systemDistro' is disabled by the computer policy. \r \n " ) ;
}
}
TEST_METHOD ( CustomNetworkingMode )
{
WSL2_TEST_ONLY ( ) ;
WslConfigChange config ( LxssGenerateTestConfig ( { . networkingMode = wsl : : core : : NetworkingMode : : VirtioProxy } ) ) ;
{
auto revert = SetPolicy ( c_allowCustomNetworkingModeUserSetting , 1 ) ;
WslShutdown ( ) ;
ValidateWarnings ( L " " ) ;
}
{
auto revertCustomMode = SetPolicy ( c_allowCustomNetworkingModeUserSetting , 0 ) ;
WslShutdown ( ) ;
ValidateWarnings ( L " wsl: The .wslconfig setting 'wsl2.networkingMode' is disabled by the computer policy. \r \n " ) ;
// Validate that no warnings are shown for NAT or None
config . Update ( LxssGenerateTestConfig ( { . networkingMode = wsl : : core : : NetworkingMode : : Nat } ) ) ;
ValidateWarnings ( L " " ) ;
config . Update ( LxssGenerateTestConfig ( { . networkingMode = wsl : : core : : NetworkingMode : : None } ) ) ;
ValidateWarnings ( L " " ) ;
// Validate that no warnings are shown if the default networking mode is set to the same value as .wslconfig.
auto revertDefault = SetPolicy ( c_defaultNetworkingMode , static_cast < DWORD > ( wsl : : core : : NetworkingMode : : VirtioProxy ) ) ;
config . Update ( LxssGenerateTestConfig ( { . networkingMode = wsl : : core : : NetworkingMode : : VirtioProxy } ) ) ;
ValidateWarnings ( L " " ) ;
}
}
TEST_METHOD ( DebugShell )
{
WSL2_TEST_ONLY ( ) ;
auto revert = SetPolicy ( c_allowDebugShellUserSetting , 0 ) ;
WslShutdown ( ) ;
// Only testing the negative case since the debug shell is difficult to programatically exit.
WslKeepAlive keepAlive ;
ValidateOutput ( L " --debug-shell " , L " The debug shell is disabled by the computer policy. \r \n " , L " " , 1 ) ;
}
TEST_METHOD ( WSL1 )
{
// Test policy registry key with allow key explicitly set.
{
auto revert = SetPolicy ( c_allowWSL1 , 1 ) ;
WslShutdown ( ) ;
ValidateWarnings ( L " " ) ;
}
// Disable WSL1.
{
auto revert = SetPolicy ( c_allowWSL1 , 0 ) ;
WslShutdown ( ) ;
// If running as WSL2, attempt to convert the distro to WSL1. If running as WSL1, attempt to run a command.
if ( LxsstuVmMode ( ) )
{
ValidateOutput (
L " --set-version " LXSS_DISTRO_NAME_TEST_L L " 1 " ,
L " WSL1 is disabled by the computer policy. \r \n Error code: Wsl/Service/WSL_E_WSL1_DISABLED \r \n " ) ;
}
else
{
ValidateOutput (
L " echo ok " ,
L " WSL1 is disabled by the computer policy. \r \n Please run 'wsl.exe --set-version " LXSS_DISTRO_NAME_TEST_L L " 2' to upgrade to WSL2. \r \n Error code: Wsl/Service/CreateInstance/WSL_E_WSL1_DISABLED \r \n " ) ;
}
}
}
TEST_METHOD ( DisableWsl )
{
// N.B. Modifying one of the policy registry keys triggers a registry watcher in the service.
// Retry for up to 30 seconds to ensure the registry watcher has time to take effect.
auto createInstance = [ & ] ( HRESULT expectedResult ) {
HRESULT result ;
const auto stop = std : : chrono : : steady_clock : : now ( ) + std : : chrono : : seconds { 30 } ;
for ( ; ; )
{
Microsoft : : WRL : : ComPtr < ILxssUserSession > session ;
result = CoCreateInstance ( CLSID_LxssUserSession , nullptr , CLSCTX_LOCAL_SERVER , IID_PPV_ARGS ( & session ) ) ;
if ( result = = expectedResult | | std : : chrono : : steady_clock : : now ( ) > stop )
{
break ;
}
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds { 250 } ) ;
}
VERIFY_ARE_EQUAL ( expectedResult , result ) ;
if ( SUCCEEDED ( result ) )
{
VERIFY_ARE_EQUAL ( LxsstuLaunchWsl ( L " /bin/true " ) , 0u ) ;
}
else
{
auto [ output , _ ] = LxsstuLaunchWslAndCaptureOutput ( L " /bin/true " , - 1 ) ;
VERIFY_ARE_EQUAL (
output ,
L " This program is blocked by group policy. For more information, contact your system administrator. "
L " \r \n Error "
L " code: Wsl/ERROR_ACCESS_DISABLED_BY_POLICY \r \n " ) ;
}
} ;
// Set the policy registry key and validate that user session creation returns the expected result,
// then delete the key and ensure user session can be created.
auto testPolicy = [ & ] ( LPCWSTR policy , HRESULT expectedResult , bool restartService ) {
{
auto revert = SetPolicy ( policy , 0 ) ;
if ( restartService )
{
RestartWslService ( ) ;
}
createInstance ( expectedResult ) ;
}
if ( restartService )
{
RestartWslService ( ) ;
}
createInstance ( S_OK ) ;
} ;
for ( const auto restartService : { false , true } )
{
// Ensure the top-level disable WSL policy works.
testPolicy ( wsl : : windows : : policies : : c_allowWSL , HRESULT_FROM_WIN32 ( ERROR_ACCESS_DISABLED_BY_POLICY ) , restartService ) ;
// Verfiy the disable inbox WSL policy does not block lifted.
testPolicy ( wsl : : windows : : policies : : c_allowInboxWSL , S_OK , restartService ) ;
}
// Delete and recreate the key without restarting the service to ensure the registry watcher continues to work.
wsl : : windows : : common : : registry : : DeleteKey ( HKEY_LOCAL_MACHINE , wsl : : windows : : policies : : c_registryKey ) ;
auto key = wsl : : windows : : common : : registry : : CreateKey ( HKEY_LOCAL_MACHINE , wsl : : windows : : policies : : c_registryKey ) ;
testPolicy ( wsl : : windows : : policies : : c_allowWSL , HRESULT_FROM_WIN32 ( ERROR_ACCESS_DISABLED_BY_POLICY ) , false ) ;
}
TEST_METHOD ( DefaultNetworkingMode )
{
WSL2_TEST_ONLY ( ) ;
WslConfigChange config ( LxssGenerateTestConfig ( ) ) ;
{
auto revert = SetPolicy ( c_defaultNetworkingMode , static_cast < DWORD > ( wsl : : core : : NetworkingMode : : None ) ) ;
WslShutdown ( ) ;
VERIFY_ARE_EQUAL ( LxsstuLaunchWsl ( L " wslinfo --networking-mode | grep -iF 'none' " ) , 0u ) ;
}
{
auto revert = SetPolicy ( c_defaultNetworkingMode , static_cast < DWORD > ( wsl : : core : : NetworkingMode : : VirtioProxy ) ) ;
WslShutdown ( ) ;
VERIFY_ARE_EQUAL ( LxsstuLaunchWsl ( L " wslinfo --networking-mode | grep -iF 'virtioproxy' " ) , 0u ) ;
}
}
} ;