gh-153400: Use glibc functions instead of syscall() by vstinner · Pull Request #155518 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions Modules/_posixsubprocess.c
Original file line number Diff line number Diff line change
Expand Up @@ -388,20 +388,26 @@ _close_range_except(int start_fd,
return 0;
}

#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
#if defined(HAVE_GETDENTS64) \
|| (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H))

#ifdef HAVE_GETDENTS64
# define py_dirent64 dirent64
#else
/* It doesn't matter if d_name has room for NAME_MAX chars; we're using this
* only to read a directory of short file descriptor number names. The kernel
* will return an error if we didn't give it enough space. Highly Unlikely.
* This structure is very old and stable: It will not change unless the kernel
* chooses to break compatibility with all existing binaries. Highly Unlikely.
*/
struct linux_dirent64 {
struct py_dirent64 {
unsigned long long d_ino;
long long d_off;
unsigned short d_reclen; /* Length of this linux_dirent */
unsigned char d_type;
char d_name[256]; /* Filename (null-terminated) */
};
#endif // !HAVE_GETDENTS64

static int
_brute_force_closer(int first, int last)
Expand Down Expand Up @@ -441,19 +447,27 @@ _close_open_fds_safe(int start_fd, int *fds_to_keep, Py_ssize_t fds_to_keep_len)
_brute_force_closer);
return;
} else {
char buffer[sizeof(struct linux_dirent64)];
int bytes;
while ((bytes = syscall(SYS_getdents64, fd_dir_fd,
(struct linux_dirent64 *)buffer,
sizeof(buffer))) > 0) {
struct linux_dirent64 *entry;
char buffer[sizeof(struct py_dirent64)];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[post merge review] confirming that this is safe. glibc's struct dirent64 is as above and has 256 bytes of d_name space declared. I think the linux kernel itself uses a sizeless d_name[] to expect the caller to allocate extra. if that happened here we'd wind up with a tiny struct without necessarily room for a name and an error from the syscall, silently falling back to a slower or unsafe code path instead.

Py_ssize_t bytes;
while (1) {
#ifdef HAVE_GETDENTS64
bytes = getdents64(fd_dir_fd, buffer, sizeof(buffer));
#else
bytes = syscall(SYS_getdents64, fd_dir_fd,
(struct py_dirent64 *)buffer, sizeof(buffer));
#endif
if (bytes <= 0) {
break;
}

struct py_dirent64 *entry;
int offset;
#ifdef _Py_MEMORY_SANITIZER
__msan_unpoison(buffer, bytes);
#endif
for (offset = 0; offset < bytes; offset += entry->d_reclen) {
int fd;
entry = (struct linux_dirent64 *)(buffer + offset);
entry = (struct py_dirent64 *)(buffer + offset);
if ((fd = _pos_int_from_ascii(entry->d_name)) < 0)
continue; /* Not a number. */
if (fd != fd_dir_fd && fd >= start_fd &&
Expand Down
36 changes: 24 additions & 12 deletions Modules/clinic/posixmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 20 additions & 8 deletions Modules/clinic/signalmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 27 additions & 9 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
# include "emscripten.h" // emscripten_debugger()
#endif

#ifdef HAVE_SYS_RANDOM_H
# include <sys/random.h> // getrandom()
#endif

#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif
Expand Down Expand Up @@ -10810,8 +10814,9 @@ os_wait_impl(PyObject *module)


// This system call always crashes on older Android versions.
#if defined(__linux__) && defined(__NR_pidfd_open) && \
!(defined(__ANDROID__) && __ANDROID_API__ < 31)
#if defined(HAVE_PIDFD_OPEN) \
|| (defined(__linux__) && defined(__NR_pidfd_open) \
&& !(defined(__ANDROID__) && __ANDROID_API__ < 31))
/*[clinic input]
os.pidfd_open
pid: pid_t
Expand All @@ -10827,7 +10832,11 @@ static PyObject *
os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags)
/*[clinic end generated code: output=5c7252698947dc41 input=03058b32c389f874]*/
{
#ifdef HAVE_PIDFD_OPEN
int fd = pidfd_open(pid, flags);
#else
int fd = syscall(__NR_pidfd_open, pid, flags);
#endif
if (fd < 0) {
return posix_error();
}
Expand All @@ -10836,8 +10845,9 @@ os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags)
#endif


#if defined(__linux__) && defined(__NR_pidfd_getfd) && \
!(defined(__ANDROID__) && __ANDROID_API__ < 31)
#if defined(HAVE_PIDFD_GETFD) \
|| (defined(__linux__) && defined(__NR_pidfd_getfd) \
&& !(defined(__ANDROID__) && __ANDROID_API__ < 31))
/*[clinic input]
os.pidfd_getfd
pidfd: int
Expand All @@ -10856,7 +10866,11 @@ os_pidfd_getfd_impl(PyObject *module, int pidfd, int targetfd,
unsigned int flags)
/*[clinic end generated code: output=e1a1415a13c7137f input=ef6417fb10deb1cc]*/
{
#ifdef HAVE_PIDFD_GETFD
int fd = pidfd_getfd(pidfd, targetfd, flags);
#else
int fd = syscall(__NR_pidfd_getfd, pidfd, targetfd, flags);
#endif
if (fd < 0) {
return posix_error();
}
Expand Down Expand Up @@ -17369,19 +17383,19 @@ os_fspath_impl(PyObject *module, PyObject *path)
return PyOS_FSPath(path);
}

#ifdef HAVE_GETRANDOM_SYSCALL
#if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL)
/*[clinic input]
os.getrandom

size: Py_ssize_t
flags: int=0
flags: unsigned_int(bitwise=True) = 0

Obtain a series of random bytes.
[clinic start generated code]*/

static PyObject *
os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags)
/*[clinic end generated code: output=b3a618196a61409c input=59bafac39c594947]*/
os_getrandom_impl(PyObject *module, Py_ssize_t size, unsigned int flags)
/*[clinic end generated code: output=c2163c05f0e1d0a1 input=e0174983f5703f82]*/
{
if (size < 0) {
errno = EINVAL;
Expand All @@ -17396,7 +17410,11 @@ os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags)

Py_ssize_t n;
while (1) {
#ifdef HAVE_GETRANDOM
n = getrandom(data, size, flags);
#else
n = syscall(SYS_getrandom, data, size, flags);
#endif
if (n < 0 && errno == EINTR) {
if (PyErr_CheckSignals() < 0) {
goto error;
Expand Down Expand Up @@ -18504,7 +18522,7 @@ all_ins(PyObject *m)
if (PyModule_AddIntMacro(m, RTLD_MEMBER)) return -1;
#endif

#ifdef HAVE_GETRANDOM_SYSCALL
#if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL)
if (PyModule_AddIntMacro(m, GRND_RANDOM)) return -1;
if (PyModule_AddIntMacro(m, GRND_NONBLOCK)) return -1;
#endif
Expand Down
22 changes: 16 additions & 6 deletions Modules/signalmodule.c
Loading
Loading