From: Jed Lejosne <jed@redhat.com>
Date: Tue, 21 Apr 2026 16:09:50 -0400
Subject: [PATCH 1/2] Use safepath for virt-launcher console socket paths

The console handler was directly constructing paths to virt-launcher
sockets (virt-serial0, virt-vnc, virt-usbredir) through /proc/<pid>/root
without safepath protection, making it vulnerable to symlink attacks.

Switch getUnixSocketPath to use res.MountRoot() with safepath's
AppendAndResolveWithRelativeRoot, and perform net.Dial inside
ExecuteNoFollow so the connection uses a kernel-managed fd path. Also
use /run instead of /var/run since the latter is a symlink.

Assisted-by: Claude (Anthropic AI assistant)
Made-with: Cursor
Signed-off-by: Jed Lejosne <jed@redhat.com>
---
 pkg/virt-handler/rest/BUILD.bazel |  1 +
 pkg/virt-handler/rest/console.go  | 42 ++++++++++++++++---------------
 2 files changed, 23 insertions(+), 20 deletions(-)

diff --git a/pkg/virt-handler/rest/BUILD.bazel b/pkg/virt-handler/rest/BUILD.bazel
index 9f749d5a6a..ed28ad2343 100644
--- a/pkg/virt-handler/rest/BUILD.bazel
+++ b/pkg/virt-handler/rest/BUILD.bazel
@@ -11,6 +11,7 @@ go_library(
     importpath = "kubevirt.io/kubevirt/pkg/virt-handler/rest",
     visibility = ["//visibility:public"],
     deps = [
+        "//pkg/safepath:go_default_library",
         "//pkg/util:go_default_library",
         "//pkg/virt-handler/cmd-client:go_default_library",
         "//pkg/virt-handler/isolation:go_default_library",
diff --git a/pkg/virt-handler/rest/console.go b/pkg/virt-handler/rest/console.go
index bcad432633..9a853dadf1 100644
--- a/pkg/virt-handler/rest/console.go
+++ b/pkg/virt-handler/rest/console.go
@@ -26,8 +26,6 @@ import (
 	"io"
 	"net"
 	"net/http"
-	"os"
-	"path"
 	"strconv"
 	"sync"
 
@@ -41,6 +39,7 @@ import (
 	kvcorev1 "kubevirt.io/client-go/kubevirt/typed/core/v1"
 	"kubevirt.io/client-go/log"
 
+	"kubevirt.io/kubevirt/pkg/safepath"
 	"kubevirt.io/kubevirt/pkg/util"
 	"kubevirt.io/kubevirt/pkg/virt-handler/isolation"
 )
@@ -86,7 +85,7 @@ func (t *ConsoleHandler) USBRedirHandler(request *restful.Request, response *res
 	uid := vmi.GetUID()
 	stopChan := make(chan struct{})
 	var slotId int
-	var unixSocketPath string
+	var unixSocketPath *safepath.Path
 	ok := func() bool {
 		// For simplicity, we handle one usbredir request at the time, for all VMIs
 		// handled by virt-handler
@@ -287,30 +286,33 @@ func deleteStopChan(uid types.UID, stopChn chan struct{}, lock *sync.Mutex, stop
 	}
 }
 
-func (t *ConsoleHandler) getUnixSocketPath(vmi *v1.VirtualMachineInstance, socketName string) (string, error) {
+func (t *ConsoleHandler) getUnixSocketPath(vmi *v1.VirtualMachineInstance, socketName string) (*safepath.Path, error) {
 	result, err := t.podIsolationDetector.Detect(vmi)
 	if err != nil {
-		return "", err
+		return nil, err
 	}
-	socketDir := path.Join("/proc", strconv.Itoa(result.Pid()), "root", "var", "run", "kubevirt-private", string(vmi.GetUID()))
-	socketPath := path.Join(socketDir, socketName)
-	if _, err = os.Stat(socketPath); errors.Is(err, os.ErrNotExist) {
-		return "", err
+	root, err := result.MountRoot()
+	if err != nil {
+		return nil, err
 	}
-
-	return socketPath, nil
+	return root.AppendAndResolveWithRelativeRoot("run", "kubevirt-private", string(vmi.GetUID()), socketName)
 }
 
-func unixSocketDialer(vmi *v1.VirtualMachineInstance, unixSocketPath string) func() (net.Conn, error) {
+func unixSocketDialer(vmi *v1.VirtualMachineInstance, socketPath *safepath.Path) func() (net.Conn, error) {
 	return func() (net.Conn, error) {
-		log.Log.Object(vmi).Infof("Connecting to %s", unixSocketPath)
-		fd, err := net.Dial("unix", unixSocketPath)
-		if err != nil {
-			log.Log.Object(vmi).Reason(err).Errorf("failed to dial unix socket %s", unixSocketPath)
-			return nil, err
-		}
-		log.Log.Object(vmi).Infof("Connected to %s", unixSocketPath)
-		return fd, nil
+		var conn net.Conn
+		err := socketPath.ExecuteNoFollow(func(safePath string) error {
+			log.Log.Object(vmi).Infof("Connecting to %s", safePath)
+			var dialErr error
+			conn, dialErr = net.Dial("unix", safePath)
+			if dialErr != nil {
+				log.Log.Object(vmi).Reason(dialErr).Errorf("failed to dial unix socket %s", safePath)
+				return dialErr
+			}
+			log.Log.Object(vmi).Infof("Connected to %s", safePath)
+			return nil
+		})
+		return conn, err
 	}
 }
 
-- 
2.47.3

