bpo-47260: Fix os.closerange() potentially being a no-op in a seccomp sandbox (GH-32418)

_Py_closerange() currently assumes that close_range() closes
all file descriptors even if it returns an error (other than ENOSYS).
This assumption can be wrong on Linux if a seccomp sandbox denies
the underlying syscall, pretending that it returns EPERM or EACCES.
In this case _Py_closerange() won't close any descriptors at all,
which in the worst case can be a security issue.

Fix this by falling back to other methods in case of any close_range()
error. Note that fallbacks will not be triggered on any problems with
closing individual file descriptors because close_range() is documented
to ignore such errors on both Linux[1] and FreeBSD[2].

[1] https://man7.org/linux/man-pages/man2/close_range.2.html
[2] https://www.freebsd.org/cgi/man.cgi?query=close_range&sektion=2
This commit is contained in:
Alexey Izbyshev 2022-04-08 20:40:39 +03:00 committed by GitHub
parent d6fb104690
commit 1c8b3b5d66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 7 additions and 4 deletions

View file

@ -0,0 +1,2 @@
Fix ``os.closerange()`` potentially being a no-op in a Linux seccomp
sandbox.

View file

@ -2624,10 +2624,11 @@ _Py_closerange(int first, int last)
first = Py_MAX(first, 0); first = Py_MAX(first, 0);
_Py_BEGIN_SUPPRESS_IPH _Py_BEGIN_SUPPRESS_IPH
#ifdef HAVE_CLOSE_RANGE #ifdef HAVE_CLOSE_RANGE
if (close_range(first, last, 0) == 0 || errno != ENOSYS) { if (close_range(first, last, 0) == 0) {
/* Any errors encountered while closing file descriptors are ignored; /* close_range() ignores errors when it closes file descriptors.
* ENOSYS means no kernel support, though, * Possible reasons of an error return are lack of kernel support
* so we'll fallback to the other methods. */ * or denial of the underlying syscall by a seccomp sandbox on Linux.
* Fallback to other methods in case of any error. */
} }
else else
#endif /* HAVE_CLOSE_RANGE */ #endif /* HAVE_CLOSE_RANGE */