This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: DirEntry.stat() of os.scandir() has no dir_fd parameter
Type: Stage: resolved
Components: Library (Lib) Versions: Python 3.11
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: corona10, serhiy.storchaka, vstinner
Priority: normal Keywords:

Created on 2022-01-23 03:38 by vstinner, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (2)
msg411338 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2022-01-23 03:38
I read the Rust CVE-2022-21658 vulnerability of std::fs::remove_dir_all:
https://blog.rust-lang.org/2022/01/20/cve-2022-21658.html

It's a race condition if an attacker replaces a directory with a symlink while Rust is removing the parent directory, Rust follows the symlink rather than just removing the symlink.

shutil._rmtree_safe_fd() uses os.scandir(). If entry.is_dir(follow_symlinks=False) is true, it calls entry.stat(follow_symlinks=False). It opens the directory as a file to remove the directory. It checks os.path.samestat(orig_st, os.fstat(dirfd)): if it's false, it raises an exception:

try:
    # This can only happen if someone replaces
    # a directory with a symlink after the call to
    # os.scandir or stat.S_ISDIR above.
    raise OSError("Cannot call rmtree on a symbolic "
                  "link")
except OSError:
    onerror(os.path.islink, fullname, sys.exc_info())

I understand that this check is in place to detect the Rust CVE-2022-21658 vulnerability.

I noticed that the first entry.is_dir(follow_symlinks=False) call does a stat() syscall, but it doesn't pass the directory file descriptor. It would be even safer to pass it, just in case if the parent directory has been modified in the meanwhile as well.
msg411341 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2022-01-23 04:29
Oh, I didn't test os.scandir() properly. If you pass a file descriptor to os.scandir(), it yields DirEntry entries with contains the directory FD. Ignore my request, Python works as expected :-D
History
Date User Action Args
2022-04-11 14:59:55adminsetgithub: 90636
2022-01-23 04:29:57vstinnersetstatus: open -> closed
resolution: not a bug
messages: + msg411341

stage: resolved
2022-01-23 03:43:01corona10setnosy: + corona10
2022-01-23 03:38:48vstinnercreate