Max link traversals should be for an entire path.

The number of symbolic links that are allowed to be followed
are for a full path and not just a chain of symbolic links.

PiperOrigin-RevId: 224047321
Change-Id: I5e3c4caf66a93c17eeddcc7f046d1e8bb9434a40
This commit is contained in:
Brian Geffon 2018-12-04 14:31:08 -08:00 committed by Shentubot
parent adafc08d7c
commit 82719be42e
17 changed files with 67 additions and 41 deletions

View File

@ -166,7 +166,8 @@ func makeOverlayTestFiles(t *testing.T) []*overlayTestFile {
// Walk to all of the files in the overlay, open them readable.
for _, f := range files {
d, err := mns.FindInode(ctx, mns.Root(), mns.Root(), f.name, 0)
maxTraversals := uint(0)
d, err := mns.FindInode(ctx, mns.Root(), mns.Root(), f.name, &maxTraversals)
if err != nil {
t.Fatalf("failed to find %q: %v", f.name, err)
}

View File

@ -170,7 +170,8 @@ func installWhitelist(ctx context.Context, m *fs.MountNamespace, paths []string)
current := paths[i][:j]
// Lookup the given component in the tree.
d, err := m.FindLink(ctx, root, nil, current, maxTraversals)
remainingTraversals := uint(maxTraversals)
d, err := m.FindLink(ctx, root, nil, current, &remainingTraversals)
if err != nil {
log.Warningf("populate failed for %q: %v", current, err)
continue

View File

@ -150,7 +150,8 @@ func allPaths(ctx context.Context, t *testing.T, m *fs.MountNamespace, base stri
root := m.Root()
defer root.DecRef()
d, err := m.FindLink(ctx, root, nil, base, 1)
maxTraversals := uint(1)
d, err := m.FindLink(ctx, root, nil, base, &maxTraversals)
if err != nil {
t.Logf("FindLink failed for %q", base)
return paths, err

View File

@ -324,7 +324,8 @@ func TestCacheFlush(t *testing.T) {
for _, fileName := range []string{upperFileName, lowerFileName} {
// Walk to the file.
dirent, err := mns.FindInode(ctx, root, nil, fileName, 0)
maxTraversals := uint(0)
dirent, err := mns.FindInode(ctx, root, nil, fileName, &maxTraversals)
if err != nil {
t.Fatalf("FindInode(%q) failed: %v", fileName, err)
}

View File

@ -115,8 +115,10 @@ func TestMountSourceParentChildRelationship(t *testing.T) {
"/waldo",
}
var maxTraversals uint
for _, p := range paths {
d, err := mm.FindLink(ctx, rootDirent, nil, p, 0)
maxTraversals = 0
d, err := mm.FindLink(ctx, rootDirent, nil, p, &maxTraversals)
if err != nil {
t.Fatalf("could not find path %q in mount manager: %v", p, err)
}
@ -164,7 +166,8 @@ func TestMountSourceParentChildRelationship(t *testing.T) {
}
// "foo" mount should have two children: /foo/bar, and /foo/qux.
d, err := mm.FindLink(ctx, rootDirent, nil, "/foo", 0)
maxTraversals = 0
d, err := mm.FindLink(ctx, rootDirent, nil, "/foo", &maxTraversals)
if err != nil {
t.Fatalf("could not find path %q in mount manager: %v", "/foo", err)
}
@ -185,7 +188,8 @@ func TestMountSourceParentChildRelationship(t *testing.T) {
}
// "waldo" mount should have no submounts or children.
waldo, err := mm.FindLink(ctx, rootDirent, nil, "/waldo", 0)
maxTraversals = 0
waldo, err := mm.FindLink(ctx, rootDirent, nil, "/waldo", &maxTraversals)
if err != nil {
t.Fatalf("could not find path %q in mount manager: %v", "/waldo", err)
}

View File

@ -350,7 +350,7 @@ func (mns *MountNamespace) Unmount(ctx context.Context, node *Dirent, detachOnly
//
// Precondition: root must be non-nil.
// Precondition: the path must be non-empty.
func (mns *MountNamespace) FindLink(ctx context.Context, root, wd *Dirent, path string, maxTraversals uint) (*Dirent, error) {
func (mns *MountNamespace) FindLink(ctx context.Context, root, wd *Dirent, path string, remainingTraversals *uint) (*Dirent, error) {
if root == nil {
panic("MountNamespace.FindLink: root must not be nil")
}
@ -419,7 +419,7 @@ func (mns *MountNamespace) FindLink(ctx context.Context, root, wd *Dirent, path
//
// See resolve for reference semantics; on err next
// will have one dropped.
current, err = mns.resolve(ctx, root, next, maxTraversals)
current, err = mns.resolve(ctx, root, next, remainingTraversals)
if err != nil {
return nil, err
}
@ -439,15 +439,15 @@ func (mns *MountNamespace) FindLink(ctx context.Context, root, wd *Dirent, path
// FindInode is identical to FindLink except the return value is resolved.
//
//go:nosplit
func (mns *MountNamespace) FindInode(ctx context.Context, root, wd *Dirent, path string, maxTraversals uint) (*Dirent, error) {
d, err := mns.FindLink(ctx, root, wd, path, maxTraversals)
func (mns *MountNamespace) FindInode(ctx context.Context, root, wd *Dirent, path string, remainingTraversals *uint) (*Dirent, error) {
d, err := mns.FindLink(ctx, root, wd, path, remainingTraversals)
if err != nil {
return nil, err
}
// See resolve for reference semantics; on err d will have the
// reference dropped.
return mns.resolve(ctx, root, d, maxTraversals)
return mns.resolve(ctx, root, d, remainingTraversals)
}
// resolve resolves the given link.
@ -458,14 +458,14 @@ func (mns *MountNamespace) FindInode(ctx context.Context, root, wd *Dirent, path
// If not successful, a reference is _also_ dropped on the node and an error
// returned. This is for convenience in using resolve directly as a return
// value.
func (mns *MountNamespace) resolve(ctx context.Context, root, node *Dirent, maxTraversals uint) (*Dirent, error) {
func (mns *MountNamespace) resolve(ctx context.Context, root, node *Dirent, remainingTraversals *uint) (*Dirent, error) {
// Resolve the path.
target, err := node.Inode.Getlink(ctx)
switch err {
case nil:
// Make sure we didn't exhaust the traversal budget.
if maxTraversals == 0 {
if *remainingTraversals == 0 {
target.DecRef()
return nil, syscall.ELOOP
}
@ -481,7 +481,7 @@ func (mns *MountNamespace) resolve(ctx context.Context, root, node *Dirent, maxT
defer node.DecRef() // See above.
// First, check if we should traverse.
if maxTraversals == 0 {
if *remainingTraversals == 0 {
return nil, syscall.ELOOP
}
@ -492,7 +492,8 @@ func (mns *MountNamespace) resolve(ctx context.Context, root, node *Dirent, maxT
}
// Find the node; we resolve relative to the current symlink's parent.
d, err := mns.FindInode(ctx, root, node.parent, targetPath, maxTraversals-1)
*remainingTraversals--
d, err := mns.FindInode(ctx, root, node.parent, targetPath, remainingTraversals)
if err != nil {
return nil, err
}
@ -544,7 +545,8 @@ func (mns *MountNamespace) ResolveExecutablePath(ctx context.Context, wd, name s
defer root.DecRef()
for _, p := range paths {
binPath := path.Join(p, name)
d, err := mns.FindInode(ctx, root, nil, binPath, linux.MaxSymlinkTraversals)
traversals := uint(linux.MaxSymlinkTraversals)
d, err := mns.FindInode(ctx, root, nil, binPath, &traversals)
if err == syserror.ENOENT || err == syserror.EACCES {
// Didn't find it here.
continue

View File

@ -77,7 +77,8 @@ func TestFindLink(t *testing.T) {
{"bar", foo, "/foo/bar"},
} {
wdPath, _ := tc.wd.FullName(root)
if d, err := mm.FindLink(ctx, root, tc.wd, tc.findPath, 0); err != nil {
maxTraversals := uint(0)
if d, err := mm.FindLink(ctx, root, tc.wd, tc.findPath, &maxTraversals); err != nil {
t.Errorf("FindLink(%q, wd=%q) failed: %v", tc.findPath, wdPath, err)
} else if got, _ := d.FullName(root); got != tc.wantPath {
t.Errorf("FindLink(%q, wd=%q) got dirent %q, want %q", tc.findPath, wdPath, got, tc.wantPath)
@ -95,7 +96,8 @@ func TestFindLink(t *testing.T) {
{"foo", foo},
} {
wdPath, _ := tc.wd.FullName(root)
if _, err := mm.FindLink(ctx, root, tc.wd, tc.findPath, 0); err == nil {
maxTraversals := uint(0)
if _, err := mm.FindLink(ctx, root, tc.wd, tc.findPath, &maxTraversals); err == nil {
t.Errorf("FindLink(%q, wd=%q) did not return error", tc.findPath, wdPath)
}
}

View File

@ -70,7 +70,8 @@ func TestMakeDirectoryTree(t *testing.T) {
defer mm.DecRef()
for _, p := range test.subdirs {
if _, err := mm.FindInode(ctx, root, nil, p, 0); err != nil {
maxTraversals := uint(0)
if _, err := mm.FindInode(ctx, root, nil, p, &maxTraversals); err != nil {
t.Errorf("%s: failed to find node %s: %v", test.name, p, err)
break
}

View File

@ -634,10 +634,11 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID,
args.Root = nil
// Grab the working directory.
remainingTraversals := uint(args.MaxSymlinkTraversals)
wd := root // Default.
if args.WorkingDirectory != "" {
var err error
wd, err = k.mounts.FindInode(ctx, root, nil, args.WorkingDirectory, args.MaxSymlinkTraversals)
wd, err = k.mounts.FindInode(ctx, root, nil, args.WorkingDirectory, &remainingTraversals)
if err != nil {
return nil, 0, fmt.Errorf("failed to find initial working directory %q: %v", args.WorkingDirectory, err)
}
@ -656,7 +657,8 @@ func (k *Kernel) CreateProcess(args CreateProcessArgs) (*ThreadGroup, ThreadID,
}
// Create a fresh task context.
tc, err := k.LoadTaskImage(ctx, k.mounts, root, wd, args.MaxSymlinkTraversals, args.Filename, args.Argv, args.Envv, k.featureSet)
remainingTraversals = uint(args.MaxSymlinkTraversals)
tc, err := k.LoadTaskImage(ctx, k.mounts, root, wd, &remainingTraversals, args.Filename, args.Argv, args.Envv, k.featureSet)
if err != nil {
return nil, 0, err
}

View File

@ -142,7 +142,7 @@ func (t *Task) Stack() *arch.Stack {
// * argv: Binary argv
// * envv: Binary envv
// * fs: Binary FeatureSet
func (k *Kernel) LoadTaskImage(ctx context.Context, mounts *fs.MountNamespace, root, wd *fs.Dirent, maxTraversals uint, filename string, argv, envv []string, fs *cpuid.FeatureSet) (*TaskContext, error) {
func (k *Kernel) LoadTaskImage(ctx context.Context, mounts *fs.MountNamespace, root, wd *fs.Dirent, maxTraversals *uint, filename string, argv, envv []string, fs *cpuid.FeatureSet) (*TaskContext, error) {
// Prepare a new user address space to load into.
m := mm.NewMemoryManager(k)
defer m.DecUsers(ctx)

View File

@ -610,7 +610,7 @@ func loadInterpreterELF(ctx context.Context, m *mm.MemoryManager, f *fs.File, in
//
// Preconditions:
// * f is an ELF file
func loadELF(ctx context.Context, m *mm.MemoryManager, mounts *fs.MountNamespace, root, wd *fs.Dirent, maxTraversals uint, fs *cpuid.FeatureSet, f *fs.File) (loadedELF, arch.Context, error) {
func loadELF(ctx context.Context, m *mm.MemoryManager, mounts *fs.MountNamespace, root, wd *fs.Dirent, maxTraversals *uint, fs *cpuid.FeatureSet, f *fs.File) (loadedELF, arch.Context, error) {
bin, ac, err := loadInitialELF(ctx, m, fs, f)
if err != nil {
ctx.Infof("Error loading binary: %v", err)

View File

@ -55,7 +55,7 @@ func readFull(ctx context.Context, f *fs.File, dst usermem.IOSequence, offset in
// installed in the Task FDMap. The caller takes ownership of both.
//
// name must be a readable, executable, regular file.
func openPath(ctx context.Context, mm *fs.MountNamespace, root, wd *fs.Dirent, maxTraversals uint, name string) (*fs.Dirent, *fs.File, error) {
func openPath(ctx context.Context, mm *fs.MountNamespace, root, wd *fs.Dirent, maxTraversals *uint, name string) (*fs.Dirent, *fs.File, error) {
if name == "" {
ctx.Infof("cannot open empty name")
return nil, nil, syserror.ENOENT
@ -136,9 +136,9 @@ const (
// * arch.Context matching the binary arch
// * fs.Dirent of the binary file
// * Possibly updated argv
func loadPath(ctx context.Context, m *mm.MemoryManager, mounts *fs.MountNamespace, root, wd *fs.Dirent, maxTraversals uint, fs *cpuid.FeatureSet, filename string, argv, envv []string) (loadedELF, arch.Context, *fs.Dirent, []string, error) {
func loadPath(ctx context.Context, m *mm.MemoryManager, mounts *fs.MountNamespace, root, wd *fs.Dirent, remainingTraversals *uint, fs *cpuid.FeatureSet, filename string, argv, envv []string) (loadedELF, arch.Context, *fs.Dirent, []string, error) {
for i := 0; i < maxLoaderAttempts; i++ {
d, f, err := openPath(ctx, mounts, root, wd, maxTraversals, filename)
d, f, err := openPath(ctx, mounts, root, wd, remainingTraversals, filename)
if err != nil {
ctx.Infof("Error opening %s: %v", filename, err)
return loadedELF{}, nil, nil, nil, err
@ -163,7 +163,7 @@ func loadPath(ctx context.Context, m *mm.MemoryManager, mounts *fs.MountNamespac
switch {
case bytes.Equal(hdr[:], []byte(elfMagic)):
loaded, ac, err := loadELF(ctx, m, mounts, root, wd, maxTraversals, fs, f)
loaded, ac, err := loadELF(ctx, m, mounts, root, wd, remainingTraversals, fs, f)
if err != nil {
ctx.Infof("Error loading ELF: %v", err)
return loadedELF{}, nil, nil, nil, err
@ -196,7 +196,7 @@ func loadPath(ctx context.Context, m *mm.MemoryManager, mounts *fs.MountNamespac
// Preconditions:
// * The Task MemoryManager is empty.
// * Load is called on the Task goroutine.
func Load(ctx context.Context, m *mm.MemoryManager, mounts *fs.MountNamespace, root, wd *fs.Dirent, maxTraversals uint, fs *cpuid.FeatureSet, filename string, argv, envv []string, extraAuxv []arch.AuxEntry, vdso *VDSO) (abi.OS, arch.Context, string, error) {
func Load(ctx context.Context, m *mm.MemoryManager, mounts *fs.MountNamespace, root, wd *fs.Dirent, maxTraversals *uint, fs *cpuid.FeatureSet, filename string, argv, envv []string, extraAuxv []arch.AuxEntry, vdso *VDSO) (abi.OS, arch.Context, string, error) {
// Load the binary itself.
loaded, ac, d, argv, err := loadPath(ctx, m, mounts, root, wd, maxTraversals, fs, filename, argv, envv)
if err != nil {

View File

@ -266,7 +266,8 @@ func (s *SocketOperations) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error {
subPath = "/"
}
var err error
d, err = t.MountNamespace().FindInode(t, root, cwd, subPath, fs.DefaultTraversalLimit)
remainingTraversals := uint(fs.DefaultTraversalLimit)
d, err = t.MountNamespace().FindInode(t, root, cwd, subPath, &remainingTraversals)
if err != nil {
// No path available.
return syserr.ErrNoSuchFile
@ -314,7 +315,8 @@ func extractEndpoint(t *kernel.Task, sockaddr []byte) (transport.BoundEndpoint,
// Find the node in the filesystem.
root := t.FSContext().RootDirectory()
cwd := t.FSContext().WorkingDirectory()
d, e := t.MountNamespace().FindInode(t, root, cwd, path, fs.DefaultTraversalLimit)
remainingTraversals := uint(fs.DefaultTraversalLimit)
d, e := t.MountNamespace().FindInode(t, root, cwd, path, &remainingTraversals)
cwd.DecRef()
root.DecRef()
if e != nil {

View File

@ -92,10 +92,11 @@ func fileOpOn(t *kernel.Task, dirFD kdefs.FD, path string, resolve bool, fn func
root := t.FSContext().RootDirectory()
// Lookup the node.
remainingTraversals := uint(linux.MaxSymlinkTraversals)
if resolve {
d, err = t.MountNamespace().FindInode(t, root, rel, path, linux.MaxSymlinkTraversals)
d, err = t.MountNamespace().FindInode(t, root, rel, path, &remainingTraversals)
} else {
d, err = t.MountNamespace().FindLink(t, root, rel, path, linux.MaxSymlinkTraversals)
d, err = t.MountNamespace().FindLink(t, root, rel, path, &remainingTraversals)
}
root.DecRef()
if wd != nil {
@ -312,7 +313,8 @@ func createAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, flags uint, mod
fileFlags.LargeFile = true
// Does this file exist already?
targetDirent, err := t.MountNamespace().FindInode(t, root, d, name, linux.MaxSymlinkTraversals)
remainingTraversals := uint(linux.MaxSymlinkTraversals)
targetDirent, err := t.MountNamespace().FindInode(t, root, d, name, &remainingTraversals)
var newFile *fs.File
switch err {
case nil:
@ -997,7 +999,8 @@ func mkdirAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, mode linux.FileM
}
// Does this directory exist already?
f, err := t.MountNamespace().FindInode(t, root, d, name, linux.MaxSymlinkTraversals)
remainingTraversals := uint(linux.MaxSymlinkTraversals)
f, err := t.MountNamespace().FindInode(t, root, d, name, &remainingTraversals)
switch err {
case nil:
// The directory existed.

View File

@ -103,7 +103,8 @@ func Execve(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
defer wd.DecRef()
// Load the new TaskContext.
tc, err := t.Kernel().LoadTaskImage(t, t.MountNamespace(), root, wd, linux.MaxSymlinkTraversals, filename, argv, envv, t.Arch().FeatureSet())
maxTraversals := uint(linux.MaxSymlinkTraversals)
tc, err := t.Kernel().LoadTaskImage(t, t.MountNamespace(), root, wd, &maxTraversals, filename, argv, envv, t.Arch().FeatureSet())
if err != nil {
return 0, nil, err
}

View File

@ -338,7 +338,8 @@ func mountSubmount(ctx context.Context, conf *Config, mns *fs.MountNamespace, ro
}
}
dirent, err := mns.FindInode(ctx, root, root, m.Destination, 0 /* maxTraversals */)
maxTraversals := uint(0)
dirent, err := mns.FindInode(ctx, root, root, m.Destination, &maxTraversals)
if err != nil {
return fmt.Errorf("failed to find mount destination %q: %v", m.Destination, err)
}
@ -582,7 +583,8 @@ func setupContainerFS(procArgs *kernel.CreateProcessArgs, spec *specs.Spec, conf
defer globalRoot.DecRef()
// Create mount point for the container's rootfs.
contDir, err := mns.FindInode(ctx, globalRoot, nil, ChildContainersDir, 0 /* TraversalLimit */)
maxTraversals := uint(0)
contDir, err := mns.FindInode(ctx, globalRoot, nil, ChildContainersDir, &maxTraversals)
if err != nil {
return fmt.Errorf("couldn't find child container dir %q: %v", ChildContainersDir, err)
}
@ -656,7 +658,8 @@ func destroyContainerFS(ctx context.Context, cid string, k *kernel.Kernel) error
mnsRoot := mns.Root()
defer mnsRoot.DecRef()
containerRoot := path.Join(ChildContainersDir, cid)
containerRootDirent, err := mns.FindInode(ctx, mnsRoot, nil, containerRoot, 0 /* maxTraversals */)
maxTraversals := uint(0)
containerRootDirent, err := mns.FindInode(ctx, mnsRoot, nil, containerRoot, &maxTraversals)
if err == syserror.ENOENT {
// Container must have been destroyed already. That's fine.
return nil
@ -691,7 +694,8 @@ func destroyContainerFS(ctx context.Context, cid string, k *kernel.Kernel) error
// Get a reference to the parent directory and remove the root
// container directory.
containersDirDirent, err := mns.FindInode(ctx, mnsRoot, nil, ChildContainersDir, 0 /* maxTraversals */)
maxTraversals = 0
containersDirDirent, err := mns.FindInode(ctx, mnsRoot, nil, ChildContainersDir, &maxTraversals)
if err != nil {
return fmt.Errorf("error finding containers directory %q: %v", ChildContainersDir, err)
}

View File

@ -406,7 +406,8 @@ func TestCreateMountNamespace(t *testing.T) {
root := mm.Root()
defer root.DecRef()
for _, p := range tc.expectedPaths {
if d, err := mm.FindInode(ctx, root, root, p, 0); err != nil {
maxTraversals := uint(0)
if d, err := mm.FindInode(ctx, root, root, p, &maxTraversals); err != nil {
t.Errorf("expected path %v to exist with spec %v, but got error %v", p, tc.spec, err)
} else {
d.DecRef()