Skip to content

Commit 1dba762

Browse files
committed
gh-97897: Prevent os.mkfifo and os.mknod segfaults with macOS 13 SDK
The macOS 13 SDK includes support for the mkfifoat and mknodat system calls. Using the dir_fd opton with either os.mkfifo() or os.mknod() can 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.
1 parent b44372e commit 1dba762

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 opton with either os.mkfifo() or os.mknod() can 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

@@ -10619,18 +10623,35 @@ os_mkfifo_impl(PyObject *module, path_t *path, int mode, int dir_fd)
1061910623
{
1062010624
int result;
1062110625
int async_err = 0;
10626+
#ifdef HAVE_MKFIFOAT
10627+
int mkfifoat_unavailable = 0;
10628+
#endif
1062210629

1062310630
do {
1062410631
Py_BEGIN_ALLOW_THREADS
1062510632
#ifdef HAVE_MKFIFOAT
10626-
if (dir_fd != DEFAULT_DIR_FD)
10627-
result = mkfifoat(dir_fd, path->narrow, mode);
10628-
else
10633+
if (dir_fd != DEFAULT_DIR_FD) {
10634+
if (HAVE_MKFIFOAT_RUNTIME) {
10635+
result = mkfifoat(dir_fd, path->narrow, mode);
10636+
10637+
} else {
10638+
mkfifoat_unavailable = 1;
10639+
result = 0;
10640+
}
10641+
} else
1062910642
#endif
1063010643
result = mkfifo(path->narrow, mode);
1063110644
Py_END_ALLOW_THREADS
1063210645
} while (result != 0 && errno == EINTR &&
1063310646
!(async_err = PyErr_CheckSignals()));
10647+
10648+
#ifdef HAVE_MKFIFOAT
10649+
if (mkfifoat_unavailable) {
10650+
argument_unavailable_error(NULL, "dir_fd");
10651+
return NULL;
10652+
}
10653+
#endif
10654+
1063410655
if (result != 0)
1063510656
return (!async_err) ? posix_error() : NULL;
1063610657

@@ -10671,18 +10692,33 @@ os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device,
1067110692
{
1067210693
int result;
1067310694
int async_err = 0;
10695+
#ifdef HAVE_MKNODAT
10696+
int mknodat_unavailable = 0;
10697+
#endif
1067410698

1067510699
do {
1067610700
Py_BEGIN_ALLOW_THREADS
1067710701
#ifdef HAVE_MKNODAT
10678-
if (dir_fd != DEFAULT_DIR_FD)
10679-
result = mknodat(dir_fd, path->narrow, mode, device);
10680-
else
10702+
if (dir_fd != DEFAULT_DIR_FD) {
10703+
if (HAVE_MKNODAT_RUNTIME) {
10704+
result = mknodat(dir_fd, path->narrow, mode, device);
10705+
10706+
} else {
10707+
mknodat_unavailable = 1;
10708+
result = 0;
10709+
}
10710+
} else
1068110711
#endif
1068210712
result = mknod(path->narrow, mode, device);
1068310713
Py_END_ALLOW_THREADS
1068410714
} while (result != 0 && errno == EINTR &&
1068510715
!(async_err = PyErr_CheckSignals()));
10716+
#ifdef HAVE_MKNODAT
10717+
if (mknodat_unavailable) {
10718+
argument_unavailable_error(NULL, "dir_fd");
10719+
return NULL;
10720+
}
10721+
#endif
1068610722
if (result != 0)
1068710723
return (!async_err) ? posix_error() : NULL;
1068810724

@@ -15525,6 +15561,14 @@ PROBE(probe_fdopendir, HAVE_FDOPENDIR_RUNTIME)
1552515561
PROBE(probe_mkdirat, HAVE_MKDIRAT_RUNTIME)
1552615562
#endif
1552715563

15564+
#ifdef HAVE_MKFIFOAT
15565+
PROBE(probe_mkfifoat, HAVE_MKFIFOAT_RUNTIME)
15566+
#endif
15567+
15568+
#ifdef HAVE_MKNODAT
15569+
PROBE(probe_mknodat, HAVE_MKNODAT_RUNTIME)
15570+
#endif
15571+
1552815572
#ifdef HAVE_RENAMEAT
1552915573
PROBE(probe_renameat, HAVE_RENAMEAT_RUNTIME)
1553015574
#endif
@@ -15658,11 +15702,11 @@ static const struct have_function {
1565815702
#endif
1565915703

1566015704
#ifdef HAVE_MKFIFOAT
15661-
{ "HAVE_MKFIFOAT", NULL },
15705+
{ "HAVE_MKFIFOAT", probe_mkfifoat },
1566215706
#endif
1566315707

1566415708
#ifdef HAVE_MKNODAT
15665-
{ "HAVE_MKNODAT", NULL },
15709+
{ "HAVE_MKNODAT", probe_mknodat },
1566615710
#endif
1566715711

1566815712
#ifdef HAVE_OPENAT

0 commit comments

Comments
 (0)