diff options
author | Nikita Radchenko <nradchenko@protonmail.com> | 2021-10-05 21:13:38 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-05 20:13:38 +0200 |
commit | 741c143ea371ef3ecf83254341f443182a9547f1 (patch) | |
tree | aca975864e0b03b110af3f59b0d2f65ea6730cc2 | |
parent | e15922b9f7f389679c73ab15ab5ac237b7c0d37f (diff) | |
download | psutil-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-- | CREDITS | 6 | ||||
-rw-r--r-- | HISTORY.rst | 2 | ||||
-rw-r--r-- | psutil/_pslinux.py | 6 | ||||
-rwxr-xr-x | psutil/tests/test_linux.py | 35 |
4 files changed, 48 insertions, 1 deletions
@@ -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): |