Skip to content

Commit 1c9521f

Browse files
authored
gh-153400: Use glibc functions instead of syscall() (#155518)
Use glibc functions instead of syscall(): pidfd_open(), pidfd_getfd() and pidfd_send_signal() (glibc 2.36), gettid() and getdents64() (glibc 2.30), and getrandom() (glibc 2.25). Use unsigned int for os.getrandom() flags and signal.pidfd_send_signal() flags.
1 parent 76f2903 commit 1c9521f

12 files changed

Lines changed: 171 additions & 51 deletions
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:mod:`os` and :mod:`signal`: Use glibc functions instead of ``syscall()``:
2+
``pidfd_open()``, ``pidfd_getfd()`` and ``pidfd_send_signal()`` (glibc 2.36),
3+
``gettid()`` and ``getdents64()`` (glibc 2.30), and ``getrandom()`` (glibc
4+
2.25). Patch by Victor Stinner.

Modules/_posixsubprocess.c

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -388,20 +388,26 @@ _close_range_except(int start_fd,
388388
return 0;
389389
}
390390

391-
#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
391+
#if defined(HAVE_GETDENTS64) \
392+
|| (defined(__linux__) && defined(HAVE_SYS_SYSCALL_H))
393+
394+
#ifdef HAVE_GETDENTS64
395+
# define py_dirent64 dirent64
396+
#else
392397
/* It doesn't matter if d_name has room for NAME_MAX chars; we're using this
393398
* only to read a directory of short file descriptor number names. The kernel
394399
* will return an error if we didn't give it enough space. Highly Unlikely.
395400
* This structure is very old and stable: It will not change unless the kernel
396401
* chooses to break compatibility with all existing binaries. Highly Unlikely.
397402
*/
398-
struct linux_dirent64 {
403+
struct py_dirent64 {
399404
unsigned long long d_ino;
400405
long long d_off;
401406
unsigned short d_reclen; /* Length of this linux_dirent */
402407
unsigned char d_type;
403408
char d_name[256]; /* Filename (null-terminated) */
404409
};
410+
#endif // !HAVE_GETDENTS64
405411

406412
static int
407413
_brute_force_closer(int first, int last)
@@ -441,19 +447,27 @@ _close_open_fds_safe(int start_fd, int *fds_to_keep, Py_ssize_t fds_to_keep_len)
441447
_brute_force_closer);
442448
return;
443449
} else {
444-
char buffer[sizeof(struct linux_dirent64)];
445-
int bytes;
446-
while ((bytes = syscall(SYS_getdents64, fd_dir_fd,
447-
(struct linux_dirent64 *)buffer,
448-
sizeof(buffer))) > 0) {
449-
struct linux_dirent64 *entry;
450+
char buffer[sizeof(struct py_dirent64)];
451+
Py_ssize_t bytes;
452+
while (1) {
453+
#ifdef HAVE_GETDENTS64
454+
bytes = getdents64(fd_dir_fd, buffer, sizeof(buffer));
455+
#else
456+
bytes = syscall(SYS_getdents64, fd_dir_fd,
457+
(struct py_dirent64 *)buffer, sizeof(buffer));
458+
#endif
459+
if (bytes <= 0) {
460+
break;
461+
}
462+
463+
struct py_dirent64 *entry;
450464
int offset;
451465
#ifdef _Py_MEMORY_SANITIZER
452466
__msan_unpoison(buffer, bytes);
453467
#endif
454468
for (offset = 0; offset < bytes; offset += entry->d_reclen) {
455469
int fd;
456-
entry = (struct linux_dirent64 *)(buffer + offset);
470+
entry = (struct py_dirent64 *)(buffer + offset);
457471
if ((fd = _pos_int_from_ascii(entry->d_name)) < 0)
458472
continue; /* Not a number. */
459473
if (fd != fd_dir_fd && fd >= start_fd &&

Modules/clinic/posixmodule.c.h

Lines changed: 24 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/clinic/signalmodule.c.h

Lines changed: 20 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/posixmodule.c

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@
6868
# include "emscripten.h" // emscripten_debugger()
6969
#endif
7070

71+
#ifdef HAVE_SYS_RANDOM_H
72+
# include <sys/random.h> // getrandom()
73+
#endif
74+
7175
#ifdef HAVE_SYS_UIO_H
7276
# include <sys/uio.h>
7377
#endif
@@ -10810,8 +10814,9 @@ os_wait_impl(PyObject *module)
1081010814

1081110815

1081210816
// This system call always crashes on older Android versions.
10813-
#if defined(__linux__) && defined(__NR_pidfd_open) && \
10814-
!(defined(__ANDROID__) && __ANDROID_API__ < 31)
10817+
#if defined(HAVE_PIDFD_OPEN) \
10818+
|| (defined(__linux__) && defined(__NR_pidfd_open) \
10819+
&& !(defined(__ANDROID__) && __ANDROID_API__ < 31))
1081510820
/*[clinic input]
1081610821
os.pidfd_open
1081710822
pid: pid_t
@@ -10827,7 +10832,11 @@ static PyObject *
1082710832
os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags)
1082810833
/*[clinic end generated code: output=5c7252698947dc41 input=03058b32c389f874]*/
1082910834
{
10835+
#ifdef HAVE_PIDFD_OPEN
10836+
int fd = pidfd_open(pid, flags);
10837+
#else
1083010838
int fd = syscall(__NR_pidfd_open, pid, flags);
10839+
#endif
1083110840
if (fd < 0) {
1083210841
return posix_error();
1083310842
}
@@ -10836,8 +10845,9 @@ os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags)
1083610845
#endif
1083710846

1083810847

10839-
#if defined(__linux__) && defined(__NR_pidfd_getfd) && \
10840-
!(defined(__ANDROID__) && __ANDROID_API__ < 31)
10848+
#if defined(HAVE_PIDFD_GETFD) \
10849+
|| (defined(__linux__) && defined(__NR_pidfd_getfd) \
10850+
&& !(defined(__ANDROID__) && __ANDROID_API__ < 31))
1084110851
/*[clinic input]
1084210852
os.pidfd_getfd
1084310853
pidfd: int
@@ -10856,7 +10866,11 @@ os_pidfd_getfd_impl(PyObject *module, int pidfd, int targetfd,
1085610866
unsigned int flags)
1085710867
/*[clinic end generated code: output=e1a1415a13c7137f input=ef6417fb10deb1cc]*/
1085810868
{
10869+
#ifdef HAVE_PIDFD_GETFD
10870+
int fd = pidfd_getfd(pidfd, targetfd, flags);
10871+
#else
1085910872
int fd = syscall(__NR_pidfd_getfd, pidfd, targetfd, flags);
10873+
#endif
1086010874
if (fd < 0) {
1086110875
return posix_error();
1086210876
}
@@ -17376,19 +17390,19 @@ os_fspath_impl(PyObject *module, PyObject *path)
1737617390
return PyOS_FSPath(path);
1737717391
}
1737817392

17379-
#ifdef HAVE_GETRANDOM_SYSCALL
17393+
#if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL)
1738017394
/*[clinic input]
1738117395
os.getrandom
1738217396

1738317397
size: Py_ssize_t
17384-
flags: int=0
17398+
flags: unsigned_int(bitwise=True) = 0
1738517399

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

1738917403
static PyObject *
17390-
os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags)
17391-
/*[clinic end generated code: output=b3a618196a61409c input=59bafac39c594947]*/
17404+
os_getrandom_impl(PyObject *module, Py_ssize_t size, unsigned int flags)
17405+
/*[clinic end generated code: output=c2163c05f0e1d0a1 input=e0174983f5703f82]*/
1739217406
{
1739317407
if (size < 0) {
1739417408
errno = EINVAL;
@@ -17403,7 +17417,11 @@ os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags)
1740317417

1740417418
Py_ssize_t n;
1740517419
while (1) {
17420+
#ifdef HAVE_GETRANDOM
17421+
n = getrandom(data, size, flags);
17422+
#else
1740617423
n = syscall(SYS_getrandom, data, size, flags);
17424+
#endif
1740717425
if (n < 0 && errno == EINTR) {
1740817426
if (PyErr_CheckSignals() < 0) {
1740917427
goto error;
@@ -18511,7 +18529,7 @@ all_ins(PyObject *m)
1851118529
if (PyModule_AddIntMacro(m, RTLD_MEMBER)) return -1;
1851218530
#endif
1851318531

18514-
#ifdef HAVE_GETRANDOM_SYSCALL
18532+
#if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL)
1851518533
if (PyModule_AddIntMacro(m, GRND_RANDOM)) return -1;
1851618534
if (PyModule_AddIntMacro(m, GRND_NONBLOCK)) return -1;
1851718535
#endif

Modules/signalmodule.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@
5353
# include <pthread.h>
5454
#endif
5555

56+
#ifdef HAVE_SYS_PIDFD_H
57+
# include <sys/pidfd.h> // pidfd_send_signal()
58+
#endif
59+
5660
#ifndef SIG_ERR
5761
# define SIG_ERR ((PyOS_sighandler_t)(-1))
5862
#endif
@@ -1300,31 +1304,37 @@ signal_pthread_kill_impl(PyObject *module, unsigned long thread_id,
13001304

13011305

13021306
// This system call always crashes on older Android versions.
1303-
#if defined(__linux__) && defined(__NR_pidfd_send_signal) && \
1304-
!(defined(__ANDROID__) && __ANDROID_API__ < 31)
1307+
#if defined(HAVE_PIDFD_SEND_SIGNAL) \
1308+
|| (defined(__linux__) && defined(__NR_pidfd_send_signal) \
1309+
&& !(defined(__ANDROID__) && __ANDROID_API__ < 31))
13051310
/*[clinic input]
13061311
signal.pidfd_send_signal
13071312
13081313
pidfd: int
13091314
signalnum: int
13101315
siginfo: object = None
1311-
flags: int = 0
1316+
flags: unsigned_int(bitwise=True) = 0
13121317
/
13131318
13141319
Send a signal to a process referred to by a pid file descriptor.
13151320
[clinic start generated code]*/
13161321

13171322
static PyObject *
13181323
signal_pidfd_send_signal_impl(PyObject *module, int pidfd, int signalnum,
1319-
PyObject *siginfo, int flags)
1320-
/*[clinic end generated code: output=2d59f04a75d9cbdf input=2a6543a1f4ac2000]*/
1324+
PyObject *siginfo, unsigned int flags)
1325+
/*[clinic end generated code: output=1804b5a19d269104 input=a6e82a3c264fa19d]*/
13211326

13221327
{
13231328
if (siginfo != Py_None) {
13241329
PyErr_SetString(PyExc_TypeError, "siginfo must be None");
13251330
return NULL;
13261331
}
1327-
if (syscall(__NR_pidfd_send_signal, pidfd, signalnum, NULL, flags) < 0) {
1332+
#ifdef HAVE_PIDFD_SEND_SIGNAL
1333+
int res = pidfd_send_signal(pidfd, signalnum, NULL, flags);
1334+
#else
1335+
int res = syscall(__NR_pidfd_send_signal, pidfd, signalnum, NULL, flags);
1336+
#endif
1337+
if (res < 0) {
13281338
PyErr_SetFromErrno(PyExc_OSError);
13291339
return NULL;
13301340
}

0 commit comments

Comments
 (0)