@@ -1237,14 +1237,31 @@ const PROXY_BASELINE_READ_WRITE: &[&str] = &["/sandbox", "/tmp"];
12371237/// socket at init time. If the directory exists but Landlock denies traversal
12381238/// (EACCES vs ECONNREFUSED), NVML returns `NVML_ERROR_INSUFFICIENT_PERMISSIONS`
12391239/// even though the daemon is optional. Only read/traversal access is needed.
1240- const GPU_BASELINE_READ_ONLY : & [ & str ] = & [ "/run/nvidia-persistenced" ] ;
1240+ ///
1241+ /// `/usr/lib/wsl`: On WSL2, CDI bind-mounts GPU libraries (libdxcore.so,
1242+ /// libcuda.so.1.1, etc.) into paths under `/usr/lib/wsl/`. Although `/usr`
1243+ /// is already in `PROXY_BASELINE_READ_ONLY`, individual file bind-mounts may
1244+ /// not be covered by the parent-directory Landlock rule when the mount crosses
1245+ /// a filesystem boundary. Listing `/usr/lib/wsl` explicitly ensures traversal
1246+ /// is permitted regardless of Landlock's cross-mount behaviour.
1247+ const GPU_BASELINE_READ_ONLY : & [ & str ] = & [
1248+ "/run/nvidia-persistenced" ,
1249+ "/usr/lib/wsl" , // WSL2: CDI-injected GPU library directory
1250+ ] ;
12411251
12421252/// GPU read-write paths (static).
12431253///
12441254/// `/dev/nvidiactl`, `/dev/nvidia-uvm`, `/dev/nvidia-uvm-tools`,
1245- /// `/dev/nvidia-modeset`: control and UVM devices injected by CDI.
1246- /// Landlock restricts `open(2)` on device files even when DAC allows it;
1247- /// these need read-write because NVML/CUDA opens them with `O_RDWR`.
1255+ /// `/dev/nvidia-modeset`: control and UVM devices injected by CDI on native
1256+ /// Linux. Landlock restricts `open(2)` on device files even when DAC allows
1257+ /// it; these need read-write because NVML/CUDA opens them with `O_RDWR`.
1258+ /// These devices do not exist on WSL2 and will be skipped by the existence
1259+ /// check in `enrich_proto_baseline_paths()`.
1260+ ///
1261+ /// `/dev/dxg`: On WSL2, NVIDIA GPUs are exposed through the DXG kernel driver
1262+ /// (DirectX Graphics) rather than the native nvidia* devices. CDI injects
1263+ /// `/dev/dxg` as the sole GPU device node; it does not exist on native Linux
1264+ /// and will be skipped there by the existence check.
12481265///
12491266/// `/proc`: CUDA writes to `/proc/<pid>/task/<tid>/comm` during `cuInit()`
12501267/// to set thread names. Without write access, `cuInit()` returns error 304.
@@ -1258,12 +1275,17 @@ const GPU_BASELINE_READ_WRITE: &[&str] = &[
12581275 "/dev/nvidia-uvm" ,
12591276 "/dev/nvidia-uvm-tools" ,
12601277 "/dev/nvidia-modeset" ,
1278+ "/dev/dxg" , // WSL2: DXG device (GPU via DirectX kernel driver, injected by CDI)
12611279 "/proc" ,
12621280] ;
12631281
12641282/// Returns true if GPU devices are present in the container.
1283+ ///
1284+ /// Checks both the native Linux NVIDIA control device (`/dev/nvidiactl`) and
1285+ /// the WSL2 DXG device (`/dev/dxg`). CDI injects exactly one of these
1286+ /// depending on the host kernel; the other will not exist.
12651287fn has_gpu_devices ( ) -> bool {
1266- std:: path:: Path :: new ( "/dev/nvidiactl" ) . exists ( )
1288+ std:: path:: Path :: new ( "/dev/nvidiactl" ) . exists ( ) || std :: path :: Path :: new ( "/dev/dxg" ) . exists ( )
12671289}
12681290
12691291/// Enumerate per-GPU device nodes (`/dev/nvidia0`, `/dev/nvidia1`, …).
@@ -1542,6 +1564,17 @@ mod baseline_tests {
15421564 ) ;
15431565 }
15441566
1567+ #[ test]
1568+ fn gpu_baseline_read_write_contains_dxg ( ) {
1569+ // /dev/dxg must be present so WSL2 sandboxes get the Landlock
1570+ // read-write rule for the CDI-injected DXG device. The existence
1571+ // check in enrich_proto_baseline_paths() skips it on native Linux.
1572+ assert ! (
1573+ GPU_BASELINE_READ_WRITE . contains( & "/dev/dxg" ) ,
1574+ "/dev/dxg must be in GPU_BASELINE_READ_WRITE for WSL2 support"
1575+ ) ;
1576+ }
1577+
15451578 #[ test]
15461579 fn local_enrichment_preserves_explicit_read_only_for_baseline_read_write_paths ( ) {
15471580 let mut policy = SandboxPolicy {
@@ -1576,6 +1609,29 @@ mod baseline_tests {
15761609 "baseline enrichment must not promote explicit read_only /tmp to read_write"
15771610 ) ;
15781611 }
1612+
1613+ #[ test]
1614+ fn gpu_baseline_read_only_contains_usr_lib_wsl ( ) {
1615+ // /usr/lib/wsl must be present so CDI-injected WSL2 GPU library
1616+ // bind-mounts are accessible under Landlock. Skipped on native Linux.
1617+ assert ! (
1618+ GPU_BASELINE_READ_ONLY . contains( & "/usr/lib/wsl" ) ,
1619+ "/usr/lib/wsl must be in GPU_BASELINE_READ_ONLY for WSL2 CDI library paths"
1620+ ) ;
1621+ }
1622+
1623+ #[ test]
1624+ fn has_gpu_devices_reflects_dxg_or_nvidiactl ( ) {
1625+ // Verify the OR logic: result must match the manual disjunction of
1626+ // the two path checks. Passes in all environments.
1627+ let nvidiactl = std:: path:: Path :: new ( "/dev/nvidiactl" ) . exists ( ) ;
1628+ let dxg = std:: path:: Path :: new ( "/dev/dxg" ) . exists ( ) ;
1629+ assert_eq ! (
1630+ has_gpu_devices( ) ,
1631+ nvidiactl || dxg,
1632+ "has_gpu_devices() should be true iff /dev/nvidiactl or /dev/dxg exists"
1633+ ) ;
1634+ }
15791635}
15801636
15811637/// Returns `true` if the error is transient and worth retrying.
0 commit comments