Skip to content

Commit

Permalink
Handle ENAMETOOLONG when resolving fd symlinks in procfs on Linux (gi…
Browse files Browse the repository at this point in the history
…ampaolo#1940)

When resolving process file descriptors symlinks in procfs (/proc/PID/fd/FD),
the kernel can only deal with file paths no longer than PAGE_SIZE
(which usually equals to PATH_MAX).

https://elixir.bootlin.com/linux/v5.12/source/fs/proc/base.c#L1759

Resolving fd symlink that corresponds to a file with a path longer
than PATH_MAX with readlink(2) would result in ENAMETOOLONG error
(see details in giampaolo#1940).

We can do nothing to fix this in userspace; therefore these errors
should be ignored.
  • Loading branch information
nradchenko committed Jun 2, 2021
1 parent ea5b2df commit 6bf219e
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -744,3 +744,7 @@ I: 1913
N: David Knaack
W: https://github.com/davidkna
I: 1921

N: Nikita Radchenko
W: https://github.com/nradchenko
I: 1940
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ XXXX-XX-XX
- 1874_: [Solaris] swap output error due to incorrect range.
- 1913_: [Linux] wait_procs seemingly ignoring timeout, TimeoutExpired thrown
- 1921_: [Windows] psutil.swap_memory() shows committed memory instead of swap
- 1940_: [Linux] psutil does not handle ENAMETOOLONG when accessing process
file descriptors in procfs

5.8.0
=====
Expand Down
6 changes: 6 additions & 0 deletions psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,9 @@ def get_proc_inodes(self, pid):
if err.errno == errno.EINVAL:
# not a link
continue
if err.errno == errno.ENAMETOOLONG:
# file name too long
continue
raise
else:
if inode.startswith('socket:['):
Expand Down Expand Up @@ -2098,6 +2101,9 @@ def open_files(self):
if err.errno == errno.EINVAL:
# not a link
continue
if err.errno == errno.ENAMETOOLONG:
# file name too long
continue
raise
else:
# If path is not an absolute there's no way to tell
Expand Down
35 changes: 35 additions & 0 deletions psutil/tests/test_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -1450,6 +1450,25 @@ def test_pid_exists_no_proc_status(self):
assert psutil.pid_exists(os.getpid())
assert m.called

def test_get_proc_inodes_fd_broken(self):
# Simulate a case where /proc/{pid}/fd/{fd} symlink
# points to a file with full path longer than PATH_MAX

def get_inodes():
connections = psutil._pslinux.Connections()
connections._procfs_path = psutil._common.get_procfs_path()
return connections.get_proc_inodes(os.getpid())

p = psutil.Process()
files = p.open_files()
with open(self.get_testfn(), 'w'):
# give the kernel some time to see the new file
call_until(p.open_files, "len(ret) != %i" % len(files))
patch_point = 'psutil._pslinux.os.readlink'
with mock.patch(patch_point,
side_effect=OSError(errno.ENAMETOOLONG, "")) as m:
assert not get_inodes()
assert m.called

# =====================================================================
# --- sensors
Expand Down Expand Up @@ -1834,6 +1853,22 @@ def test_open_files_fd_gone(self):
assert not files
assert m.called

def test_open_files_fd_broken(self):
# Simulate a case where /proc/{pid}/fd/{fd} symlink
# points to a file with full path longer than PATH_MAX

p = psutil.Process()
files = p.open_files()
with open(self.get_testfn(), 'w'):
# give the kernel some time to see the new file
call_until(p.open_files, "len(ret) != %i" % len(files))
patch_point = 'psutil._pslinux.os.readlink'
with mock.patch(patch_point,
side_effect=OSError(errno.ENAMETOOLONG, "")) as m:
files = p.open_files()
assert not files
assert m.called

# --- mocked tests

def test_terminal_mocked(self):
Expand Down

0 comments on commit 6bf219e

Please sign in to comment.