TOCTOU race condition in subPath directory creation (subPath mount path resolution)
Description
Root cause: A race condition (TOCTOU) in subPath directory creation for Kubernetes subPath volumes. The patch changes error handling to tolerate an already-existing subPath directory (ignoring ErrExist) and avoids leaking host filesystem details in user-facing errors. This reduces the window where a malicious actor with access to host paths could influence path resolution during mounting and potentially cause a subPath to reference an unintended host location. The exact code changes indicate an intent to make subPath directory creation idempotent when the directory already exists, rather than failing and exposing host FS details. Additionally, error wrapping was adjusted in subpath implementations to use proper error wrapping (%w) rather than exposing raw strings. Overall, this is a real TOCTOU mitigation in subPath directory creation for container mounts, addressing security concerns around host path leakage and path resolution races.
Proof of Concept
PoC outline (high-level, not a live exploit):
Goal: Demonstrate a TOCTOU race in subPath directory creation that could allow a container to mount a subPath into an unintended host path.
Assumptions:
- A Kubernetes node is using subPath volumes and an attacker has reasonable access to the node’s filesystem to manipulate the host path used for subPath (e.g., /var/lib/kubelet/pods/.../volumes/.../subPath).
- The host path used for subPath is created under a base directory that an attacker can observe and influence (via symlinks or directory replacements).
Race scenario (conceptual, not a runnable exploit):
1) The kubelet process (as part of mounting a Pod) attempts to safely create a subPath directory under a host-based volume path using SafeMakeDir(basePath, subPath, perm).
2) In parallel, an attacker replaces or mutates the path component with a symlink or directory change just before the mkdir/creation step, e.g., replacing basePath/subPath with a symlink to /etc or another sensitive host location.
3) If the race window aligns such that the directory creation follows the attacker’s path modification, the created directory (or the subsequent access) can end up under the attacker-controlled host path, potentially causing the container to be mounted to an unintended location or to leak host path information.
Proof-of-concept sketch (high-level, not executable):
- Setup a base host path: /var/lib/kubelet/pods/pod-123/volumes/subpath-base
- Attacker goroutine (or shell process) quickly:
• Remove or rename /var/lib/kubelet/pods/pod-123/volumes/subpath-base/subPath
• Create a symlink: /var/lib/kubelet/pods/pod-123/volumes/subpath-base/subPath -> /etc
• Sleep briefly to create a tight window
- Kubelet operation (the vulnerable path):
• Calls SafeMakeDir(subPath, basePath, perm)
• If the race succeeds, the mkdir operation resolves the symlink and creates under /etc, or the resulting mount points to /etc content, leaking host-sensitive data or giving access to unintended host areas.
Note: The exact exploitability depends on the timing, filesystem features, and how SafeMakeDir resolves paths with symlinks under Mkdirat/Openat semantics. The patch’s primary mitigation is to treat ErrExist as non-fatal (idempotent behavior) and to avoid leaking host path details in errors, which reduces the likelihood of a successful TOCTOU exploitation in normal operation.
Commit Details
Author: Kubernetes Prow Robot
Date: 2026-05-30 06:38 UTC
Message:
Merge pull request #134540 from dddddai/subpath
Fix a race condition when creating subPath directories
Triage Assessment
Vulnerability Type: Race condition (TOCTOU)
Confidence: MEDIUM
Reasoning:
The commit message states it fixes a race condition when creating subPath directories. The code changes address safe directory creation paths and adjust error handling to avoid leaking host FS details, which aligns with mitigating a TOCTOU-style race condition that could impact security.
Verification Assessment
Vulnerability Type: TOCTOU race condition in subPath directory creation (subPath mount path resolution)
Confidence: MEDIUM
Affected Versions: v1.36.0-beta.0 and earlier (v1.36 line)
Code Diff
diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go
index d3d94a793836c..ba21aab3efdad 100644
--- a/pkg/kubelet/kubelet_pods.go
+++ b/pkg/kubelet/kubelet_pods.go
@@ -361,7 +361,7 @@ func makeMounts(logger klog.Logger, pod *v1.Pod, podDir string, container *v1.Co
if err != nil {
return nil, cleanupAction, err
}
- if err := subpather.SafeMakeDir(subPath, volumePath, perm); err != nil {
+ if err := subpather.SafeMakeDir(subPath, volumePath, perm); err != nil && !goerrors.Is(err, os.ErrExist) {
// Don't pass detailed error back to the user because it could give information about host filesystem
logger.Error(nil, "Failed to create subPath directory for volumeMount of the container", "containerName", container.Name, "volumeMountName", mount.Name)
return nil, cleanupAction, fmt.Errorf("failed to create subPath directory for volumeMount %q of container %q", mount.Name, container.Name)
diff --git a/pkg/volume/util/subpath/subpath_linux.go b/pkg/volume/util/subpath/subpath_linux.go
index 18eae61e01d07..99f67c238713b 100644
--- a/pkg/volume/util/subpath/subpath_linux.go
+++ b/pkg/volume/util/subpath/subpath_linux.go
@@ -428,7 +428,7 @@ func doSafeMakeDir(pathname string, base string, perm os.FileMode) error {
klog.V(4).Infof("Creating %s", dir)
err = syscall.Mkdirat(parentFD, currentPath, uint32(perm))
if err != nil {
- return fmt.Errorf("cannot create directory %s: %s", currentPath, err)
+ return fmt.Errorf("cannot create directory %s: %w", currentPath, err)
}
// Dive into the created directory
childFD, err = syscall.Openat(parentFD, dir, nofollowFlags|unix.O_CLOEXEC, 0)
diff --git a/pkg/volume/util/subpath/subpath_windows.go b/pkg/volume/util/subpath/subpath_windows.go
index 06ff952d99c06..d50f702a885a7 100644
--- a/pkg/volume/util/subpath/subpath_windows.go
+++ b/pkg/volume/util/subpath/subpath_windows.go
@@ -325,7 +325,7 @@ func doSafeMakeDir(pathname string, base string, perm os.FileMode) error {
currentPath = filepath.Join(currentPath, dir)
klog.V(4).Infof("Creating %s", dir)
if err := os.Mkdir(currentPath, perm); err != nil {
- return fmt.Errorf("cannot create directory %s: %s", currentPath, err)
+ return fmt.Errorf("cannot create directory %s: %w", currentPath, err)
}
handle, err := lockPath(currentPath)
if err != nil {