Thanks to visit codestin.com
Credit goes to github.com

Skip to content

gh-118441: Limit posixpath.realpath(..., strict=True) symlinks #119172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
9 changes: 6 additions & 3 deletions Lib/posixpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@ def abspath(path):
# Return a canonical path (i.e. the absolute location of a file on the
# filesystem).

_MAXLINKS = 40 # TODO: use limit set by OS

def realpath(filename, *, strict=False):
"""Return the canonical path of the specified filename, eliminating any
symbolic links encountered in the path."""
Expand All @@ -402,7 +404,8 @@ def realpath(filename, *, strict=False):
curdir = '.'
pardir = '..'
getcwd = os.getcwd
return _realpath(filename, strict, sep, curdir, pardir, getcwd)
return _realpath(filename, strict, sep, curdir, pardir, getcwd,
maxlinks=_MAXLINKS if strict else None)

def _realpath(filename, strict=False, sep=sep, curdir=curdir, pardir=pardir,
getcwd=os.getcwd, lstat=os.lstat, readlink=os.readlink, maxlinks=None):
Expand Down Expand Up @@ -453,7 +456,7 @@ def _realpath(filename, strict=False, sep=sep, curdir=curdir, pardir=pardir,
if link_count > maxlinks:
if strict:
raise OSError(errno.ELOOP, os.strerror(errno.ELOOP),
newpath)
filename)
path = newpath
continue
elif newpath in seen:
Expand All @@ -465,7 +468,7 @@ def _realpath(filename, strict=False, sep=sep, curdir=curdir, pardir=pardir,
# The symlink is not resolved, so we must have a symlink loop.
if strict:
raise OSError(errno.ELOOP, os.strerror(errno.ELOOP),
newpath)
filename)
path = newpath
continue
target = readlink(newpath)
Expand Down
27 changes: 26 additions & 1 deletion Lib/test/test_posixpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import posixpath
import sys
import unittest
from posixpath import realpath, abspath, dirname, basename
from posixpath import _MAXLINKS, realpath, abspath, dirname, basename
from test import test_genericpath
from test.support import import_helper
from test.support import cpython_only, os_helper
Expand Down Expand Up @@ -689,6 +689,31 @@ def test_realpath_unreadable_symlink(self):
os.chmod(ABSTFN, 0o755, follow_symlinks=False)
os.unlink(ABSTFN)

@os_helper.skip_unless_symlink
@skip_if_ABSTFN_contains_backslash
def test_realpath_too_many_symlinks(self):
try:
os.mkdir(ABSTFN)
os.symlink('.', f'{ABSTFN}/link')
self.assertEqual(realpath(ABSTFN + '/link' * _MAXLINKS), ABSTFN)
self.assertEqual(realpath(ABSTFN + '/link' * _MAXLINKS,
strict=True), ABSTFN)
self.assertEqual(realpath(ABSTFN + '/link' * (_MAXLINKS+1)), ABSTFN)
with self.assertRaises(OSError):
realpath(ABSTFN + '/link' * (_MAXLINKS+1), strict=True)

# Test using relative path as well.
with os_helper.change_cwd(ABSTFN):
self.assertEqual(realpath('link/' * _MAXLINKS), ABSTFN)
self.assertEqual(realpath('link/' * _MAXLINKS, strict=True),
ABSTFN)
self.assertEqual(realpath('link/' * (_MAXLINKS+1)), ABSTFN)
with self.assertRaises(OSError):
realpath('link/' * (_MAXLINKS+1), strict=True)
finally:
os_helper.unlink(f'{ABSTFN}/link')
safe_rmdir(ABSTFN)

def test_relpath(self):
(real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar")
try:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix error message for :func:`os.path.realpath` on Unix.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we remove this from the news entry?

:func:`os.path.realpath` now raises :exc:`OSError` when *strict* mode is enabled and a path with too many symlinks is supplied.
Loading