mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
committed by
gVisor bot
parent
7cafac9f42
commit
1947c87342
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user