From 8b77d9d1dbc027641f72784ef3e4067f1ea0f890 Mon Sep 17 00:00:00 2001 From: Md Arif <111168803+sabamdarif@users.noreply.github.com> Date: Sat, 4 Jul 2026 00:23:01 +0530 Subject: [PATCH 1/5] gh-152936: Make privileged functions available on Android --- Doc/library/os.rst | 20 +++++------ Doc/library/socket.rst | 2 +- Modules/posixmodule.c | 80 ++++++++++++++++++++++++++++++++++++++++++ Modules/socketmodule.c | 9 +++++ configure | 7 +--- configure.ac | 7 +--- 6 files changed, 102 insertions(+), 23 deletions(-) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 7b043f257ca0b5..535c9633401794 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -574,7 +574,7 @@ 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 @@ -610,21 +610,21 @@ process and user. Set the current process's effective group id. - .. availability:: Unix, not WASI, not Android. + .. availability:: Unix, not WASI. .. function:: seteuid(euid, /) Set the current process's effective user id. - .. availability:: Unix, not WASI, not Android. + .. availability:: Unix, not WASI. .. function:: setgid(gid, /) Set the current process' group id. - .. availability:: Unix, not WASI, not Android. + .. availability:: Unix, not WASI. .. function:: setgroups(groups, /) @@ -718,14 +718,14 @@ process and user. Set the current process's real and effective group ids. - .. availability:: Unix, not WASI, not Android. + .. availability:: Unix, not WASI. .. 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 @@ -734,7 +734,7 @@ process and user. 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 @@ -743,7 +743,7 @@ process and user. Set the current process's real and effective user ids. - .. availability:: Unix, not WASI, not Android. + .. availability:: Unix, not WASI. .. function:: getsid(pid, /) @@ -766,7 +766,7 @@ process and user. Set the current process's user id. - .. availability:: Unix, not WASI, not Android. + .. availability:: Unix, not WASI. .. placed in this section since it relates to errno.... a little weak @@ -2304,7 +2304,7 @@ 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`. diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 836aa91bb0885b..40c463e11b04c3 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1372,7 +1372,7 @@ 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 diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 57db175336702e..fd865ddacf42e2 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4400,6 +4400,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); @@ -9855,6 +9863,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 PyErr_SetFromErrno(PyExc_OSError); + } +#endif const char *username = PyBytes_AS_STRING(oname); if (initgroups(username, gid) == -1) @@ -10278,6 +10294,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; @@ -10299,6 +10323,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; @@ -10320,6 +10352,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; @@ -10342,6 +10382,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 { @@ -10366,6 +10414,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; @@ -10386,6 +10442,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; @@ -15390,6 +15454,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; @@ -15413,6 +15485,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 ada4fda6571049..872734235e8883 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -5947,6 +5947,15 @@ socket_sethostname(PyObject *self, PyObject *args) 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 01faef615a3d5e..0d087cf5450839 100755 --- a/configure +++ b/configure @@ -20005,14 +20005,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 a9fe5c269618fc..62009021646273 100644 --- a/configure.ac +++ b/configure.ac @@ -5381,14 +5381,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 From f7effcf98d156a051f8570a94d5c2d416d8b6e5f Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2026 06:54:58 +0000 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Library/2026-07-08-06-54-57.gh-issue-152936.TaFl0J.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-07-08-06-54-57.gh-issue-152936.TaFl0J.rst 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 00000000000000..2e481b99e1b1b1 --- /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. From dd73903ec567cdddecfd14d2c66fb03f8e37dbdd Mon Sep 17 00:00:00 2001 From: Md Arif <111168803+sabamdarif@users.noreply.github.com> Date: Wed, 8 Jul 2026 12:30:51 +0530 Subject: [PATCH 3/5] gh-152936: Use posix_error for Android checks --- .../2026-07-08-06-54-57.gh-issue-152936.TaFl0J.rst | 10 +++++----- Modules/posixmodule.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) 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 index 2e481b99e1b1b1..d8021b248977f1 100644 --- 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 @@ -1,6 +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 +: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 fd865ddacf42e2..70836520ae57ef 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4405,7 +4405,7 @@ os_chroot_impl(PyObject *module, path_t *path) // rather than returning a permission error. if (getuid() != 0) { errno = EPERM; - return path_error(path); + return posix_error(); } #endif int res; @@ -9868,7 +9868,7 @@ os_initgroups_impl(PyObject *module, PyObject *oname, gid_t gid) // rather than returning a permission error. if (getuid() != 0) { errno = EPERM; - return PyErr_SetFromErrno(PyExc_OSError); + return posix_error(); } #endif const char *username = PyBytes_AS_STRING(oname); From 27e854c6038129ef8b8e7e6198092ca8b696d7ea Mon Sep 17 00:00:00 2001 From: Md Arif <111168803+sabamdarif@users.noreply.github.com> Date: Wed, 8 Jul 2026 20:20:49 +0530 Subject: [PATCH 4/5] gh-152936: Document Android support in os and socket --- Doc/library/os.rst | 30 ++++++++++++++++++++++++++++++ Doc/library/socket.rst | 3 +++ Doc/whatsnew/3.16.rst | 17 +++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 535c9633401794..bb50caae1187f5 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -578,6 +578,9 @@ process and user. .. versionadded:: 3.2 + .. versionchanged:: 3.16 + Support for Android now exists. + .. function:: putenv(key, value, /) @@ -612,6 +615,9 @@ process and user. .. availability:: Unix, not WASI. + .. versionchanged:: 3.16 + Support for Android now exists. + .. function:: seteuid(euid, /) @@ -619,6 +625,9 @@ process and user. .. availability:: Unix, not WASI. + .. versionchanged:: 3.16 + Support for Android now exists. + .. function:: setgid(gid, /) @@ -626,6 +635,9 @@ process and user. .. availability:: Unix, not WASI. + .. versionchanged:: 3.16 + Support for Android now exists. + .. function:: setgroups(groups, /) @@ -720,6 +732,9 @@ process and user. .. availability:: Unix, not WASI. + .. versionchanged:: 3.16 + Support for Android now exists. + .. function:: setresgid(rgid, egid, sgid, /) @@ -729,6 +744,9 @@ process and user. .. versionadded:: 3.2 + .. versionchanged:: 3.16 + Support for Android now exists. + .. function:: setresuid(ruid, euid, suid, /) @@ -738,6 +756,9 @@ process and user. .. versionadded:: 3.2 + .. versionchanged:: 3.16 + Support for Android now exists. + .. function:: setreuid(ruid, euid, /) @@ -745,6 +766,9 @@ process and user. .. availability:: Unix, not WASI. + .. versionchanged:: 3.16 + Support for Android now exists. + .. function:: getsid(pid, /) @@ -768,6 +792,9 @@ process and user. .. availability:: Unix, not WASI. + .. versionchanged:: 3.16 + Support for Android now exists. + .. placed in this section since it relates to errno.... a little weak .. function:: strerror(code, /) @@ -2309,6 +2336,9 @@ features: .. 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 40c463e11b04c3..fb9249df4be33e 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1376,6 +1376,9 @@ The :mod:`!socket` module also offers various network-related services: .. 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 1a73a79a58b78b..d355adeb6471f0 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -269,6 +269,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`.) + re -- @@ -297,6 +305,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`.) + + tkinter ------- From 907676bfc06490e896e7a2a0ab7528ac34c5f691 Mon Sep 17 00:00:00 2001 From: Md Arif <111168803+sabamdarif@users.noreply.github.com> Date: Sat, 11 Jul 2026 15:48:33 +0530 Subject: [PATCH 5/5] gh-152936: Fix test_os and test_posix failures on Android --- Lib/test/test_os/test_os.py | 5 ++++- Lib/test/test_os/test_posix.py | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 5c258ff89ba735..a10e8be6a4f2c0 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -4517,7 +4517,10 @@ def test_oserror_filename(self): try: func(name, *func_args) except OSError as err: - self.assertIs(err.filename, name, str(func)) + if func is os.chroot and support.is_android and os.getuid() != 0: + self.assertIsNone(err.filename, str(func)) + else: + self.assertIs(err.filename, name, str(func)) except UnicodeDecodeError: pass else: diff --git a/Lib/test/test_os/test_posix.py b/Lib/test/test_os/test_posix.py index 8e83fa21dae6e2..eb5c32e3693f6e 100644 --- a/Lib/test/test_os/test_posix.py +++ b/Lib/test/test_os/test_posix.py @@ -99,6 +99,9 @@ def test_getresgid(self): @unittest.skipUnless(hasattr(posix, 'setresuid'), 'test needs posix.setresuid()') def test_setresuid(self): + 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. @@ -116,6 +119,9 @@ def test_setresuid_exception(self): @unittest.skipUnless(hasattr(posix, 'setresgid'), 'test needs posix.setresgid()') def test_setresgid(self): + 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.