Avoid calling openat with unix.AT_FDCWD from the sentry in directfs.

We can achieve this by opening the current working directory for the sandbox
process right before the application starts. We use this cached FD to restore
CWD after DoInDir() operations.

Since the package which provides DoInDir() has to be aware of the sandbox
process and requirements around initializing the CWD, I have moved this to
pkg/sentry/fsutil - which can contain sentry aware code.

This will be used by directfs. This helps because this allows us to block
AT_FDCWD in directfs seccomp filters.

PiperOrigin-RevId: 513409592
This commit is contained in:
Ayush Ranjan
2023-03-01 19:45:41 -08:00
committed by gVisor bot
parent 807fd0fd27
commit d12e5c3406
5 changed files with 45 additions and 11 deletions
-2
View File
@@ -5,7 +5,6 @@ licenses(["notice"])
go_library(
name = "fsutil",
srcs = [
"chdir.go",
"fsutil.go",
"fsutil_amd64_unsafe.go",
"fsutil_arm64_unsafe.go",
@@ -13,7 +12,6 @@ go_library(
],
visibility = ["//visibility:public"],
deps = [
"//pkg/sync",
"//pkg/syserr",
"@org_golang_x_sys//unix:go_default_library",
],
+1
View File
@@ -96,6 +96,7 @@ go_library(
"//pkg/sentry/fsimpl/lock",
"//pkg/sentry/fsmetric",
"//pkg/sentry/fsutil",
"//pkg/sentry/fsutil/chdir",
"//pkg/sentry/hostfd",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
+3 -2
View File
@@ -27,6 +27,7 @@ import (
"gvisor.dev/gvisor/pkg/fsutil"
"gvisor.dev/gvisor/pkg/lisafs"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/fsutil/chdir"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
"gvisor.dev/gvisor/pkg/sentry/vfs"
@@ -490,7 +491,7 @@ func (d *directfsDentry) bindAt(ctx context.Context, name string, creds *auth.Cr
// There are no filesystems mounted in the sandbox process's mount namespace.
// So we can't perform absolute path traversals. So fchdir(2) to this
// directory and bind at name (relative path traversal).
if err := fsutil.DoInDir(d.controlFD, func() error {
if err := chdir.DoInDir(d.controlFD, func() error {
return unix.Bind(sockFD, &unix.SockaddrUnix{Name: name})
}); err != nil {
hbep.ResetBoundSocketFD(ctx)
@@ -617,7 +618,7 @@ func (d *directfsDentry) connect(ctx context.Context, sockType linux.SockType) (
// There are no filesystems mounted in the sandbox process's mount namespace.
// So we can't perform absolute path traversals. So fchdir(2) to parent
// and connect to this socket at name (relative path traversal).
if err := fsutil.DoInDir(d.parent.impl.(*directfsDentry).controlFD, func() error {
if err := chdir.DoInDir(d.parent.impl.(*directfsDentry).controlFD, func() error {
return unix.Connect(sock, &unix.SockaddrUnix{Name: d.name})
}); err != nil {
unix.Close(sock)
+13
View File
@@ -0,0 +1,13 @@
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",
],
)
@@ -12,31 +12,52 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package fsutil
// 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.
// 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()
oldCWD, err := unix.Openat(unix.AT_FDCWD, ".", unix.O_PATH, 0 /* mode */)
if err != nil {
return err
if cwd == nil {
panic("DoInDir() called without calling InitCWD()")
}
defer func() {
if err := unix.Fchdir(oldCWD); err != nil {
if err := unix.Fchdir(int(cwd.Fd())); err != nil {
panic(fmt.Errorf("restoring orginial CWD failed: %v", err))
}
}()