Skip to content

Commit fe87313

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 fe87313

4 files changed

Lines changed: 46 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: 27 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,15 @@
207207
# include <sanitizer/msan_interface.h> // __msan_unpoison()
208208
#endif
209209

210+
#if defined(HAVE_COPY_FILE_RANGE) || \
211+
(defined(__linux__) && defined(__NR_copy_file_range))
212+
# define _Py_HAVE_COPY_FILE_RANGE
213+
#endif
214+
#if defined(HAVE_MEMFD_CREATE) || \
215+
(defined(__linux__) && defined(__NR_memfd_create) && defined(MFD_CLOEXEC))
216+
# define _Py_HAVE_MEMFD_CREATE
217+
#endif
218+
210219

211220
// --- More complex system includes -----------------------------------------
212221

@@ -13044,7 +13053,7 @@ os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
1304413053
}
1304513054
#endif /* HAVE_PWRITEV */
1304613055

13047-
#ifdef HAVE_COPY_FILE_RANGE
13056+
#ifdef _Py_HAVE_COPY_FILE_RANGE
1304813057
/*[clinic input]
1304913058

1305013059
os.copy_file_range
@@ -13096,7 +13105,13 @@ os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count,
1309613105

1309713106
do {
1309813107
Py_BEGIN_ALLOW_THREADS
13108+
#ifdef HAVE_COPY_FILE_RANGE
1309913109
ret = copy_file_range(src, p_offset_src, dst, p_offset_dst, count, flags);
13110+
#else
13111+
/* Largefile support makes off_t 64-bit, as the kernel expects. */
13112+
ret = syscall(__NR_copy_file_range, src, p_offset_src, dst, p_offset_dst,
13113+
count, flags);
13114+
#endif
1310013115
Py_END_ALLOW_THREADS
1310113116
} while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
1310213117

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

1310713122
return PyLong_FromSsize_t(ret);
1310813123
}
13109-
#endif /* HAVE_COPY_FILE_RANGE*/
13124+
#endif /* _Py_HAVE_COPY_FILE_RANGE */
1311013125

1311113126
#if (defined(HAVE_SPLICE) && !defined(_AIX))
1311213127
/*[clinic input]
@@ -15808,7 +15823,7 @@ os_urandom_impl(PyObject *module, Py_ssize_t size)
1580815823
return PyBytesWriter_Finish(writer);
1580915824
}
1581015825

15811-
#ifdef HAVE_MEMFD_CREATE
15826+
#ifdef _Py_HAVE_MEMFD_CREATE
1581215827
/*[clinic input]
1581315828
os.memfd_create
1581415829

@@ -15824,7 +15839,11 @@ os_memfd_create_impl(PyObject *module, PyObject *name, unsigned int flags)
1582415839
int fd;
1582515840
const char *bytes = PyBytes_AS_STRING(name);
1582615841
Py_BEGIN_ALLOW_THREADS
15842+
#ifdef HAVE_MEMFD_CREATE
1582715843
fd = memfd_create(bytes, flags);
15844+
#else
15845+
fd = syscall(__NR_memfd_create, bytes, flags);
15846+
#endif
1582815847
Py_END_ALLOW_THREADS
1582915848
if (fd == -1) {
1583015849
return PyErr_SetFromErrno(PyExc_OSError);
@@ -18433,7 +18452,7 @@ all_ins(PyObject *m)
1843318452
if (PyModule_AddIntMacro(m, GRND_RANDOM)) return -1;
1843418453
if (PyModule_AddIntMacro(m, GRND_NONBLOCK)) return -1;
1843518454
#endif
18436-
#ifdef HAVE_MEMFD_CREATE
18455+
#ifdef _Py_HAVE_MEMFD_CREATE
1843718456
if (PyModule_AddIntMacro(m, MFD_CLOEXEC)) return -1;
1843818457
if (PyModule_AddIntMacro(m, MFD_ALLOW_SEALING)) return -1;
1843918458
#ifdef MFD_HUGETLB
@@ -18481,7 +18500,7 @@ all_ins(PyObject *m)
1848118500
#ifdef MFD_HUGE_16GB
1848218501
if (PyModule_AddIntMacro(m, MFD_HUGE_16GB)) return -1;
1848318502
#endif
18484-
#endif /* HAVE_MEMFD_CREATE */
18503+
#endif /* _Py_HAVE_MEMFD_CREATE */
1848518504

1848618505
#if defined(HAVE_EVENTFD) && defined(EFD_CLOEXEC)
1848718506
if (PyModule_AddIntMacro(m, EFD_CLOEXEC)) return -1;
@@ -18737,7 +18756,7 @@ static const struct have_function {
1873718756
{ "HAVE_LUTIMES", NULL },
1873818757
#endif
1873918758

18740-
#ifdef HAVE_MEMFD_CREATE
18759+
#ifdef _Py_HAVE_MEMFD_CREATE
1874118760
{ "HAVE_MEMFD_CREATE", NULL },
1874218761
#endif
1874318762

0 commit comments

Comments
 (0)