Skip to content

Commit 77796d0

Browse files
[3.9] gh-97897: Prevent os.mkfifo and os.mknod segfaults with macOS 13 SDK (GH-97944) (#97968)
The macOS 13 SDK includes support for the `mkfifoat` and `mknodat` system calls. Using the `dir_fd` option with either `os.mkfifo` or `os.mknod` could result in a segfault if cpython is built with the macOS 13 SDK but run on an earlier version of macOS. Prevent this by adding runtime support for detection of these system calls ("weaklinking") as is done for other newer syscalls on macOS. (cherry picked from commit 6d0a019) Co-authored-by: Ned Deily <[email protected]>
1 parent 358b7a4 commit 77796d0

File tree

3 files changed

+80
-8
lines changed

3 files changed

+80
-8
lines changed

Lib/test/test_posix.py

+22
Original file line numberDiff line numberDiff line change
@@ -2040,6 +2040,28 @@ def test_mkdir(self):
20402040
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
20412041
os.mkdir("dir", dir_fd=0)
20422042

2043+
def test_mkfifo(self):
2044+
self._verify_available("HAVE_MKFIFOAT")
2045+
if self.mac_ver >= (13, 0):
2046+
self.assertIn("HAVE_MKFIFOAT", posix._have_functions)
2047+
2048+
else:
2049+
self.assertNotIn("HAVE_MKFIFOAT", posix._have_functions)
2050+
2051+
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
2052+
os.mkfifo("path", dir_fd=0)
2053+
2054+
def test_mknod(self):
2055+
self._verify_available("HAVE_MKNODAT")
2056+
if self.mac_ver >= (13, 0):
2057+
self.assertIn("HAVE_MKNODAT", posix._have_functions)
2058+
2059+
else:
2060+
self.assertNotIn("HAVE_MKNODAT", posix._have_functions)
2061+
2062+
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
2063+
os.mknod("path", dir_fd=0)
2064+
20432065
def test_rename_replace(self):
20442066
self._verify_available("HAVE_RENAMEAT")
20452067
if self.mac_ver >= (10, 10):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
The macOS 13 SDK includes support for the ``mkfifoat`` and ``mknodat`` system calls.
2+
Using the ``dir_fd`` option with either :func:`os.mkfifo` or :func:`os.mknod` could result in a
3+
segfault if cpython is built with the macOS 13 SDK but run on an earlier
4+
version of macOS. Prevent this by adding runtime support for detection of
5+
these system calls ("weaklinking") as is done for other newer syscalls on
6+
macOS.

Modules/posixmodule.c

+52-8
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@
7979
# define HAVE_FUTIMENS_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
8080
# define HAVE_UTIMENSAT_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
8181
# define HAVE_PWRITEV_RUNTIME __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)
82+
# define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
83+
# define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
8284

8385
# define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *)
8486

@@ -163,6 +165,8 @@
163165
# define HAVE_FUTIMENS_RUNTIME 1
164166
# define HAVE_UTIMENSAT_RUNTIME 1
165167
# define HAVE_PWRITEV_RUNTIME 1
168+
# define HAVE_MKFIFOAT_RUNTIME 1
169+
# define HAVE_MKNODAT_RUNTIME 1
166170
#endif
167171

168172

@@ -10482,18 +10486,35 @@ os_mkfifo_impl(PyObject *module, path_t *path, int mode, int dir_fd)
1048210486
{
1048310487
int result;
1048410488
int async_err = 0;
10489+
#ifdef HAVE_MKFIFOAT
10490+
int mkfifoat_unavailable = 0;
10491+
#endif
1048510492

1048610493
do {
1048710494
Py_BEGIN_ALLOW_THREADS
1048810495
#ifdef HAVE_MKFIFOAT
10489-
if (dir_fd != DEFAULT_DIR_FD)
10490-
result = mkfifoat(dir_fd, path->narrow, mode);
10491-
else
10496+
if (dir_fd != DEFAULT_DIR_FD) {
10497+
if (HAVE_MKFIFOAT_RUNTIME) {
10498+
result = mkfifoat(dir_fd, path->narrow, mode);
10499+
10500+
} else {
10501+
mkfifoat_unavailable = 1;
10502+
result = 0;
10503+
}
10504+
} else
1049210505
#endif
1049310506
result = mkfifo(path->narrow, mode);
1049410507
Py_END_ALLOW_THREADS
1049510508
} while (result != 0 && errno == EINTR &&
1049610509
!(async_err = PyErr_CheckSignals()));
10510+
10511+
#ifdef HAVE_MKFIFOAT
10512+
if (mkfifoat_unavailable) {
10513+
argument_unavailable_error(NULL, "dir_fd");
10514+
return NULL;
10515+
}
10516+
#endif
10517+
1049710518
if (result != 0)
1049810519
return (!async_err) ? posix_error() : NULL;
1049910520

@@ -10534,18 +10555,33 @@ os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device,
1053410555
{
1053510556
int result;
1053610557
int async_err = 0;
10558+
#ifdef HAVE_MKNODAT
10559+
int mknodat_unavailable = 0;
10560+
#endif
1053710561

1053810562
do {
1053910563
Py_BEGIN_ALLOW_THREADS
1054010564
#ifdef HAVE_MKNODAT
10541-
if (dir_fd != DEFAULT_DIR_FD)
10542-
result = mknodat(dir_fd, path->narrow, mode, device);
10543-
else
10565+
if (dir_fd != DEFAULT_DIR_FD) {
10566+
if (HAVE_MKNODAT_RUNTIME) {
10567+
result = mknodat(dir_fd, path->narrow, mode, device);
10568+
10569+
} else {
10570+
mknodat_unavailable = 1;
10571+
result = 0;
10572+
}
10573+
} else
1054410574
#endif
1054510575
result = mknod(path->narrow, mode, device);
1054610576
Py_END_ALLOW_THREADS
1054710577
} while (result != 0 && errno == EINTR &&
1054810578
!(async_err = PyErr_CheckSignals()));
10579+
#ifdef HAVE_MKNODAT
10580+
if (mknodat_unavailable) {
10581+
argument_unavailable_error(NULL, "dir_fd");
10582+
return NULL;
10583+
}
10584+
#endif
1054910585
if (result != 0)
1055010586
return (!async_err) ? posix_error() : NULL;
1055110587

@@ -15265,6 +15301,14 @@ PROBE(probe_fdopendir, HAVE_FDOPENDIR_RUNTIME)
1526515301
PROBE(probe_mkdirat, HAVE_MKDIRAT_RUNTIME)
1526615302
#endif
1526715303

15304+
#ifdef HAVE_MKFIFOAT
15305+
PROBE(probe_mkfifoat, HAVE_MKFIFOAT_RUNTIME)
15306+
#endif
15307+
15308+
#ifdef HAVE_MKNODAT
15309+
PROBE(probe_mknodat, HAVE_MKNODAT_RUNTIME)
15310+
#endif
15311+
1526815312
#ifdef HAVE_RENAMEAT
1526915313
PROBE(probe_renameat, HAVE_RENAMEAT_RUNTIME)
1527015314
#endif
@@ -15394,11 +15438,11 @@ static const struct have_function {
1539415438
#endif
1539515439

1539615440
#ifdef HAVE_MKFIFOAT
15397-
{ "HAVE_MKFIFOAT", NULL },
15441+
{ "HAVE_MKFIFOAT", probe_mkfifoat },
1539815442
#endif
1539915443

1540015444
#ifdef HAVE_MKNODAT
15401-
{ "HAVE_MKNODAT", NULL },
15445+
{ "HAVE_MKNODAT", probe_mknodat },
1540215446
#endif
1540315447

1540415448
#ifdef HAVE_OPENAT

0 commit comments

Comments
 (0)