Skip to content

Commit 96a7a84

Browse files
committed
gh-153400: Add syscall fallbacks for copy_file_range/memfd_create
glibc only grew copy_file_range() and memfd_create() in 2.27, and we compile the os functions out when the libc we build against doesn't have them. That loses them for good in a redistributable built against an older glibc, such as the python-build-standalone builds targeting glibc 2.17, even when the kernel it runs on implements the syscalls. Keep calling the libc wrappers when they are available, so we don't lose their symbol versioning and _FORTIFY_SOURCE checks, and issue the syscall directly when they aren't. If the syscall number is missing as well, the functions are still left out. pidfd_open() and pidfd_getfd() already use raw syscalls, so nothing changes for them. Include <sys/syscall.h> whenever it exists rather than only when the getrandom() syscall was detected, since __NR_* is now needed for more than getrandom(). Signed-off-by: Daan De Meyer <daan@amutable.com>
1 parent cd98657 commit 96a7a84

4 files changed

Lines changed: 47 additions & 15 deletions

File tree

Doc/library/os.rst

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,10 +1011,14 @@ as internal buffering of data.
10111011
It will always copy no bytes and return 0 as if the file was empty
10121012
because of a known Linux kernel issue.
10131013

1014-
.. availability:: Linux >= 4.5 with glibc >= 2.27.
1014+
.. availability:: Linux >= 4.5.
10151015

10161016
.. versionadded:: 3.8
10171017

1018+
.. versionchanged:: 3.16
1019+
The function is now also available when Python is built against a libc
1020+
that lacks ``copy_file_range()``, such as glibc older than 2.27.
1021+
10181022

10191023
.. function:: device_encoding(fd)
10201024

@@ -4400,10 +4404,14 @@ The following flags are used in :attr:`statvfs_result.f_flag`.
44004404
the file descriptor, and as such multiple files can have the same name
44014405
without any side effects.
44024406

4403-
.. availability:: Linux >= 3.17 with glibc >= 2.27.
4407+
.. availability:: Linux >= 3.17.
44044408

44054409
.. versionadded:: 3.8
44064410

4411+
.. versionchanged:: 3.16
4412+
The function is now also available when Python is built against a libc
4413+
that lacks ``memfd_create()``, such as glibc older than 2.27.
4414+
44074415

44084416
.. data:: MFD_CLOEXEC
44094417
MFD_ALLOW_SEALING
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
On Linux, :func:`os.copy_file_range` and :func:`os.memfd_create` now fall back
2+
to the raw syscall when the libc Python is built against does not provide the
3+
wrapper function, so they stay available on interpreters built against a libc
4+
older than glibc 2.27.

Modules/clinic/posixmodule.c.h

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

Modules/posixmodule.c

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@
157157
#ifdef HAVE_LINUX_RANDOM_H
158158
# include <linux/random.h> // GRND_RANDOM
159159
#endif
160-
#ifdef HAVE_GETRANDOM_SYSCALL
161-
# include <sys/syscall.h> // syscall()
160+
#ifdef HAVE_SYS_SYSCALL_H
161+
# include <sys/syscall.h> // syscall(), __NR_xxx syscall numbers
162162
#endif
163163

164164
#ifdef HAVE_POSIX_SPAWN
@@ -13133,7 +13133,12 @@ os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
1313313133
}
1313413134
#endif /* HAVE_PWRITEV */
1313513135

13136-
#ifdef HAVE_COPY_FILE_RANGE
13136+
#if defined(HAVE_COPY_FILE_RANGE) || \
13137+
(defined(__linux__) && defined(__NR_copy_file_range))
13138+
# define _Py_HAVE_COPY_FILE_RANGE
13139+
#endif
13140+
13141+
#ifdef _Py_HAVE_COPY_FILE_RANGE
1313713142
/*[clinic input]
1313813143

1313913144
os.copy_file_range
@@ -13185,7 +13190,13 @@ os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count,
1318513190

1318613191
do {
1318713192
Py_BEGIN_ALLOW_THREADS
13193+
#ifdef HAVE_COPY_FILE_RANGE
1318813194
ret = copy_file_range(src, p_offset_src, dst, p_offset_dst, count, flags);
13195+
#else
13196+
/* Largefile support makes off_t 64-bit, as the kernel expects. */
13197+
ret = syscall(__NR_copy_file_range, src, p_offset_src, dst, p_offset_dst,
13198+
count, flags);
13199+
#endif
1318913200
Py_END_ALLOW_THREADS
1319013201
} while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
1319113202

@@ -13195,7 +13206,7 @@ os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count,
1319513206

1319613207
return PyLong_FromSsize_t(ret);
1319713208
}
13198-
#endif /* HAVE_COPY_FILE_RANGE*/
13209+
#endif /* _Py_HAVE_COPY_FILE_RANGE */
1319913210

1320013211
#if (defined(HAVE_SPLICE) && !defined(_AIX))
1320113212
/*[clinic input]
@@ -15897,7 +15908,12 @@ os_urandom_impl(PyObject *module, Py_ssize_t size)
1589715908
return PyBytesWriter_Finish(writer);
1589815909
}
1589915910

15900-
#ifdef HAVE_MEMFD_CREATE
15911+
#if defined(HAVE_MEMFD_CREATE) || \
15912+
(defined(__linux__) && defined(__NR_memfd_create) && defined(MFD_CLOEXEC))
15913+
# define _Py_HAVE_MEMFD_CREATE
15914+
#endif
15915+
15916+
#ifdef _Py_HAVE_MEMFD_CREATE
1590115917
/*[clinic input]
1590215918
os.memfd_create
1590315919

@@ -15913,7 +15929,11 @@ os_memfd_create_impl(PyObject *module, PyObject *name, unsigned int flags)
1591315929
int fd;
1591415930
const char *bytes = PyBytes_AS_STRING(name);
1591515931
Py_BEGIN_ALLOW_THREADS
15932+
#ifdef HAVE_MEMFD_CREATE
1591615933
fd = memfd_create(bytes, flags);
15934+
#else
15935+
fd = syscall(__NR_memfd_create, bytes, flags);
15936+
#endif
1591715937
Py_END_ALLOW_THREADS
1591815938
if (fd == -1) {
1591915939
return PyErr_SetFromErrno(PyExc_OSError);
@@ -18533,7 +18553,7 @@ all_ins(PyObject *m)
1853318553
if (PyModule_AddIntMacro(m, GRND_RANDOM)) return -1;
1853418554
if (PyModule_AddIntMacro(m, GRND_NONBLOCK)) return -1;
1853518555
#endif
18536-
#ifdef HAVE_MEMFD_CREATE
18556+
#ifdef _Py_HAVE_MEMFD_CREATE
1853718557
if (PyModule_AddIntMacro(m, MFD_CLOEXEC)) return -1;
1853818558
if (PyModule_AddIntMacro(m, MFD_ALLOW_SEALING)) return -1;
1853918559
#ifdef MFD_HUGETLB
@@ -18581,7 +18601,7 @@ all_ins(PyObject *m)
1858118601
#ifdef MFD_HUGE_16GB
1858218602
if (PyModule_AddIntMacro(m, MFD_HUGE_16GB)) return -1;
1858318603
#endif
18584-
#endif /* HAVE_MEMFD_CREATE */
18604+
#endif /* _Py_HAVE_MEMFD_CREATE */
1858518605

1858618606
#if defined(HAVE_EVENTFD) && defined(EFD_CLOEXEC)
1858718607
if (PyModule_AddIntMacro(m, EFD_CLOEXEC)) return -1;
@@ -18837,7 +18857,7 @@ static const struct have_function {
1883718857
{ "HAVE_LUTIMES", NULL },
1883818858
#endif
1883918859

18840-
#ifdef HAVE_MEMFD_CREATE
18860+
#ifdef _Py_HAVE_MEMFD_CREATE
1884118861
{ "HAVE_MEMFD_CREATE", NULL },
1884218862
#endif
1884318863

0 commit comments

Comments
 (0)