Get rid of fchdir(2) usage in directfs.

fchdir(2) was only needed to support connect(2) and bind(2), which are the
only filesystem operations that force host path traversal. The sandbox process
does not have the container filesystem in its mount namespace. It does not even
have procfs, so we can't use a path like /proc/self/fd/{socket-fd}. So earlier
we were using fchdir(2) to go into the socket's parent directory and use a
relative path.

However, allowing fchdir(2) makes it harder to reason about the sandbox process
state because it modifies the process's CWD. Directfs seccomp filters today do
not allow the usage of AT_FDCWD, but that could change in the future.
Operations that rely on the sandbox CWD need to synchronize using
pkg/sentry/fsutil/chdir package, like directfs does today. If they don't then
we could have nasty bugs.

Now we fallback to using LISAFS in such scenarios. This makes bind(2) and
connect(2) a little slower because now directfs has to perform a LISAFS walk
to get a LISAFS FD to the socket and then make the Connect/Bind RPC. But this
allows us to remove fchdir, socket, connect, bind, listen and accept from the
directfs seccomp filters. The rationale is that if making 2 relatively-rare
operations slightly slower helps us avoid chdir(2) and these other socket-based
syscalls, then it is overall a win.

Reported-by: Etienne Perot <eperot@google.com>
PiperOrigin-RevId: 516931050
This commit is contained in:
Ayush Ranjan
2023-03-15 14:34:41 -07:00
committed by gVisor bot
parent 8fb66eb289
commit d2da7d77d2
13 changed files with 89 additions and 321 deletions
-13
View File
@@ -1,13 +0,0 @@
load("//tools:defs.bzl", "go_library")
package(licenses = ["notice"])
go_library(
name = "chdir",
srcs = ["chdir.go"],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/sync",
"@org_golang_x_sys//unix:go_default_library",
],
)
-69
View File
@@ -1,69 +0,0 @@
// Copyright 2023 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package chdir provides utilities to control the sandbox process's current
// working directory.
package chdir
import (
"fmt"
"os"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/sync"
)
// chdirMu is the global mutex that synchronizes host chdir operations for the
// sandbox process.
var chdirMu sync.Mutex
// cwd is the current working directory for the sandbox process. The sandbox
// process usually runs in an empty chroot so cwd should be pointing to '/'.
// cwd is protected by chdirMu.
var cwd *os.File
// InitCWD initializes the global cwd FD. InitCWD must be called after the
// sandbox process has been configured with pivot_root(2)/chroot(2).
func InitCWD() (err error) {
chdirMu.Lock()
defer chdirMu.Unlock()
if cwd != nil {
panic("InitCWD() called twice")
}
cwd, err = os.Open(".")
return
}
// DoInDir performs fn after chdir-ing to dirFD and then reverts back to the
// original CWD.
//
// Precondition: InitCWD() must have been called.
func DoInDir(dirFD int, fn func() error) error {
chdirMu.Lock()
defer chdirMu.Unlock()
if cwd == nil {
panic("DoInDir() called without calling InitCWD()")
}
defer func() {
if err := unix.Fchdir(int(cwd.Fd())); err != nil {
panic(fmt.Errorf("restoring orginial CWD failed: %v", err))
}
}()
if err := unix.Fchdir(dirFD); err != nil {
return err
}
return fn()
}