Skip to content

Commit 0dc17c3

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 0dc17c3

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
@@ -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
@@ -13044,7 +13044,12 @@ os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
1304413044
}
1304513045
#endif /* HAVE_PWRITEV */
1304613046

13047-
#ifdef HAVE_COPY_FILE_RANGE
13047+
#if defined(HAVE_COPY_FILE_RANGE) || \
13048+
(defined(__linux__) && defined(__NR_copy_file_range))
13049+
# define _Py_HAVE_COPY_FILE_RANGE
13050+
#endif
13051+
13052+
#ifdef _Py_HAVE_COPY_FILE_RANGE
1304813053
/*[clinic input]
1304913054

1305013055
os.copy_file_range
@@ -13096,7 +13101,13 @@ os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count,
1309613101

1309713102
do {
1309813103
Py_BEGIN_ALLOW_THREADS
13104+
#ifdef HAVE_COPY_FILE_RANGE
1309913105
ret = copy_file_range(src, p_offset_src, dst, p_offset_dst, count, flags);
13106+
#else
13107+
/* Largefile support makes off_t 64-bit, as the kernel expects. */
13108+
ret = syscall(__NR_copy_file_range, src, p_offset_src, dst, p_offset_dst,
13109+
count, flags);
13110+
#endif
1310013111
Py_END_ALLOW_THREADS
1310113112
} while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
1310213113

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

1310713118
return PyLong_FromSsize_t(ret);
1310813119
}
13109-
#endif /* HAVE_COPY_FILE_RANGE*/
13120+
#endif /* _Py_HAVE_COPY_FILE_RANGE */
1311013121

1311113122
#if (defined(HAVE_SPLICE) && !defined(_AIX))
1311213123
/*[clinic input]
@@ -15808,7 +15819,12 @@ os_urandom_impl(PyObject *module, Py_ssize_t size)
1580815819
return PyBytesWriter_Finish(writer);
1580915820
}
1581015821

15811-
#ifdef HAVE_MEMFD_CREATE
15822+
#if defined(HAVE_MEMFD_CREATE) || \
15823+
(defined(__linux__) && defined(__NR_memfd_create) && defined(MFD_CLOEXEC))
15824+
# define _Py_HAVE_MEMFD_CREATE
15825+
#endif
15826+
15827+
#ifdef _Py_HAVE_MEMFD_CREATE
1581215828
/*[clinic input]
1581315829
os.memfd_create
1581415830

@@ -15824,7 +15840,11 @@ os_memfd_create_impl(PyObject *module, PyObject *name, unsigned int flags)
1582415840
int fd;
1582515841
const char *bytes = PyBytes_AS_STRING(name);
1582615842
Py_BEGIN_ALLOW_THREADS
15843+
#ifdef HAVE_MEMFD_CREATE
1582715844
fd = memfd_create(bytes, flags);
15845+
#else
15846+
fd = syscall(__NR_memfd_create, bytes, flags);
15847+
#endif
1582815848
Py_END_ALLOW_THREADS
1582915849
if (fd == -1) {
1583015850
return PyErr_SetFromErrno(PyExc_OSError);
@@ -18433,7 +18453,7 @@ all_ins(PyObject *m)
1843318453
if (PyModule_AddIntMacro(m, GRND_RANDOM)) return -1;
1843418454
if (PyModule_AddIntMacro(m, GRND_NONBLOCK)) return -1;
1843518455
#endif
18436-
#ifdef HAVE_MEMFD_CREATE
18456+
#ifdef _Py_HAVE_MEMFD_CREATE
1843718457
if (PyModule_AddIntMacro(m, MFD_CLOEXEC)) return -1;
1843818458
if (PyModule_AddIntMacro(m, MFD_ALLOW_SEALING)) return -1;
1843918459
#ifdef MFD_HUGETLB
@@ -18481,7 +18501,7 @@ all_ins(PyObject *m)
1848118501
#ifdef MFD_HUGE_16GB
1848218502
if (PyModule_AddIntMacro(m, MFD_HUGE_16GB)) return -1;
1848318503
#endif
18484-
#endif /* HAVE_MEMFD_CREATE */
18504+
#endif /* _Py_HAVE_MEMFD_CREATE */
1848518505

1848618506
#if defined(HAVE_EVENTFD) && defined(EFD_CLOEXEC)
1848718507
if (PyModule_AddIntMacro(m, EFD_CLOEXEC)) return -1;
@@ -18737,7 +18757,7 @@ static const struct have_function {
1873718757
{ "HAVE_LUTIMES", NULL },
1873818758
#endif
1873918759

18740-
#ifdef HAVE_MEMFD_CREATE
18760+
#ifdef _Py_HAVE_MEMFD_CREATE
1874118761
{ "HAVE_MEMFD_CREATE", NULL },
1874218762
#endif
1874318763

0 commit comments

Comments
 (0)