Skip to content

Commit 6057030

Browse files
[3.11] gh-97897: Prevent os.mkfifo and os.mknod segfaults with macOS 13 SDK (GH-97944) (#97969)
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 bd3dcb3 commit 6057030

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
@@ -2090,6 +2090,28 @@ def test_mkdir(self):
20902090
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
20912091
os.mkdir("dir", dir_fd=0)
20922092

2093+
def test_mkfifo(self):
2094+
self._verify_available("HAVE_MKFIFOAT")
2095+
if self.mac_ver >= (13, 0):
2096+
self.assertIn("HAVE_MKFIFOAT", posix._have_functions)
2097+
2098+
else:
2099+
self.assertNotIn("HAVE_MKFIFOAT", posix._have_functions)
2100+
2101+
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
2102+
os.mkfifo("path", dir_fd=0)
2103+
2104+
def test_mknod(self):
2105+
self._verify_available("HAVE_MKNODAT")
2106+
if self.mac_ver >= (13, 0):
2107+
self.assertIn("HAVE_MKNODAT", posix._have_functions)
2108+
2109+
else:
2110+
self.assertNotIn("HAVE_MKNODAT", posix._have_functions)
2111+
2112+
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
2113+
os.mknod("path", dir_fd=0)
2114+
20932115
def test_rename_replace(self):
20942116
self._verify_available("HAVE_RENAMEAT")
20952117
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
@@ -91,6 +91,8 @@
9191
# define HAVE_FUTIMENS_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
9292
# define HAVE_UTIMENSAT_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
9393
# define HAVE_PWRITEV_RUNTIME __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)
94+
# define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
95+
# define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
9496

9597
# define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *)
9698

@@ -175,6 +177,8 @@
175177
# define HAVE_FUTIMENS_RUNTIME 1
176178
# define HAVE_UTIMENSAT_RUNTIME 1
177179
# define HAVE_PWRITEV_RUNTIME 1
180+
# define HAVE_MKFIFOAT_RUNTIME 1
181+
# define HAVE_MKNODAT_RUNTIME 1
178182
#endif
179183

180184

@@ -10644,18 +10648,35 @@ os_mkfifo_impl(PyObject *module, path_t *path, int mode, int dir_fd)
1064410648
{
1064510649
int result;
1064610650
int async_err = 0;
10651+
#ifdef HAVE_MKFIFOAT
10652+
int mkfifoat_unavailable = 0;
10653+
#endif
1064710654

1064810655
do {
1064910656
Py_BEGIN_ALLOW_THREADS
1065010657
#ifdef HAVE_MKFIFOAT
10651-
if (dir_fd != DEFAULT_DIR_FD)
10652-
result = mkfifoat(dir_fd, path->narrow, mode);
10653-
else
10658+
if (dir_fd != DEFAULT_DIR_FD) {
10659+
if (HAVE_MKFIFOAT_RUNTIME) {
10660+
result = mkfifoat(dir_fd, path->narrow, mode);
10661+
10662+
} else {
10663+
mkfifoat_unavailable = 1;
10664+
result = 0;
10665+
}
10666+
} else
1065410667
#endif
1065510668
result = mkfifo(path->narrow, mode);
1065610669
Py_END_ALLOW_THREADS
1065710670
} while (result != 0 && errno == EINTR &&
1065810671
!(async_err = PyErr_CheckSignals()));
10672+
10673+
#ifdef HAVE_MKFIFOAT
10674+
if (mkfifoat_unavailable) {
10675+
argument_unavailable_error(NULL, "dir_fd");
10676+
return NULL;
10677+
}
10678+
#endif
10679+
1065910680
if (result != 0)
1066010681
return (!async_err) ? posix_error() : NULL;
1066110682

@@ -10696,18 +10717,33 @@ os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device,
1069610717
{
1069710718
int result;
1069810719
int async_err = 0;
10720+
#ifdef HAVE_MKNODAT
10721+
int mknodat_unavailable = 0;
10722+
#endif
1069910723

1070010724
do {
1070110725
Py_BEGIN_ALLOW_THREADS
1070210726
#ifdef HAVE_MKNODAT
10703-
if (dir_fd != DEFAULT_DIR_FD)
10704-
result = mknodat(dir_fd, path->narrow, mode, device);
10705-
else
10727+
if (dir_fd != DEFAULT_DIR_FD) {
10728+
if (HAVE_MKNODAT_RUNTIME) {
10729+
result = mknodat(dir_fd, path->narrow, mode, device);
10730+
10731+
} else {
10732+
mknodat_unavailable = 1;
10733+
result = 0;
10734+
}
10735+
} else
1070610736
#endif
1070710737
result = mknod(path->narrow, mode, device);
1070810738
Py_END_ALLOW_THREADS
1070910739
} while (result != 0 && errno == EINTR &&
1071010740
!(async_err = PyErr_CheckSignals()));
10741+
#ifdef HAVE_MKNODAT
10742+
if (mknodat_unavailable) {
10743+
argument_unavailable_error(NULL, "dir_fd");
10744+
return NULL;
10745+
}
10746+
#endif
1071110747
if (result != 0)
1071210748
return (!async_err) ? posix_error() : NULL;
1071310749

@@ -15576,6 +15612,14 @@ PROBE(probe_fdopendir, HAVE_FDOPENDIR_RUNTIME)
1557615612
PROBE(probe_mkdirat, HAVE_MKDIRAT_RUNTIME)
1557715613
#endif
1557815614

15615+
#ifdef HAVE_MKFIFOAT
15616+
PROBE(probe_mkfifoat, HAVE_MKFIFOAT_RUNTIME)
15617+
#endif
15618+
15619+
#ifdef HAVE_MKNODAT
15620+
PROBE(probe_mknodat, HAVE_MKNODAT_RUNTIME)
15621+
#endif
15622+
1557915623
#ifdef HAVE_RENAMEAT
1558015624
PROBE(probe_renameat, HAVE_RENAMEAT_RUNTIME)
1558115625
#endif
@@ -15709,11 +15753,11 @@ static const struct have_function {
1570915753
#endif
1571015754

1571115755
#ifdef HAVE_MKFIFOAT
15712-
{ "HAVE_MKFIFOAT", NULL },
15756+
{ "HAVE_MKFIFOAT", probe_mkfifoat },
1571315757
#endif
1571415758

1571515759
#ifdef HAVE_MKNODAT
15716-
{ "HAVE_MKNODAT", NULL },
15760+
{ "HAVE_MKNODAT", probe_mknodat },
1571715761
#endif
1571815762

1571915763
#ifdef HAVE_OPENAT

0 commit comments

Comments
 (0)