From 230bb1aad47ab52d3209b2a280c47005df74ecd3 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Fri, 12 Jun 2026 23:44:03 +0200 Subject: [PATCH] fix(mobile): _pyrepl pruning + mimalloc seccomp open() on 3.13/3.14 Two distinct on-device runtime bugs that only surface on a genuine 3.13/3.14 mobile run (3.12 unaffected), found via mobile-forge genuine multi-Python mobile tests (fork run 27439192170): 1. _pyrepl was pruned from the iOS + Android stdlib excludes as a "dev-only / interactive-REPL" module. But CPython 3.14 pdb.py imports _pyrepl at module load, so any on-device code importing pdb (incl. pytest) dies with "ModuleNotFoundError: No module named _pyrepl". 3.13 pdb does not import it, which is why 3.13 iOS was unaffected. Un-prune _pyrepl in darwin/python-darwin-stdlib.exclude + android/python-android-dart.exclude. 2. mimalloc (bundled in CPython since 3.13) issues a bare open(2) syscall from its load-time constructor (mi_process_load -> unix_detect_overcommit reading /proc/sys/vm/overcommit_memory) where SYS_open is defined (x86_64). Android bionic seccomp forbids open(2) -> SIGSYS at dlopen of libpython, before the interpreter starts. arm64 is unaffected (no SYS_open; mimalloc falls back to libc open() -> openat). Patch mi_prim_open to use SYS_openat (android/patches/mimalloc_seccomp_openat.patch, gated >=3.13 in build.sh). --- android/build.sh | 6 +++++ android/patches/mimalloc_seccomp_openat.patch | 22 +++++++++++++++++++ android/python-android-dart.exclude | 4 ++-- darwin/python-darwin-stdlib.exclude | 5 +++-- 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 android/patches/mimalloc_seccomp_openat.patch diff --git a/android/build.sh b/android/build.sh index b1870f7..981794e 100755 --- a/android/build.sh +++ b/android/build.sh @@ -57,6 +57,12 @@ if [ $version_int -eq 312 ]; then # Android tooling; this 3.12-only patch matches that behavior at link time. patches+=" soname_linktime bldlibrary grp" fi +if [ $version_int -ge 313 ]; then + # mimalloc (bundled since 3.13) issues a bare SYS_open at load on x86_64, + # which Android seccomp kills (SIGSYS) at dlopen of libpython. Switch it to + # SYS_openat. See patches/mimalloc_seccomp_openat.patch. + patches+=" mimalloc_seccomp_openat" +fi for name in $patches; do patch_file="$script_dir/patches/$name.patch" echo "$patch_file" diff --git a/android/patches/mimalloc_seccomp_openat.patch b/android/patches/mimalloc_seccomp_openat.patch new file mode 100644 index 0000000..b427cdf --- /dev/null +++ b/android/patches/mimalloc_seccomp_openat.patch @@ -0,0 +1,22 @@ +mimalloc: use SYS_openat instead of SYS_open (Android seccomp). + +CPython bundles mimalloc since 3.13. On Android, mimalloc's load-time constructor +(mi_process_load -> unix_detect_overcommit, Objects/mimalloc/init.c) reads +/proc/sys/vm/overcommit_memory through mi_prim_open. On platforms where SYS_open +is defined (x86_64) mi_prim_open issues the bare open(2) syscall directly (to +avoid recursion while mimalloc is still initializing). Android's bionic seccomp +policy forbids open(2) -- only openat(2) is permitted -- so the kernel raises +SIGSYS (SYS_SECCOMP) and the process is killed at dlopen() of libpython, before +the interpreter starts. 3.12 has no mimalloc; arm64 is unaffected (it has no +SYS_open, so mimalloc falls back to libc open(), which bionic routes through +openat). SYS_openat(AT_FDCWD, ...) is semantically identical and allowed. + +--- a/Objects/mimalloc/prim/unix/prim.c ++++ b/Objects/mimalloc/prim/unix/prim.c +@@ -67,5 +67,5 @@ + static int mi_prim_open(const char* fpath, int open_flags) { +- return syscall(SYS_open,fpath,open_flags,0); ++ return syscall(SYS_openat,AT_FDCWD,fpath,open_flags,0); + } + static ssize_t mi_prim_read(int fd, void* buf, size_t bufsize) { + return syscall(SYS_read,fd,buf,bufsize); diff --git a/android/python-android-dart.exclude b/android/python-android-dart.exclude index cee3d2d..2d95dbd 100644 --- a/android/python-android-dart.exclude +++ b/android/python-android-dart.exclude @@ -20,8 +20,8 @@ lib/python*/tkinter lib/python*/turtle* lib/python*/wsgiref # Interactive-REPL / dev-only modules + easter eggs / frozen demos (never imported when -# running a script in embedded mode). -lib/python*/_pyrepl +# running a script in embedded mode). _pyrepl is intentionally NOT pruned: CPython 3.14's +# pdb imports it at module load, so dropping it breaks pdb / pytest / anything that imports it. lib/python*/rlcompleter* lib/python*/tabnanny* lib/python*/antigravity* diff --git a/darwin/python-darwin-stdlib.exclude b/darwin/python-darwin-stdlib.exclude index 64725c9..03fb233 100644 --- a/darwin/python-darwin-stdlib.exclude +++ b/darwin/python-darwin-stdlib.exclude @@ -19,8 +19,9 @@ tkinter turtle* wsgiref # Interactive-REPL / dev-only modules — never imported when running a script (embedded -# mode); plus easter eggs and frozen demo modules. -_pyrepl +# mode); plus easter eggs and frozen demo modules. _pyrepl is intentionally NOT pruned: +# CPython 3.14's pdb imports it at module load, so dropping it breaks pdb / pytest / +# anything that imports it on-device. rlcompleter* tabnanny* antigravity*