mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Create gofer filestores in gofer's mount namespace.
Before this change, the gofer filestore FDs were opened in the host's mount namespace. This prevented containerd from unmounting a subcontainer's rootfs in case (due to a ref leak or bug) the container's rootfs was not destroyed in gVisor's VFS layer. In such a scenario, the sentry would leak the rootfs filestore FD and containerd's umount attempts will fail with EBUSY. This change ensures that all gofer filestores are created in the gofer's mount namespace, so any unmount attempt from the host mount namespace is not blocked. This change makes the following 2 logical changes: 1. It introduces a new gofer process synchronization flag --sync-chroot-fd. The gofer waits on this FD and then proceeds to set up container filesystem and pivot_root(2) + chroot(2) the gofer mount namespace. This is useful if an external process wants to take action in gofer's mount namespace while having access to the host filesystem. As a result, the existing --sync-nvproxy-fd became redundant because it has the same use case. IOW, it was just renamed to "sync-chroot-fd" and its meaning became broader. 2. Container.createGoferFilestores() earlier was doing two things: creating gofer mount configs and creating all filestores. But the intent of this change is to only create filestore FDs after gofer starts in gofer mountns. However, the gofer mount configs are needed before that. So this function was split into Container.initGoferConfs(). We open FDs from the gofer mount namespace using a neat trick suggested by Andrei, we prefix the absolute paths with /proc/<gofer-pid>/root. See proc_pid_root(5) for more details about this. There are no other intended changes in behavior. Fixes #9834 Suggested-by: Andrei Vagin <avagin@google.com> PiperOrigin-RevId: 706880500
This commit is contained in:
+17
-16
@@ -64,11 +64,12 @@ var goferCaps = &specs.LinuxCapabilities{
|
||||
// goferSyncFDs contains file descriptors that are used for synchronization
|
||||
// of the Gofer startup process against other processes.
|
||||
type goferSyncFDs struct {
|
||||
// nvproxyFD is a file descriptor that is used to wait until
|
||||
// nvproxy-related setup is done. This setup involves creating mounts in the
|
||||
// Gofer process's mount namespace.
|
||||
// chrootFD is a file descriptor that is used to wait until container
|
||||
// filesystem related setup is done. This setup involves creating files and
|
||||
// mounts in the Gofer process's mount namespace and needs to be done before
|
||||
// the Gofer chroots.
|
||||
// If this is set, this FD is the first that the Gofer waits for.
|
||||
nvproxyFD int
|
||||
chrootFD int
|
||||
// usernsFD is a file descriptor that is used to wait until
|
||||
// user namespace ID mappings are established in the Gofer's userns.
|
||||
// If this is set, this FD is the second that the Gofer waits for.
|
||||
@@ -154,7 +155,7 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomm
|
||||
util.Fatalf("reading spec: %v", err)
|
||||
}
|
||||
|
||||
g.syncFDs.syncNVProxy()
|
||||
g.syncFDs.syncChroot()
|
||||
g.syncFDs.syncUsernsForRootless()
|
||||
|
||||
goferToHostRPCSock, err := unet.NewSocket(g.goferToHostRPCFD)
|
||||
@@ -728,7 +729,7 @@ func adjustMountOptions(conf *config.Config, path string, opts []string) ([]stri
|
||||
|
||||
// setFlags sets sync FD flags on the given FlagSet.
|
||||
func (g *goferSyncFDs) setFlags(f *flag.FlagSet) {
|
||||
f.IntVar(&g.nvproxyFD, "sync-nvproxy-fd", -1, "file descriptor that the gofer waits on until nvproxy setup is done")
|
||||
f.IntVar(&g.chrootFD, "sync-chroot-fd", -1, "file descriptor that the gofer waits on until container filesystem setup is done")
|
||||
f.IntVar(&g.usernsFD, "sync-userns-fd", -1, "file descriptor the gofer waits on until userns mappings are set up")
|
||||
f.IntVar(&g.procMountFD, "proc-mount-sync-fd", -1, "file descriptor that the gofer writes to when /proc isn't needed anymore and can be unmounted")
|
||||
}
|
||||
@@ -737,7 +738,7 @@ func (g *goferSyncFDs) setFlags(f *flag.FlagSet) {
|
||||
// to a re-executed version of this process.
|
||||
func (g *goferSyncFDs) flags() map[string]string {
|
||||
return map[string]string{
|
||||
"sync-nvproxy-fd": fmt.Sprintf("%d", g.nvproxyFD),
|
||||
"sync-chroot-fd": fmt.Sprintf("%d", g.chrootFD),
|
||||
"sync-userns-fd": fmt.Sprintf("%d", g.usernsFD),
|
||||
"proc-mount-sync-fd": fmt.Sprintf("%d", g.procMountFD),
|
||||
}
|
||||
@@ -827,16 +828,16 @@ func syncUsernsForRootless(fd int) {
|
||||
}
|
||||
}
|
||||
|
||||
// syncNVProxy waits on nvproxyFD to be closed.
|
||||
// Used for synchronization during nvproxy setup which is done from the
|
||||
// non-gofer process.
|
||||
// This function is a no-op if nvProxySyncFD is -1.
|
||||
func (g *goferSyncFDs) syncNVProxy() {
|
||||
if g.nvproxyFD < 0 {
|
||||
// syncChroot waits on chrootFD to be closed.
|
||||
// Used for synchronization during container filesystem setup which is done
|
||||
// from the non-gofer process.
|
||||
// This function is a no-op if chrootFD is -1.
|
||||
func (g *goferSyncFDs) syncChroot() {
|
||||
if g.chrootFD < 0 {
|
||||
return
|
||||
}
|
||||
if err := waitForFD(g.nvproxyFD, "nvproxy sync FD"); err != nil {
|
||||
util.Fatalf("failed to sync on NVProxy FD: %v", err)
|
||||
if err := waitForFD(g.chrootFD, "chroot sync FD"); err != nil {
|
||||
util.Fatalf("failed to sync on chroot FD: %v", err)
|
||||
}
|
||||
g.nvproxyFD = -1
|
||||
g.chrootFD = -1
|
||||
}
|
||||
|
||||
@@ -972,6 +972,11 @@ func (o *Overlay2) SubMountOverlayMedium() OverlayMedium {
|
||||
return o.medium
|
||||
}
|
||||
|
||||
// Medium returns the overlay medium config.
|
||||
func (o Overlay2) Medium() OverlayMedium {
|
||||
return o.medium
|
||||
}
|
||||
|
||||
// HostSettingsPolicy dictates how host settings should be handled.
|
||||
type HostSettingsPolicy int
|
||||
|
||||
|
||||
+209
-178
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user