Skip to content

Commit 33834f3

Browse files
committed
fix(sandbox): add WSL2 GPU device and library paths to Landlock baseline
On WSL2, NVIDIA GPUs are exposed through the DXG kernel driver (/dev/dxg) rather than the native nvidia* devices. CDI injects /dev/dxg as the sole GPU device node, plus GPU libraries under /usr/lib/wsl/. has_gpu_devices() previously only checked for /dev/nvidiactl, which does not exist on WSL2, so GPU enrichment never ran. This meant /dev/dxg was never permitted by Landlock and /proc write access (required by CUDA for thread naming) was never granted. Fix by: - Extending has_gpu_devices() to also detect /dev/dxg - Adding /dev/dxg to GPU_BASELINE_READ_WRITE (device nodes need O_RDWR) - Adding /usr/lib/wsl to GPU_BASELINE_READ_ONLY for CDI-injected GPU library bind-mounts that may not be covered by the /usr parent rule across filesystem boundaries The existing path existence check in enrich_proto_baseline_paths() ensures all new entries are silently skipped on native Linux where these paths do not exist.
1 parent 2cb94ea commit 33834f3

File tree

1 file changed

+61
-5
lines changed
  • crates/openshell-sandbox/src

1 file changed

+61
-5
lines changed

crates/openshell-sandbox/src/lib.rs

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,14 +1220,31 @@ const PROXY_BASELINE_READ_WRITE: &[&str] = &["/sandbox", "/tmp"];
12201220
/// socket at init time. If the directory exists but Landlock denies traversal
12211221
/// (EACCES vs ECONNREFUSED), NVML returns `NVML_ERROR_INSUFFICIENT_PERMISSIONS`
12221222
/// even though the daemon is optional. Only read/traversal access is needed.
1223-
const GPU_BASELINE_READ_ONLY: &[&str] = &["/run/nvidia-persistenced"];
1223+
///
1224+
/// `/usr/lib/wsl`: On WSL2, CDI bind-mounts GPU libraries (libdxcore.so,
1225+
/// libcuda.so.1.1, etc.) into paths under `/usr/lib/wsl/`. Although `/usr`
1226+
/// is already in `PROXY_BASELINE_READ_ONLY`, individual file bind-mounts may
1227+
/// not be covered by the parent-directory Landlock rule when the mount crosses
1228+
/// a filesystem boundary. Listing `/usr/lib/wsl` explicitly ensures traversal
1229+
/// is permitted regardless of Landlock's cross-mount behaviour.
1230+
const GPU_BASELINE_READ_ONLY: &[&str] = &[
1231+
"/run/nvidia-persistenced",
1232+
"/usr/lib/wsl", // WSL2: CDI-injected GPU library directory
1233+
];
12241234

12251235
/// GPU read-write paths (static).
12261236
///
12271237
/// `/dev/nvidiactl`, `/dev/nvidia-uvm`, `/dev/nvidia-uvm-tools`,
1228-
/// `/dev/nvidia-modeset`: control and UVM devices injected by CDI.
1229-
/// Landlock restricts `open(2)` on device files even when DAC allows it;
1230-
/// these need read-write because NVML/CUDA opens them with `O_RDWR`.
1238+
/// `/dev/nvidia-modeset`: control and UVM devices injected by CDI on native
1239+
/// Linux. Landlock restricts `open(2)` on device files even when DAC allows
1240+
/// it; these need read-write because NVML/CUDA opens them with `O_RDWR`.
1241+
/// These devices do not exist on WSL2 and will be skipped by the existence
1242+
/// check in `enrich_proto_baseline_paths()`.
1243+
///
1244+
/// `/dev/dxg`: On WSL2, NVIDIA GPUs are exposed through the DXG kernel driver
1245+
/// (DirectX Graphics) rather than the native nvidia* devices. CDI injects
1246+
/// `/dev/dxg` as the sole GPU device node; it does not exist on native Linux
1247+
/// and will be skipped there by the existence check.
12311248
///
12321249
/// `/proc`: CUDA writes to `/proc/<pid>/task/<tid>/comm` during `cuInit()`
12331250
/// to set thread names. Without write access, `cuInit()` returns error 304.
@@ -1241,12 +1258,17 @@ const GPU_BASELINE_READ_WRITE: &[&str] = &[
12411258
"/dev/nvidia-uvm",
12421259
"/dev/nvidia-uvm-tools",
12431260
"/dev/nvidia-modeset",
1261+
"/dev/dxg", // WSL2: DXG device (GPU via DirectX kernel driver, injected by CDI)
12441262
"/proc",
12451263
];
12461264

12471265
/// Returns true if GPU devices are present in the container.
1266+
///
1267+
/// Checks both the native Linux NVIDIA control device (`/dev/nvidiactl`) and
1268+
/// the WSL2 DXG device (`/dev/dxg`). CDI injects exactly one of these
1269+
/// depending on the host kernel; the other will not exist.
12481270
fn has_gpu_devices() -> bool {
1249-
std::path::Path::new("/dev/nvidiactl").exists()
1271+
std::path::Path::new("/dev/nvidiactl").exists() || std::path::Path::new("/dev/dxg").exists()
12501272
}
12511273

12521274
/// Enumerate per-GPU device nodes (`/dev/nvidia0`, `/dev/nvidia1`, …).
@@ -1479,6 +1501,40 @@ mod baseline_tests {
14791501
);
14801502
}
14811503
}
1504+
1505+
#[test]
1506+
fn gpu_baseline_read_write_contains_dxg() {
1507+
// /dev/dxg must be present so WSL2 sandboxes get the Landlock
1508+
// read-write rule for the CDI-injected DXG device. The existence
1509+
// check in enrich_proto_baseline_paths() skips it on native Linux.
1510+
assert!(
1511+
GPU_BASELINE_READ_WRITE.contains(&"/dev/dxg"),
1512+
"/dev/dxg must be in GPU_BASELINE_READ_WRITE for WSL2 support"
1513+
);
1514+
}
1515+
1516+
#[test]
1517+
fn gpu_baseline_read_only_contains_usr_lib_wsl() {
1518+
// /usr/lib/wsl must be present so CDI-injected WSL2 GPU library
1519+
// bind-mounts are accessible under Landlock. Skipped on native Linux.
1520+
assert!(
1521+
GPU_BASELINE_READ_ONLY.contains(&"/usr/lib/wsl"),
1522+
"/usr/lib/wsl must be in GPU_BASELINE_READ_ONLY for WSL2 CDI library paths"
1523+
);
1524+
}
1525+
1526+
#[test]
1527+
fn has_gpu_devices_reflects_dxg_or_nvidiactl() {
1528+
// Verify the OR logic: result must match the manual disjunction of
1529+
// the two path checks. Passes in all environments.
1530+
let nvidiactl = std::path::Path::new("/dev/nvidiactl").exists();
1531+
let dxg = std::path::Path::new("/dev/dxg").exists();
1532+
assert_eq!(
1533+
has_gpu_devices(),
1534+
nvidiactl || dxg,
1535+
"has_gpu_devices() should be true iff /dev/nvidiactl or /dev/dxg exists"
1536+
);
1537+
}
14821538
}
14831539

14841540
/// Load sandbox policy from local files or gRPC.

0 commit comments

Comments
 (0)