WSL2 TUN Module Version Mismatch: Diagnosis and Fix

Problem

Running modprobe tun in WSL2 fails with:

modprobe: ERROR: could not insert 'tun': Exec format error

If you’re encountering this error, the solution and diagnostic approach in this article apply to you.

Quick Fix

Extract modules.vhd from a newer WSL package and replace the outdated version in the system directory:

# Windows side (Administrator)
# 1. Backup the old version
Copy-Item "C:\Program Files\WSL\tools\modules.vhd" "C:\Users\<user>\Downloads\modules.vhd.bak"

# 2. Extract modules.vhd from the new WSL package (unpack msixbundle → msi → extract)
# 3. Replace
Copy-Item "new-modules.vhd" "C:\Program Files\WSL\tools\modules.vhd" -Force

# 4. Restart WSL
wsl --shutdown
# Verify in WSL
sudo modprobe tun && echo "MODULE LOAD OK"

Root Cause

WSL2 kernel and module version mismatch:

ComponentVersionNote
Kernel (kernel)6.18.33.2Manually updated
modules.vhd (module package)6.6.87.2Missed, outdated

When updating the WSL2 kernel previously, only kernel and system.vhd were replaced — modules.vhd was missed. The kernel is new, but tun.ko’s vermagic is still the old version (6.6.87.2), so the kernel refuses to load it — this is the real cause of Exec format error.

Diagnostic Chain

modprobe tun → Exec format error
  ↓
CONFIG_TUN=m → Kernel supports TUN, not "kernel limitation"
  ↓
modinfo tun.ko → vermagic: 6.6.87.2
uname -r      → 6.18.33.2
  ↓ Version mismatch → vermagic mismatch
  ↓
Trace operation history: only replaced kernel + system.vhd, missed modules.vhd

Key insight: In the kernel module context, Exec format error ≠ file corruption, it’s vermagic mismatch (kernel refuses to load a .ko file with the wrong version).

Exec format Error Diagnostic Framework

When modprobe reports Exec format error, rule out in order:

CheckMethodRoot Cause
① vermagic versionmodinfo xxx.ko | grep vermagic vs uname -rKernel/module version mismatch (this case)
② vermagic suffixCompare full vermagic string (e.g., -tegra suffix)CONFIG_LOCALVERSION mismatch
③ Source versiongit log vs uname -r corresponding versionWrong source version used to compile module
④ GCC versiongcc --version vs kernel build GCCMultiple GCC versions causing vermagic difference
⑤ Symbol tabledmesg | grep "disagrees about version"Kernel ABI changed, module needs recompilation
⑥ depmodsudo depmod -a then retryDependency index not updated after manual module install

WSL2 Module Loading Mechanism

How WSL2 6.6+ kernel modules are distributed:

  • Modules are packaged in modules.vhd (Windows-side file)
  • Mounted at startup as the overlay’s lower layer (read-only), path /modules
  • /usr/lib/modules/<ver>/ is the overlay merged view:
overlay → lower = /modules (modules.vhd, read-only)
          upper = /lib/modules/.../rw/upper (writable, persists across reboots)

Lessons Learned

This diagnosis got stuck on the wrong assumption that “Exec format error = file corruption”, wasting significant time. Checking vermagic first would have saved a lot of effort.

  1. Exec format error = vermagic mismatch, not “file corruption” or “kernel doesn’t support it”
  2. WSL2 kernel update requires replacing three files: kernel + system.vhd + modules.vhd
  3. When confirming “kernel doesn’t support it”, check CONFIG_TUN first: grep CONFIG_TUN /boot/config-$(uname -r), CONFIG_TUN=m means kernel supports it, just module not loaded