https://github.com/python/cpython/commit/adea841addefe2161afa817c90470f66904149ee
commit: adea841addefe2161afa817c90470f66904149ee
branch: main
author: Md Arif <[email protected]>
committer: freakboy3742 <[email protected]>
date: 2026-09-08T15:09:19+08:00
summary:

gh-152936: Make privileged functions available on Android (#152977)

Expose the chroot, initgroups, setegid, seteuid, setgid, sethostname, setregid,
setresgid, setresuid, setreuid, and setuid functions on Android. Previously, 
these methods were excluded by an autoconf guard; they're now included with
a permission check to prevent issues invoking them as a non-root user.

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Malcolm Smith <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-07-08-06-54-57.gh-issue-152936.TaFl0J.rst
M Doc/library/os.rst
M Doc/library/socket.rst
M Doc/whatsnew/3.16.rst
M Lib/test/test_os/test_posix.py
M Modules/posixmodule.c
M Modules/socketmodule.c
M configure
M configure.ac

diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index 596597b1ab223ee..6e0e67d2a613c85 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -574,10 +574,13 @@ process and user.
    the groups of which the specified username is a member, plus the specified
    group id.
 
-   .. availability:: Unix, not WASI, not Android.
+   .. availability:: Unix, not WASI.
 
    .. versionadded:: 3.2
 
+   .. versionchanged:: 3.16
+      Support for Android now exists.
+
 
 .. function:: putenv(key, value, /)
 
@@ -610,21 +613,30 @@ process and user.
 
    Set the current process's effective group id.
 
-   .. availability:: Unix, not WASI, not Android.
+   .. availability:: Unix, not WASI.
+
+   .. versionchanged:: 3.16
+      Support for Android now exists.
 
 
 .. function:: seteuid(euid, /)
 
    Set the current process's effective user id.
 
-   .. availability:: Unix, not WASI, not Android.
+   .. availability:: Unix, not WASI.
+
+   .. versionchanged:: 3.16
+      Support for Android now exists.
 
 
 .. function:: setgid(gid, /)
 
    Set the current process' group id.
 
-   .. availability:: Unix, not WASI, not Android.
+   .. availability:: Unix, not WASI.
+
+   .. versionchanged:: 3.16
+      Support for Android now exists.
 
 
 .. function:: setgroups(groups, /)
@@ -718,32 +730,44 @@ process and user.
 
    Set the current process's real and effective group ids.
 
-   .. availability:: Unix, not WASI, not Android.
+   .. availability:: Unix, not WASI.
+
+   .. versionchanged:: 3.16
+      Support for Android now exists.
 
 
 .. function:: setresgid(rgid, egid, sgid, /)
 
    Set the current process's real, effective, and saved group ids.
 
-   .. availability:: Unix, not WASI, not Android, not macOS, not iOS.
+   .. availability:: Unix, not WASI, not macOS, not iOS.
 
    .. versionadded:: 3.2
 
+   .. versionchanged:: 3.16
+      Support for Android now exists.
+
 
 .. function:: setresuid(ruid, euid, suid, /)
 
    Set the current process's real, effective, and saved user ids.
 
-   .. availability:: Unix, not WASI, not Android, not macOS, not iOS.
+   .. availability:: Unix, not WASI, not macOS, not iOS.
 
    .. versionadded:: 3.2
 
+   .. versionchanged:: 3.16
+      Support for Android now exists.
+
 
 .. function:: setreuid(ruid, euid, /)
 
    Set the current process's real and effective user ids.
 
-   .. availability:: Unix, not WASI, not Android.
+   .. availability:: Unix, not WASI.
+
+   .. versionchanged:: 3.16
+      Support for Android now exists.
 
 
 .. function:: getsid(pid, /)
@@ -766,7 +790,10 @@ process and user.
 
    Set the current process's user id.
 
-   .. availability:: Unix, not WASI, not Android.
+   .. availability:: Unix, not WASI.
+
+   .. versionchanged:: 3.16
+      Support for Android now exists.
 
 
 .. placed in this section since it relates to errno.... a little weak
@@ -2324,11 +2351,14 @@ features:
 
    Change the root directory of the current process to *path*.
 
-   .. availability:: Unix, not WASI, not Android.
+   .. availability:: Unix, not WASI.
 
    .. versionchanged:: 3.6
       Accepts a :term:`path-like object`.
 
+   .. versionchanged:: 3.16
+      Support for Android now exists.
+
 
 .. function:: fchdir(fd)
 
diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst
index 836aa91bb0885b1..fb9249df4be33ef 100644
--- a/Doc/library/socket.rst
+++ b/Doc/library/socket.rst
@@ -1372,10 +1372,13 @@ The :mod:`!socket` module also offers various 
network-related services:
 
    .. audit-event:: socket.sethostname name socket.sethostname
 
-   .. availability:: Unix, not Android.
+   .. availability:: Unix.
 
    .. versionadded:: 3.3
 
+   .. versionchanged:: 3.16
+      Support for Android now exists.
+
 
 .. function:: if_nameindex()
 
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index 858dc3b8a878e79..243bd078d37c998 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -474,6 +474,14 @@ os
   process via a pidfd.  Available on Linux 5.6+.
   (Contributed by Maurycy Pawłowski-Wieroński in :gh:`149464`.)
 
+* The following functions are now available on Android:
+  :func:`os.chroot`, :func:`os.initgroups`, :func:`os.setegid`,
+  :func:`os.seteuid`, :func:`os.setgid`, :func:`os.setregid`,
+  :func:`os.setresgid`, :func:`os.setresuid`, :func:`os.setreuid`,
+  and :func:`os.setuid`. Calling them without sufficient privileges
+  now raises :exc:`PermissionError` instead of the functions being unavailable.
+  (Contributed by Md Arif in :gh:`152936`.)
+
 
 pydoc
 -----
@@ -513,6 +521,15 @@ shlex
   (Contributed by Jay Berry in :gh:`148846`.)
 
 
+socket
+------
+
+* The :func:`socket.sethostname` function is now available on Android.
+  Calling it without sufficient privileges now raises :exc:`PermissionError`
+  instead of the function being unavailable.
+  (Contributed by Md Arif in :gh:`152936`.)
+
+
 sqlite3
 -------
 
diff --git a/Lib/test/test_os/test_posix.py b/Lib/test/test_os/test_posix.py
index 814f945aac7453c..f13ad46aac45aa1 100644
--- a/Lib/test/test_os/test_posix.py
+++ b/Lib/test/test_os/test_posix.py
@@ -107,6 +107,10 @@ def test_getresgid(self):
     @unittest.skipUnless(hasattr(posix, 'setresuid'),
                          'test needs posix.setresuid()')
     def test_setresuid(self):
+        # Android blocks this function for non-root users regardless of the 
arguments.
+        if support.is_android and os.getuid() != 0:
+            self.assertRaises(PermissionError, posix.setresuid, -1, -1, -1)
+            return
         current_user_ids = posix.getresuid()
         self.assertIsNone(posix.setresuid(*current_user_ids))
         # -1 means don't change that value.
@@ -124,6 +128,10 @@ def test_setresuid_exception(self):
     @unittest.skipUnless(hasattr(posix, 'setresgid'),
                          'test needs posix.setresgid()')
     def test_setresgid(self):
+        # Android blocks this function for non-root users regardless of the 
arguments.
+        if support.is_android and os.getuid() != 0:
+            self.assertRaises(PermissionError, posix.setresgid, -1, -1, -1)
+            return
         current_group_ids = posix.getresgid()
         self.assertIsNone(posix.setresgid(*current_group_ids))
         # -1 means don't change that value.
diff --git 
a/Misc/NEWS.d/next/Library/2026-07-08-06-54-57.gh-issue-152936.TaFl0J.rst 
b/Misc/NEWS.d/next/Library/2026-07-08-06-54-57.gh-issue-152936.TaFl0J.rst
new file mode 100644
index 000000000000000..d8021b248977f1f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-08-06-54-57.gh-issue-152936.TaFl0J.rst
@@ -0,0 +1,6 @@
+:func:`os.chroot`, :func:`os.initgroups`, :func:`os.setegid`,
+:func:`os.seteuid`, :func:`os.setgid`, :func:`os.setregid`,
+:func:`os.setresgid`, :func:`os.setresuid`, :func:`os.setreuid`,
+:func:`os.setuid`, and :func:`socket.sethostname` are now available on
+Android. Calling them without sufficient privileges now raises
+:exc:`PermissionError` instead of the functions being unavailable.
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 847237142854da1..7636d51485c730d 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -4399,6 +4399,14 @@ static PyObject *
 os_chroot_impl(PyObject *module, path_t *path)
 /*[clinic end generated code: output=de80befc763a4475 input=14822965652c3dc3]*/
 {
+#ifdef __ANDROID__
+    // On Android, calling this function as a non-root user leads to a process 
crash
+    // rather than returning a permission error.
+    if (getuid() != 0) {
+        errno = EPERM;
+        return path_error(path);
+    }
+#endif
     int res;
     Py_BEGIN_ALLOW_THREADS
     res = chroot(path->narrow);
@@ -9875,6 +9883,14 @@ os_initgroups_impl(PyObject *module, PyObject *oname, 
gid_t gid)
 /*[clinic end generated code: output=59341244521a9e3f input=7e4514dff4526a95]*/
 #endif
 {
+#ifdef __ANDROID__
+    // On Android, calling this function as a non-root user leads to a process 
crash
+    // rather than returning a permission error.
+    if (getuid() != 0) {
+        errno = EPERM;
+        return posix_error();
+    }
+#endif
     const char *username = PyBytes_AS_STRING(oname);
 
     if (initgroups(username, gid) == -1)
@@ -10301,6 +10317,14 @@ static PyObject *
 os_setuid_impl(PyObject *module, uid_t uid)
 /*[clinic end generated code: output=a0a41fd0d1ec555f input=c921a3285aa22256]*/
 {
+#ifdef __ANDROID__
+    // On Android, calling this function as a non-root user leads to a process 
crash
+    // rather than returning a permission error.
+    if (getuid() != 0) {
+        errno = EPERM;
+        return posix_error();
+    }
+#endif
     if (setuid(uid) < 0)
         return posix_error();
     Py_RETURN_NONE;
@@ -10322,6 +10346,14 @@ static PyObject *
 os_seteuid_impl(PyObject *module, uid_t euid)
 /*[clinic end generated code: output=102e3ad98361519a input=ba93d927e4781aa9]*/
 {
+#ifdef __ANDROID__
+    // On Android, calling this function as a non-root user leads to a process 
crash
+    // rather than returning a permission error.
+    if (getuid() != 0) {
+        errno = EPERM;
+        return posix_error();
+    }
+#endif
     if (seteuid(euid) < 0)
         return posix_error();
     Py_RETURN_NONE;
@@ -10343,6 +10375,14 @@ static PyObject *
 os_setegid_impl(PyObject *module, gid_t egid)
 /*[clinic end generated code: output=4e4b825a6a10258d input=4080526d0ccd6ce3]*/
 {
+#ifdef __ANDROID__
+    // On Android, calling this function as a non-root user leads to a process 
crash
+    // rather than returning a permission error.
+    if (getuid() != 0) {
+        errno = EPERM;
+        return posix_error();
+    }
+#endif
     if (setegid(egid) < 0)
         return posix_error();
     Py_RETURN_NONE;
@@ -10365,6 +10405,14 @@ static PyObject *
 os_setreuid_impl(PyObject *module, uid_t ruid, uid_t euid)
 /*[clinic end generated code: output=62d991210006530a input=0ca8978de663880c]*/
 {
+#ifdef __ANDROID__
+    // On Android, calling this function as a non-root user leads to a process 
crash
+    // rather than returning a permission error.
+    if (getuid() != 0) {
+        errno = EPERM;
+        return posix_error();
+    }
+#endif
     if (setreuid(ruid, euid) < 0) {
         return posix_error();
     } else {
@@ -10389,6 +10437,14 @@ static PyObject *
 os_setregid_impl(PyObject *module, gid_t rgid, gid_t egid)
 /*[clinic end generated code: output=aa803835cf5342f3 input=c59499f72846db78]*/
 {
+#ifdef __ANDROID__
+    // On Android, calling this function as a non-root user leads to a process 
crash
+    // rather than returning a permission error.
+    if (getuid() != 0) {
+        errno = EPERM;
+        return posix_error();
+    }
+#endif
     if (setregid(rgid, egid) < 0)
         return posix_error();
     Py_RETURN_NONE;
@@ -10409,6 +10465,14 @@ static PyObject *
 os_setgid_impl(PyObject *module, gid_t gid)
 /*[clinic end generated code: output=bdccd7403f6ad8c3 input=27d30c4059045dc6]*/
 {
+#ifdef __ANDROID__
+    // On Android, calling this function as a non-root user leads to a process 
crash
+    // rather than returning a permission error.
+    if (getuid() != 0) {
+        errno = EPERM;
+        return posix_error();
+    }
+#endif
     if (setgid(gid) < 0)
         return posix_error();
     Py_RETURN_NONE;
@@ -15506,6 +15570,14 @@ static PyObject *
 os_setresuid_impl(PyObject *module, uid_t ruid, uid_t euid, uid_t suid)
 /*[clinic end generated code: output=834a641e15373e97 input=9e33cb79a82792f3]*/
 {
+#ifdef __ANDROID__
+    // On Android, calling this function as a non-root user leads to a process 
crash
+    // rather than returning a permission error.
+    if (getuid() != 0) {
+        errno = EPERM;
+        return posix_error();
+    }
+#endif
     if (setresuid(ruid, euid, suid) < 0)
         return posix_error();
     Py_RETURN_NONE;
@@ -15529,6 +15601,14 @@ static PyObject *
 os_setresgid_impl(PyObject *module, gid_t rgid, gid_t egid, gid_t sgid)
 /*[clinic end generated code: output=6aa402f3d2e514a9 input=33e9e0785ef426b1]*/
 {
+#ifdef __ANDROID__
+    // On Android, calling this function as a non-root user leads to a process 
crash
+    // rather than returning a permission error.
+    if (getuid() != 0) {
+        errno = EPERM;
+        return posix_error();
+    }
+#endif
     if (setresgid(rgid, egid, sgid) < 0)
         return posix_error();
     Py_RETURN_NONE;
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index 82899572f80255a..d189c6d478836c4 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -5988,6 +5988,15 @@ _socket_sethostname(PyObject *module, PyObject *hnobj)
     Py_buffer buf;
     int res, flag = 0;
 
+#ifdef __ANDROID__
+    // On Android, calling this function as a non-root user leads to a process 
crash
+    // rather than returning a permission error.
+    if (getuid() != 0) {
+        errno = EPERM;
+        return set_error();
+    }
+#endif
+
 #if defined(_AIX) || (defined(__sun) && defined(__SVR4) && Py_SUNOS_VERSION <= 
510)
 /* issue #18259, sethostname is not declared in any useful header file on AIX
  * the same is true for Solaris 10 */
diff --git a/configure b/configure
index 7e4844263a3491c..423e3724cfc9131 100755
--- a/configure
+++ b/configure
@@ -20366,14 +20366,9 @@ printf "%s\n" "$MACHDEP_OBJS" >&6; }
 fi
 
 if test "$ac_sys_system" = "Linux-android"; then
-  # When these functions are used in an unprivileged process, they crash rather
-  # than returning an error.
-  blocked_funcs="chroot initgroups setegid seteuid setgid sethostname
-    setregid setresgid setresuid setreuid setuid"
-
   # These functions are unimplemented and always return an error
   # 
(https://android.googlesource.com/platform/system/sepolicy/+/refs/heads/android13-release/public/domain.te#1044)
-  blocked_funcs="$blocked_funcs sem_open sem_unlink"
+  blocked_funcs="sem_open sem_unlink"
 
   # Before API level 23, when fchmodat is called with the unimplemented flag
   # AT_SYMLINK_NOFOLLOW, instead of returning ENOTSUP as it should, it actually
diff --git a/configure.ac b/configure.ac
index 8345c97349b7382..d754522bb029be3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5494,14 +5494,9 @@ else
 fi
 
 if test "$ac_sys_system" = "Linux-android"; then
-  # When these functions are used in an unprivileged process, they crash rather
-  # than returning an error.
-  blocked_funcs="chroot initgroups setegid seteuid setgid sethostname
-    setregid setresgid setresuid setreuid setuid"
-
   # These functions are unimplemented and always return an error
   # 
(https://android.googlesource.com/platform/system/sepolicy/+/refs/heads/android13-release/public/domain.te#1044)
-  blocked_funcs="$blocked_funcs sem_open sem_unlink"
+  blocked_funcs="sem_open sem_unlink"
 
   # Before API level 23, when fchmodat is called with the unimplemented flag
   # AT_SYMLINK_NOFOLLOW, instead of returning ENOTSUP as it should, it actually

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to