summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2023-12-05 15:40:49 (GMT)
committerGitHub <noreply@github.com>2023-12-05 15:40:49 (GMT)
commit563ccded6e83bfdd8c5622663c4edb679e96e08b (patch)
tree63294ad3f972317a65bf88d58596469a7f92c971
parentbc68f4a4abcfbea60bb1db1ccadb07613561931c (diff)
downloadcpython-563ccded6e83bfdd8c5622663c4edb679e96e08b.zip
cpython-563ccded6e83bfdd8c5622663c4edb679e96e08b.tar.gz
cpython-563ccded6e83bfdd8c5622663c4edb679e96e08b.tar.bz2
gh-94692: Only catch OSError in shutil.rmtree() (#112756)
Previously a symlink attack resistant version of shutil.rmtree() could ignore or pass to the error handler arbitrary exception when invalid arguments were provided.
-rw-r--r--Lib/shutil.py4
-rw-r--r--Lib/test/test_shutil.py33
-rw-r--r--Misc/NEWS.d/next/Library/2023-12-05-16-20-40.gh-issue-94692.-e5C3c.rst4
3 files changed, 22 insertions, 19 deletions
diff --git a/Lib/shutil.py b/Lib/shutil.py
index 93b00d7..bdbbcbc 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -768,13 +768,13 @@ def rmtree(path, ignore_errors=False, onerror=None, *, onexc=None, dir_fd=None):
# lstat()/open()/fstat() trick.
try:
orig_st = os.lstat(path, dir_fd=dir_fd)
- except Exception as err:
+ except OSError as err:
onexc(os.lstat, path, err)
return
try:
fd = os.open(path, os.O_RDONLY, dir_fd=dir_fd)
fd_closed = False
- except Exception as err:
+ except OSError as err:
onexc(os.open, path, err)
return
try:
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index 7ea2496..9b8ec42 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -317,7 +317,7 @@ class TestRmTree(BaseTest, unittest.TestCase):
self.assertTrue(os.path.exists(dir3))
self.assertTrue(os.path.exists(file1))
- def test_rmtree_errors_onerror(self):
+ def test_rmtree_errors(self):
# filename is guaranteed not to exist
filename = tempfile.mktemp(dir=self.mkdtemp())
self.assertRaises(FileNotFoundError, shutil.rmtree, filename)
@@ -326,8 +326,8 @@ class TestRmTree(BaseTest, unittest.TestCase):
# existing file
tmpdir = self.mkdtemp()
- write_file((tmpdir, "tstfile"), "")
filename = os.path.join(tmpdir, "tstfile")
+ write_file(filename, "")
with self.assertRaises(NotADirectoryError) as cm:
shutil.rmtree(filename)
self.assertEqual(cm.exception.filename, filename)
@@ -335,6 +335,19 @@ class TestRmTree(BaseTest, unittest.TestCase):
# test that ignore_errors option is honored
shutil.rmtree(filename, ignore_errors=True)
self.assertTrue(os.path.exists(filename))
+
+ self.assertRaises(TypeError, shutil.rmtree, None)
+ self.assertRaises(TypeError, shutil.rmtree, None, ignore_errors=True)
+ exc = TypeError if shutil.rmtree.avoids_symlink_attacks else NotImplementedError
+ with self.assertRaises(exc):
+ shutil.rmtree(filename, dir_fd='invalid')
+ with self.assertRaises(exc):
+ shutil.rmtree(filename, dir_fd='invalid', ignore_errors=True)
+
+ def test_rmtree_errors_onerror(self):
+ tmpdir = self.mkdtemp()
+ filename = os.path.join(tmpdir, "tstfile")
+ write_file(filename, "")
errors = []
def onerror(*args):
errors.append(args)
@@ -350,23 +363,9 @@ class TestRmTree(BaseTest, unittest.TestCase):
self.assertEqual(errors[1][2][1].filename, filename)
def test_rmtree_errors_onexc(self):
- # filename is guaranteed not to exist
- filename = tempfile.mktemp(dir=self.mkdtemp())
- self.assertRaises(FileNotFoundError, shutil.rmtree, filename)
- # test that ignore_errors option is honored
- shutil.rmtree(filename, ignore_errors=True)
-
- # existing file
tmpdir = self.mkdtemp()
- write_file((tmpdir, "tstfile"), "")
filename = os.path.join(tmpdir, "tstfile")
- with self.assertRaises(NotADirectoryError) as cm:
- shutil.rmtree(filename)
- self.assertEqual(cm.exception.filename, filename)
- self.assertTrue(os.path.exists(filename))
- # test that ignore_errors option is honored
- shutil.rmtree(filename, ignore_errors=True)
- self.assertTrue(os.path.exists(filename))
+ write_file(filename, "")
errors = []
def onexc(*args):
errors.append(args)
diff --git a/Misc/NEWS.d/next/Library/2023-12-05-16-20-40.gh-issue-94692.-e5C3c.rst b/Misc/NEWS.d/next/Library/2023-12-05-16-20-40.gh-issue-94692.-e5C3c.rst
new file mode 100644
index 0000000..c67ba6c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-12-05-16-20-40.gh-issue-94692.-e5C3c.rst
@@ -0,0 +1,4 @@
+:func:`shutil.rmtree` now only catches OSError exceptions. Previously a
+symlink attack resistant version of ``shutil.rmtree()`` could ignore or pass
+to the error handler arbitrary exception when invalid arguments were
+provided.