From 3cf338422024638e5959a5215baccaf30077c79d Mon Sep 17 00:00:00 2001 From: Jing Chen Date: Thu, 7 Mar 2024 16:04:16 -0800 Subject: [PATCH] Return a task image's file capability when the image is loaded to be executed. PiperOrigin-RevId: 613732844 --- pkg/sentry/kernel/task_image.go | 17 +++++++++--- pkg/sentry/loader/loader.go | 48 ++++++++++++++++++++++++++------- pkg/sentry/vfs/permissions.go | 2 +- test/syscalls/linux/xattr.cc | 5 ++-- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/pkg/sentry/kernel/task_image.go b/pkg/sentry/kernel/task_image.go index d3c5aee88..18b674b72 100644 --- a/pkg/sentry/kernel/task_image.go +++ b/pkg/sentry/kernel/task_image.go @@ -50,6 +50,14 @@ type TaskImage struct { // st is the task's syscall table. st *SyscallTable `state:".(syscallTableInfo)"` + + // fileCaps is the image's extended attribute named security.capability. + fileCaps string +} + +// FileCaps return the task image's security.capability extended attribute. +func (image *TaskImage) FileCaps() string { + return image.fileCaps } // release releases all resources held by the TaskImage. release is called by @@ -142,13 +150,13 @@ func (k *Kernel) LoadTaskImage(ctx context.Context, args loader.LoadArgs) (*Task defer m.DecUsers(ctx) args.MemoryManager = m - os, ac, name, err := loader.Load(ctx, args, k.extraAuxv, k.vdso) + info, err := loader.Load(ctx, args, k.extraAuxv, k.vdso) if err != nil { return nil, err } // Lookup our new syscall table. - st, ok := LookupSyscallTable(os, ac.Arch()) + st, ok := LookupSyscallTable(info.OS, info.Arch.Arch()) if !ok { // No syscall table found. This means that the ELF binary does not match // the architecture. @@ -159,10 +167,11 @@ func (k *Kernel) LoadTaskImage(ctx context.Context, args loader.LoadArgs) (*Task panic("Failed to increment users count on new MM") } return &TaskImage{ - Name: name, - Arch: ac, + Name: info.Name, + Arch: info.Arch, MemoryManager: m, fu: k.futexes.Fork(), st: st, + fileCaps: info.FileCaps, }, nil } diff --git a/pkg/sentry/loader/loader.go b/pkg/sentry/loader/loader.go index af5f32698..3af837060 100644 --- a/pkg/sentry/loader/loader.go +++ b/pkg/sentry/loader/loader.go @@ -38,6 +38,10 @@ import ( "gvisor.dev/gvisor/pkg/usermem" ) +const ( + securityCapability = linux.XATTR_SECURITY_PREFIX + "capability" +) + // LoadArgs holds specifications for an executable file to be loaded. type LoadArgs struct { // MemoryManager is the memory manager to load the executable into. @@ -236,6 +240,18 @@ func loadExecutable(ctx context.Context, args LoadArgs) (loadedELF, *arch.Contex return loadedELF{}, nil, nil, nil, linuxerr.ELOOP } +// ImageInfo represents the information for the loaded image. +type ImageInfo struct { + // The target operating system of the image. + OS abi.OS + // AMD64 context. + Arch *arch.Context64 + // The base name of the binary. + Name string + // The binary's file capability. + FileCaps string +} + // Load loads args.File into a MemoryManager. If args.File is nil, the path // args.Filename is resolved and loaded instead. // @@ -245,18 +261,25 @@ func loadExecutable(ctx context.Context, args LoadArgs) (loadedELF, *arch.Contex // Preconditions: // - The Task MemoryManager is empty. // - Load is called on the Task goroutine. -func Load(ctx context.Context, args LoadArgs, extraAuxv []arch.AuxEntry, vdso *VDSO) (abi.OS, *arch.Context64, string, *syserr.Error) { +func Load(ctx context.Context, args LoadArgs, extraAuxv []arch.AuxEntry, vdso *VDSO) (ImageInfo, *syserr.Error) { // Load the executable itself. loaded, ac, file, newArgv, err := loadExecutable(ctx, args) if err != nil { - return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("failed to load %s: %v", args.Filename, err), syserr.FromError(err).ToLinux()) + return ImageInfo{}, syserr.NewDynamic(fmt.Sprintf("failed to load %s: %v", args.Filename, err), syserr.FromError(err).ToLinux()) } defer file.DecRef(ctx) + xattr, err := file.GetXattr(ctx, &vfs.GetXattrOptions{Name: securityCapability, Size: linux.XATTR_CAPS_SZ_3}) + switch { + case linuxerr.Equals(linuxerr.ENODATA, err), linuxerr.Equals(linuxerr.ENOTSUP, err): + xattr = "" + case err != nil: + return ImageInfo{}, syserr.NewDynamic(fmt.Sprintf("failed to read file capabilities of %s: %v", args.Filename, err), syserr.FromError(err).ToLinux()) + } // Load the VDSO. vdsoAddr, err := loadVDSO(ctx, args.MemoryManager, vdso, loaded) if err != nil { - return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("error loading VDSO: %v", err), syserr.FromError(err).ToLinux()) + return ImageInfo{}, syserr.NewDynamic(fmt.Sprintf("error loading VDSO: %v", err), syserr.FromError(err).ToLinux()) } // Setup the heap. brk starts at the next page after the end of the @@ -264,29 +287,29 @@ func Load(ctx context.Context, args LoadArgs, extraAuxv []arch.AuxEntry, vdso *V // loaded.end is available for its use. e, ok := loaded.end.RoundUp() if !ok { - return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("brk overflows: %#x", loaded.end), errno.ENOEXEC) + return ImageInfo{}, syserr.NewDynamic(fmt.Sprintf("brk overflows: %#x", loaded.end), errno.ENOEXEC) } args.MemoryManager.BrkSetup(ctx, e) // Allocate our stack. stack, err := allocStack(ctx, args.MemoryManager, ac) if err != nil { - return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to allocate stack: %v", err), syserr.FromError(err).ToLinux()) + return ImageInfo{}, syserr.NewDynamic(fmt.Sprintf("Failed to allocate stack: %v", err), syserr.FromError(err).ToLinux()) } // Push the original filename to the stack, for AT_EXECFN. if _, err := stack.PushNullTerminatedByteSlice([]byte(args.Filename)); err != nil { - return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to push exec filename: %v", err), syserr.FromError(err).ToLinux()) + return ImageInfo{}, syserr.NewDynamic(fmt.Sprintf("Failed to push exec filename: %v", err), syserr.FromError(err).ToLinux()) } execfn := stack.Bottom // Push 16 random bytes on the stack which AT_RANDOM will point to. var b [16]byte if _, err := rand.Read(b[:]); err != nil { - return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to read random bytes: %v", err), syserr.FromError(err).ToLinux()) + return ImageInfo{}, syserr.NewDynamic(fmt.Sprintf("Failed to read random bytes: %v", err), syserr.FromError(err).ToLinux()) } if _, err = stack.PushNullTerminatedByteSlice(b[:]); err != nil { - return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to push random bytes: %v", err), syserr.FromError(err).ToLinux()) + return ImageInfo{}, syserr.NewDynamic(fmt.Sprintf("Failed to push random bytes: %v", err), syserr.FromError(err).ToLinux()) } random := stack.Bottom @@ -311,7 +334,7 @@ func Load(ctx context.Context, args LoadArgs, extraAuxv []arch.AuxEntry, vdso *V sl, err := stack.Load(newArgv, args.Envv, auxv) if err != nil { - return 0, nil, "", syserr.NewDynamic(fmt.Sprintf("Failed to load stack: %v", err), syserr.FromError(err).ToLinux()) + return ImageInfo{}, syserr.NewDynamic(fmt.Sprintf("Failed to load stack: %v", err), syserr.FromError(err).ToLinux()) } m := args.MemoryManager @@ -331,5 +354,10 @@ func Load(ctx context.Context, args LoadArgs, extraAuxv []arch.AuxEntry, vdso *V name = name[:linux.TASK_COMM_LEN-1] } - return loaded.os, ac, name, nil + return ImageInfo{ + OS: loaded.os, + Arch: ac, + Name: name, + FileCaps: xattr, + }, nil } diff --git a/pkg/sentry/vfs/permissions.go b/pkg/sentry/vfs/permissions.go index 2ea5bae8a..0301808d9 100644 --- a/pkg/sentry/vfs/permissions.go +++ b/pkg/sentry/vfs/permissions.go @@ -325,7 +325,7 @@ func CheckXattrPermissions(creds *auth.Credentials, ats AccessTypes, mode linux. } case strings.HasPrefix(name, linux.XATTR_SECURITY_PREFIX): if ats.MayRead() { - return linuxerr.ENODATA + return nil } return linuxerr.EOPNOTSUPP } diff --git a/test/syscalls/linux/xattr.cc b/test/syscalls/linux/xattr.cc index 065accb7e..11b48819a 100644 --- a/test/syscalls/linux/xattr.cc +++ b/test/syscalls/linux/xattr.cc @@ -39,6 +39,7 @@ namespace testing { namespace { using ::gvisor::testing::IsTmpfs; +using ::testing::AnyOf; class XattrTest : public FileTest {}; @@ -112,11 +113,9 @@ TEST_F(XattrTest, SecurityCapacityXattr) { const std::string val = ""; EXPECT_THAT(lsetxattr(path, name, &val, val.size(), 0), SyscallFailsWithErrno(EOPNOTSUPP)); - int buf = 0; EXPECT_THAT(lgetxattr(path, name, &buf, /*size=*/128), - SyscallFailsWithErrno(ENODATA)); - + SyscallFailsWithErrno(AnyOf(ENODATA, EOPNOTSUPP))); EXPECT_THAT(lremovexattr(path, name), SyscallFailsWithErrno(EOPNOTSUPP)); }