Skip to content

Commit 10bc047

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 b4db948 commit 10bc047

4 files changed

Lines changed: 51 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: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@
153153
#ifdef HAVE_LINUX_RANDOM_H
154154
# include <linux/random.h> // GRND_RANDOM
155155
#endif
156-
#ifdef HAVE_GETRANDOM_SYSCALL
157-
# include <sys/syscall.h> // syscall()
156+
#ifdef HAVE_SYS_SYSCALL_H
157+
# include <sys/syscall.h> // syscall(), __NR_xxx syscall numbers
158158
#endif
159159

160160
#ifdef HAVE_POSIX_SPAWN
@@ -207,6 +207,20 @@
207207
# include <sanitizer/msan_interface.h> // __msan_unpoison()
208208
#endif
209209

210+
/* copy_file_range() and memfd_create() only got a libc wrapper in glibc 2.27.
211+
Prefer the wrapper, but fall back to the raw syscall when the libc we build
212+
against is older, so that a binary built against such a libc still provides
213+
the functions on a kernel that implements them. memfd_create() also needs
214+
MFD_CLOEXEC, which comes from the same headers as its wrapper. */
215+
#if defined(HAVE_COPY_FILE_RANGE) || \
216+
(defined(__linux__) && defined(__NR_copy_file_range))
217+
# define _Py_HAVE_COPY_FILE_RANGE
218+
#endif
219+
#if defined(HAVE_MEMFD_CREATE) || \
220+
(defined(__linux__) && defined(__NR_memfd_create) && defined(MFD_CLOEXEC))
221+
# define _Py_HAVE_MEMFD_CREATE
222+
#endif
223+
210224

211225
// --- More complex system includes -----------------------------------------
212226

@@ -13044,7 +13058,7 @@ os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
1304413058
}
1304513059
#endif /* HAVE_PWRITEV */
1304613060

13047-
#ifdef HAVE_COPY_FILE_RANGE
13061+
#ifdef _Py_HAVE_COPY_FILE_RANGE
1304813062
/*[clinic input]
1304913063

1305013064
os.copy_file_range
@@ -13096,7 +13110,13 @@ os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count,
1309613110

1309713111
do {
1309813112
Py_BEGIN_ALLOW_THREADS
13113+
#ifdef HAVE_COPY_FILE_RANGE
1309913114
ret = copy_file_range(src, p_offset_src, dst, p_offset_dst, count, flags);
13115+
#else
13116+
/* Largefile support makes off_t 64-bit, as the kernel expects. */
13117+
ret = syscall(__NR_copy_file_range, src, p_offset_src, dst, p_offset_dst,
13118+
count, flags);
13119+
#endif
1310013120
Py_END_ALLOW_THREADS
1310113121
} while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
1310213122

@@ -13106,7 +13126,7 @@ os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count,
1310613126

1310713127
return PyLong_FromSsize_t(ret);
1310813128
}
13109-
#endif /* HAVE_COPY_FILE_RANGE*/
13129+
#endif /* _Py_HAVE_COPY_FILE_RANGE */
1311013130

1311113131
#if (defined(HAVE_SPLICE) && !defined(_AIX))
1311213132
/*[clinic input]
@@ -15808,7 +15828,7 @@ os_urandom_impl(PyObject *module, Py_ssize_t size)
1580815828
return PyBytesWriter_Finish(writer);
1580915829
}
1581015830

15811-
#ifdef HAVE_MEMFD_CREATE
15831+
#ifdef _Py_HAVE_MEMFD_CREATE
1581215832
/*[clinic input]
1581315833
os.memfd_create
1581415834

@@ -15824,7 +15844,11 @@ os_memfd_create_impl(PyObject *module, PyObject *name, unsigned int flags)
1582415844
int fd;
1582515845
const char *bytes = PyBytes_AS_STRING(name);
1582615846
Py_BEGIN_ALLOW_THREADS
15847+
#ifdef HAVE_MEMFD_CREATE
1582715848
fd = memfd_create(bytes, flags);
15849+
#else
15850+
fd = syscall(__NR_memfd_create, bytes, flags);
15851+
#endif
1582815852
Py_END_ALLOW_THREADS
1582915853
if (fd == -1) {
1583015854
return PyErr_SetFromErrno(PyExc_OSError);
@@ -18433,7 +18457,7 @@ all_ins(PyObject *m)
1843318457
if (PyModule_AddIntMacro(m, GRND_RANDOM)) return -1;
1843418458
if (PyModule_AddIntMacro(m, GRND_NONBLOCK)) return -1;
1843518459
#endif
18436-
#ifdef HAVE_MEMFD_CREATE
18460+
#ifdef _Py_HAVE_MEMFD_CREATE
1843718461
if (PyModule_AddIntMacro(m, MFD_CLOEXEC)) return -1;
1843818462
if (PyModule_AddIntMacro(m, MFD_ALLOW_SEALING)) return -1;
1843918463
#ifdef MFD_HUGETLB
@@ -18481,7 +18505,7 @@ all_ins(PyObject *m)
1848118505
#ifdef MFD_HUGE_16GB
1848218506
if (PyModule_AddIntMacro(m, MFD_HUGE_16GB)) return -1;
1848318507
#endif
18484-
#endif /* HAVE_MEMFD_CREATE */
18508+
#endif /* _Py_HAVE_MEMFD_CREATE */
1848518509

1848618510
#if defined(HAVE_EVENTFD) && defined(EFD_CLOEXEC)
1848718511
if (PyModule_AddIntMacro(m, EFD_CLOEXEC)) return -1;
@@ -18737,7 +18761,7 @@ static const struct have_function {
1873718761
{ "HAVE_LUTIMES", NULL },
1873818762
#endif
1873918763

18740-
#ifdef HAVE_MEMFD_CREATE
18764+
#ifdef _Py_HAVE_MEMFD_CREATE
1874118765
{ "HAVE_MEMFD_CREATE", NULL },
1874218766
#endif
1874318767

0 commit comments

Comments
 (0)