summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Radchenko <nradchenko@protonmail.com>2021-10-05 21:13:38 +0300
committerGitHub <noreply@github.com>2021-10-05 20:13:38 +0200
commit741c143ea371ef3ecf83254341f443182a9547f1 (patch)
treeaca975864e0b03b110af3f59b0d2f65ea6730cc2
parente15922b9f7f389679c73ab15ab5ac237b7c0d37f (diff)
downloadpsutil-741c143ea371ef3ecf83254341f443182a9547f1.tar.gz
Handle ENAMETOOLONG on Linux (#1940) (#1955)
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 #1940). We can do nothing to fix this in userspace; therefore these errors should be ignored.
-rw-r--r--CREDITS6
-rw-r--r--HISTORY.rst2
-rw-r--r--psutil/_pslinux.py6
-rwxr-xr-xpsutil/tests/test_linux.py35
4 files changed, 48 insertions, 1 deletions
diff --git a/CREDITS b/CREDITS
index 280c3b1c..cc7e7a59 100644
--- a/CREDITS
+++ b/CREDITS
@@ -745,6 +745,10 @@ N: David Knaack
W: https://github.com/davidkna
I: 1921
+N: Nikita Radchenko
+W: https://github.com/nradchenko
+I: 1940
+
N: MaWe2019
W: https://github.com/MaWe2019
I: 1953
@@ -761,4 +765,4 @@ I: 1598
N: Xuehai Pan
W: https://github.com/XuehaiPan
-I: 1948
+I: 1948 \ No newline at end of file
diff --git a/HISTORY.rst b/HISTORY.rst
index a8284ad2..ccc5c829 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -34,6 +34,8 @@ XXXX-XX-XX
- 1913_: [Linux] wait_procs seemingly ignoring timeout, TimeoutExpired thrown
- 1919_: [Linux] sensors_battery() can raise TypeError on PureOS.
- 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
- 1948_: Process' memoize_when_activated decorator was not thread-safe. (patch
by Xuehai Pan)
- 1953_: [Windows] disk_partitions() crashes due to insufficient buffer len.
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index 3afe6c65..1917e20d 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -832,6 +832,9 @@ class Connections:
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:['):
@@ -2101,6 +2104,9 @@ class Process(object):
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
diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py
index 23f6b221..3fda7874 100755
--- a/psutil/tests/test_linux.py
+++ b/psutil/tests/test_linux.py
@@ -1446,6 +1446,25 @@ class TestMisc(PsutilTestCase):
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
@@ -1830,6 +1849,22 @@ class TestProcess(PsutilTestCase):
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):