Skip to content

fix(runtime): make the local runtime install on x86_64 (proot seccomp fork fix) - #17

Merged
doublepi123 merged 3 commits into
mainfrom
fix/x86_64-proot-fork
Oct 18, 2026
Merged

doublepi123 merged 3 commits into
mainfrom
fix/x86_64-proot-fork

Conversation

@doublepi123

Copy link
Copy Markdown
Owner

Summary

On x86_64 Android (e.g. the emulator) the local runtime never installed: apk add failed with /bin/sh: can't fork: Function not implemented.

Root cause: Android's app seccomp policy only allows fork(2) on 32-bit ABIs (bionic SECCOMP_ALLOWLIST_APP.TXT: fork() lp32). musl calls SYS_fork directly on x86_64, and PRoot's SIGSYS handler answers unhandled syscalls with ENOSYS. arm64 has no fork(2), so it is unaffected.

  • Carry the PRoot fix proposed upstream as seccomp: run fork(2) as clone(2) when the outer policy rejects it termux/proot#415 (rewrites fork(2) into clone(SIGCHLD, 0, 0, 0, 0)) as a termux-packages recipe patch.
  • Release workflow: build the native payload from source first, and build the github APKs from it too (-Pandcode.sourceNativeLibs=true), so published APKs ship the patched proot. The release runtime-asset cache gets a separate key.
  • Source builds override the pinned termux-packages commit's proot recipe (5.1.107.94) with the mirrored 5.1.107.96 recipe, so proot is not downgraded. The pinned commit can't be bumped because the .96 bump moved to NDK r30.
  • Third-party notices / in-app legal docs now describe the source build and the proot modification (GPL-2.0 §2(a)).
  • native-source-libs.yml checks that both architectures produced proot_5.1.107.96 and that the patch was applied twice.

Local and CI dev builds keep the mirror path. Until termux/proot#415 lands and the lock is bumped, x86_64 works only with release/source-built APKs.

Verification

  • Android 15 x86_64 emulator (API 35 app, under the zygote seccomp filter), Alpine 3.24 rootfs:
    • unpatched proot (Termux 5.1.107.96 prebuilt and current upstream master): setup fails with can't fork: Function not implemented
    • patched proot (5.1.107.96 + patch, and master + patch, built with NDK r29): setup completes ("The runtime is ready", OpenCode 1.18.5), apk add of ~150 packages succeeds, opencode serve listens on 4098 and /global/health answers.
  • The patch applies cleanly to termux/proot v5.1.107.94 and v5.1.107.96.
  • LegalDisclosureComplianceTest 19/19 pass. bash -n and YAML parse are OK for the edited script and workflows.
  • The full termux source build of the native debs was not run locally. native-source-libs.yml covers it in CI.

Pre-PR review

判定: APPROVE

ブロッカー

なし

提案(非ブロッキング)

なし

チェック済み項目

  • ブランチ全体は 3 commit(09a331be、7e931f99、8e35031b)、10 ファイルです。worktree に未コミットの変更はありません。
  • runtime_tools/termux-packaging-recipes/proot.build.sh: origin/main との差分は 0 行で、verbatim の写しに戻っています。ヘッダの "Mirrored verbatim" とも矛盾しません。上書きコピーの説明は build_android_runtime_native_debs.sh:138-144 にだけ書かれています。
  • .github/workflows/native-source-libs.yml:37-50 を確認しました。
    • set -o pipefail と tee の組み合わせなので、ビルドスクリプトが失敗すればステップも失敗します。
    • バージョン確認: proot レシピには TERMUX_PKG_REVISION がないので、生成される deb の名前は proot_5.1.107.96_<arch>.deb になります。glob の対象はレシピにある 2 arch の分だけで、件数は 2 になります。上書きが効かなかった場合は .94 になり、件数が 0 になって失敗します。ls が失敗した場合も wc -l が 0 を返すので、test で落ちます。
    • パッチ確認: 固定 commit 2a342d4b の termux_step_patch_package.sh は、パッチごとに echo "Applying patch: $(basename $patch)" を標準出力に出します。そのため arch ごとに 1 回、合計 2 回ログに出るはずで、grep -c ... -eq 2 の想定と合います。
  • 前回までに確認した内容は変わっていません。proot を .96 に上書きしてパッチを当てる処理、ライセンス・サービス文書の同一性(index ハッシュが一致)と改変の告知、release.yml のソースビルドとキャッシュキー、パッチの中身と上流 seccomp: run fork(2) as clone(2) when the outer policy rejects it termux/proot#415 との一致です。
  • Kotlin、リソース、i18n キーの変更はないので、detekt/spotless と 8 ロケールの確認は対象外です。秘密情報もありません。

On x86_64 Android (e.g. the emulator) the local runtime never installed:
`apk add` failed with "/bin/sh: can't fork: Function not implemented".
Android's app seccomp policy only allows fork(2) on 32-bit ABIs, musl
calls SYS_fork directly on x86_64, and PRoot's SIGSYS handler answers
unknown syscalls with ENOSYS. arm64 has no fork(2) and is unaffected.

Carry the PRoot change proposed upstream (termux/proot#415) as a
termux-packages recipe patch that rewrites fork(2) into
clone(SIGCHLD, 0, 0, 0, 0), and build the github release APKs from the
source-built native payload too, so they ship the patched proot rather
than the Termux mirror prebuilt. The release runtime-asset cache gets
its own key so a mirror-derived payload is never restored there, and the
native source build job checks that the patch reached the proot source.

Verified on the Android 15 x86_64 emulator: with the patched proot the
app's setup installs the runtime and `opencode serve` comes up.
… patch

Source builds used the pinned termux-packages commit's proot recipe, 5.1.107.94, while the
lock pins 5.1.107.96, so building the github APKs from source would have downgraded proot.
Override the recipe with the mirrored 5.1.107.96 build.sh (the pinned commit can't be bumped:
the .96 bump moved to NDK r30). Update the third-party notices, which said release binaries
come from the mirror unmodified, to state the source build and the proot modification.
Keep proot.build.sh a verbatim mirror (the note now lives only in the build script).
@doublepi123
doublepi123 merged commit c9b2eed into main Oct 18, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant