gh-153711: Test availability for dup3() and pipe2() on iOS (#155222)

Modifies the configure test for dup3 and pipe2 to allow for iOS. These symbols
exist, but are unlinkable on iOS 26.

Co-authored-by: Russell Keith-Magee <russell@keith-magee.com>
Co-authored-by: Ned Deily <nad@python.org>
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index 32ab3ae..3f0b91a 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -1479,7 +1479,7 @@
    Return a pair of file descriptors ``(r, w)`` usable for reading and writing,
    respectively.
 
-   .. availability:: Unix, macOS >= 27.0, not WASI, not iOS.
+   .. availability:: Unix, macOS >= 27.0, iOS >= 27.0, not WASI.
 
    .. versionadded:: 3.3
 
diff --git a/Misc/NEWS.d/next/Library/2026-08-05-10-13-23.gh-issue-153711.9g9dVz.rst b/Misc/NEWS.d/next/Library/2026-08-05-10-13-23.gh-issue-153711.9g9dVz.rst
new file mode 100644
index 0000000..ca825ce
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-05-10-13-23.gh-issue-153711.9g9dVz.rst
@@ -0,0 +1,3 @@
+On iOS, add :func:`os.pipe2`, and use the syscalls :manpage:`pipe2 (2)` and
+:manpage:`dup3 (2)` internally, when available at both build-time and
+run-time.
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 8b226d7..a3f4449 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -513,8 +513,8 @@ static const unsigned int _Py_STATX_KNOWN = (STATX_BASIC_STATS | STATX_BTIME
 #  define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
 #  define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
 #  define HAVE_PTSNAME_R_RUNTIME __builtin_available(macOS 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.3, *)
-#  define HAVE_DUP3_RUNTIME __builtin_available(macOS 27.0, *)
-#  define HAVE_PIPE2_RUNTIME __builtin_available(macOS 27.0, *)
+#  define HAVE_DUP3_RUNTIME __builtin_available(macOS 27.0, iOS 27.0, tvOS 27.0, watchOS 27.0, visionOS 27.0, *)
+#  define HAVE_PIPE2_RUNTIME __builtin_available(macOS 27.0, iOS 27.0, tvOS 27.0, watchOS 27.0, visionOS 27.0, *)
 
 #  define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *)
 
diff --git a/configure b/configure
index 7a4c4ac..a628d33 100755
--- a/configure
+++ b/configure
@@ -4288,7 +4288,7 @@
 		aarch64-apple-ios*)           AR=arm64-apple-ios-ar ;;
 		x86_64-apple-ios*-simulator)  AR=x86_64-apple-ios-simulator-ar ;;
 		*)
-	esac
+    esac
 fi
 if test -z "$CC"; then
 	case "$host" in
@@ -21849,8 +21849,37 @@
 # found by configure), but either raise a compilation error (because the
 # header definition prevents usage - autoconf doesn't use the headers), or
 # raise an error if used at runtime. Force these symbols off.
-if test "$ac_sys_system" != "iOS" ; then
-  ac_fn_c_check_func "$LINENO" "dup3" "ac_cv_func_dup3"
+# In particular, dup3 and pipe2 are available as of iOS 27 but were
+# incorrectly detected in older simulator versions.
+case "$ac_sys_system" in
+    iOS)
+        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if iOS SDK is older than 27" >&5
+printf %s "checking if iOS SDK is older than 27... " >&6; }
+        ios_sdk_version=$(xcrun --sdk iphoneos --show-sdk-version 2>/dev/null | cut -d. -f1)
+        if test -n "$ios_sdk_version" && test "$ios_sdk_version" -ge 27; then
+            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+            ac_fn_c_check_func "$LINENO" "dup3" "ac_cv_func_dup3"
+if test "x$ac_cv_func_dup3" = xyes
+then :
+  printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "pipe2" "ac_cv_func_pipe2"
+if test "x$ac_cv_func_pipe2" = xyes
+then :
+  printf "%s\n" "#define HAVE_PIPE2 1" >>confdefs.h
+
+fi
+
+        else
+            { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+        fi
+        ;;
+    *)
+        # not iOS
+        ac_fn_c_check_func "$LINENO" "dup3" "ac_cv_func_dup3"
 if test "x$ac_cv_func_dup3" = xyes
 then :
   printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h
@@ -21881,7 +21910,8 @@
 
 fi
 
-fi
+        ;;
+esac
 
 ac_fn_check_decl "$LINENO" "dirfd" "ac_cv_have_decl_dirfd" "#include <sys/types.h>
                #include <dirent.h>
diff --git a/configure.ac b/configure.ac
index 6ac3c88..cf01e6f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -439,7 +439,7 @@
 		aarch64-apple-ios*)           AR=arm64-apple-ios-ar ;;
 		x86_64-apple-ios*-simulator)  AR=x86_64-apple-ios-simulator-ar ;;
 		*)
-	esac
+    esac
 fi
 if test -z "$CC"; then
 	case "$host" in
@@ -5605,9 +5605,24 @@
 # found by configure), but either raise a compilation error (because the
 # header definition prevents usage - autoconf doesn't use the headers), or
 # raise an error if used at runtime. Force these symbols off.
-if test "$ac_sys_system" != "iOS" ; then
-  AC_CHECK_FUNCS([dup3 getentropy getgroups pipe2 system])
-fi
+# In particular, dup3 and pipe2 are available as of iOS 27 but were
+# incorrectly detected in older simulator versions.
+case "$ac_sys_system" in
+    iOS)
+        AC_MSG_CHECKING([if iOS SDK is older than 27])
+        ios_sdk_version=$(xcrun --sdk iphoneos --show-sdk-version 2>/dev/null | cut -d. -f1)
+        if test -n "$ios_sdk_version" && test "$ios_sdk_version" -ge 27; then
+            AC_MSG_RESULT([no])
+            AC_CHECK_FUNCS([dup3 pipe2])
+        else
+            AC_MSG_RESULT([yes])
+        fi
+        ;;
+    *)
+        # not iOS
+        AC_CHECK_FUNCS([dup3 getentropy getgroups pipe2 system])
+        ;;
+esac
 
 AC_CHECK_DECL([dirfd],
               [AC_DEFINE([HAVE_DIRFD], [1],