Fix deadlock in /proc/[pid]/fd/[num]

In order to resolve path names, fsSymlink.Readlink() may need to reenter
kernfs. Change the code so that kernfs.Inode.Readlink() is called without
locks and document the new contract.

PiperOrigin-RevId: 371770222
This commit is contained in:
Fabricio Voznika
2021-05-03 13:50:37 -07:00
committed by gVisor bot
parent 7cafac9f42
commit 1947c87342
4 changed files with 26 additions and 2 deletions
+10 -2
View File
@@ -612,16 +612,24 @@ afterTrailingSymlink:
// ReadlinkAt implements vfs.FilesystemImpl.ReadlinkAt.
func (fs *Filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (string, error) {
fs.mu.RLock()
defer fs.processDeferredDecRefs(ctx)
defer fs.mu.RUnlock()
fs.mu.RLock()
d, err := fs.walkExistingLocked(ctx, rp)
if err != nil {
fs.mu.RUnlock()
return "", err
}
if !d.isSymlink() {
fs.mu.RUnlock()
return "", syserror.EINVAL
}
// Inode.Readlink() cannot be called holding fs locks.
d.IncRef()
defer d.DecRef(ctx)
fs.mu.RUnlock()
return d.inode.Readlink(ctx, rp.Mount())
}
+6
View File
@@ -534,6 +534,9 @@ func (d *Dentry) FSLocalPath() string {
// - Checking that dentries passed to methods are of the appropriate file type.
// - Checking permissions.
//
// Inode functions may be called holding filesystem wide locks and are not
// allowed to call vfs functions that may reenter, unless otherwise noted.
//
// Specific responsibilities of implementations are documented below.
type Inode interface {
// Methods related to reference counting. A generic implementation is
@@ -680,6 +683,9 @@ type inodeDirectory interface {
type inodeSymlink interface {
// Readlink returns the target of a symbolic link. If an inode is not a
// symlink, the implementation should return EINVAL.
//
// Readlink is called with no kernfs locks held, so it may reenter if needed
// to resolve symlink targets.
Readlink(ctx context.Context, mnt *vfs.Mount) (string, error)
// Getlink returns the target of a symbolic link, as used by path
+2
View File
@@ -221,6 +221,8 @@ func (s *fdSymlink) Readlink(ctx context.Context, _ *vfs.Mount) (string, error)
defer file.DecRef(ctx)
root := vfs.RootFromContext(ctx)
defer root.DecRef(ctx)
// Note: it's safe to reenter kernfs from Readlink if needed to resolve path.
return s.task.Kernel().VFS().PathnameWithDeleted(ctx, root, file.VirtualDentry())
}
+8
View File
@@ -2698,6 +2698,14 @@ TEST(Proc, Statfs) {
EXPECT_EQ(st.f_namelen, NAME_MAX);
}
// Tests that /proc/[pid]/fd/[num] can resolve to a path inside /proc.
TEST(Proc, ResolveSymlinkToProc) {
const auto proc = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/cmdline", 0));
const auto path = JoinPath("/proc/self/fd/", absl::StrCat(proc.get()));
const auto target = ASSERT_NO_ERRNO_AND_VALUE(ReadLink(path));
EXPECT_EQ(target, JoinPath("/proc/", absl::StrCat(getpid()), "/cmdline"));
}
} // namespace
} // namespace testing
} // namespace gvisor