Skip to content

Fixed(system-linker-exec): Exempt common alternative entrypoint process contexts from system linker exec - #39

Open
robertkirkman wants to merge 7 commits into
termux:masterfrom
robertkirkman:alternative-entrypoint
Open

Fixed(system-linker-exec): Exempt common alternative entrypoint process contexts from system linker exec#39
robertkirkman wants to merge 7 commits into
termux:masterfrom
robertkirkman:alternative-entrypoint

Conversation

@robertkirkman

Copy link
Copy Markdown
Member

@robertkirkman

Copy link
Copy Markdown
Member Author

Will link to use case/bug reproduction walkthrough soon, forming webring

@robertkirkman robertkirkman changed the title Fixed(system-linker-exec): Exclude common alternative entrypoint process contexts from system linker exec Fixed(system-linker-exec): Exempt common alternative entrypoint process contexts from system linker exec Aug 1, 2026
@robertkirkman
robertkirkman force-pushed the alternative-entrypoint branch from 824da62 to 4f04e2e Compare August 1, 2026 06:25
@robertkirkman

Copy link
Copy Markdown
Member Author

@agnostic-apollo

Copy link
Copy Markdown
Member

Note that documentation changes under site/ should be done in a separate commit, so that commit link of the "code" changes can be added to the Commits: list of TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE variable docs. The pull must also be merged locally and pushed to master so that the commit hash of code changes do no change, as github UI will change that.

@robertkirkman
robertkirkman force-pushed the alternative-entrypoint branch from 4f04e2e to b9e58f3 Compare August 1, 2026 09:49
@robertkirkman

robertkirkman commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

The pull must also be merged locally and pushed to master so that the commit hash of code changes do no change, as github UI will change that.

I'm not familiar with how to achieve that correctly, is there a guide for that in the context of this repository? Or do you mean that you will do that on your end?

@robertkirkman
robertkirkman force-pushed the alternative-entrypoint branch from b9e58f3 to 366eef9 Compare August 1, 2026 10:16
@agnostic-apollo
agnostic-apollo force-pushed the alternative-entrypoint branch 2 times, most recently from 44d0446 to 71ab6d1 Compare August 1, 2026 10:19
@agnostic-apollo

agnostic-apollo commented Aug 1, 2026

Copy link
Copy Markdown
Member

I have made some small changes, test them if you can before I merge.

Normally merging locally can be done like this. I have already added the commit link to the docs, will have to update it if changes are made again. Replace pull-branch will branch name of pull. I can do the merge myself. Will have to add release changelog files for both repos before releases are made. termux-core must also be updated in termux-packages before termux-exec is attempted to be built.

git checkout -b pull-branch master
git pull https://github.com/termux/termux-exec-package.git pull-branch
git checkout master
git merge --ff-only pull-branch
git push origin master

@robertkirkman

robertkirkman commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

Unfortunately, when you force-pushed the branch, you erased my most recent commit because you had checked out a commit before I uploaded a commit and then force-pushed without rebasing your version, but I only assume this because the most recent commit I had made was related to one of the changes you had asked me to make:

image

If you want this change to be preserved, then you might need to reapply it.

git push origin master

Ok I see, the git push origin master will have to be done by you, so it is better for you to use those commands.

termux-core must also be updated in termux-packages before termux-exec is attempted to be built.

Not sure if you prefer/need a different convention for termux-core and termux-exec from most packages, but I was thinking they would just be bumped together in the same PR, right, like how they were in this?

@robertkirkman

Copy link
Copy Markdown
Member Author

I have tested the current versions of both PRs with Magisk (stringEquals()) and run-as (stringStartsWith()) and they are working.

@agnostic-apollo

Copy link
Copy Markdown
Member

Ah, sorry. Will fix the commas in docs.

Yeah, I can merge, but I have to currently go out, so will do it when I come back.

Same termux-packages pull will work too due to dependency order. I meant if commits are pushed directly to master in wrong order and action builds.

@agnostic-apollo

Copy link
Copy Markdown
Member

Thanks for testing.

@agnostic-apollo
agnostic-apollo force-pushed the alternative-entrypoint branch from 71ab6d1 to 212b475 Compare August 2, 2026 19:45
@agnostic-apollo

Copy link
Copy Markdown
Member

Found an issue in termux_exec__ld_preload_lib__setup__run_command(), check docs added there, need to think more on whether solution is ideal before this can be merged. I haven't tested the code either, will require testing termux-exec-ld-preload-lib/termux-exec-system-linker-exec as root, su <termux_uid> and run-as.

@robertkirkman

robertkirkman commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

Found an issue in termux_exec__ld_preload_lib__setup__run_command(), check docs added there, need to think more on whether solution is ideal before this can be merged. I haven't tested the code either, will require testing termux-exec-ld-preload-lib/termux-exec-system-linker-exec as root, su <termux_uid> and run-as.

So, the edge case you found could be described as "using an alternative entrypoint that has an unrestricted process context to upgrade or reinstall a build of termux-exec from this codebase inside a targetSdkVersion 29+ Termux will reset the symbolic link libtermux-exec.so from libtermux-exec-linker-ld-preload.so to libtermux-exec-direct-ld-preload.so, and then something would not work anymore in shells started from the targetSdkVersion 29+ Termux app's normal entrypoint until termux-exec is upgraded or reinstalled again from there (if shell and failsafe shell are not too broken by that to permit it)", right?

Edit: that did not happen as assumed; some results from testing are posted below.

@robertkirkman

robertkirkman commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Well, currently, the Google Play Termux vanilla version (unmodified from what Google Play Termux organization released, meaning it uses their termux-exec) is not actually (at least visibly) affected by this problem, with any process contexts relevant to it, including the ADB run-as, KernelSU, and the default one,

image

so at least as far as symptoms have been recorded at this point, the issue is specific to F-Droid Termux.

I tried to find any patches specific to this, like a patch containing TERMUX_EXEC__PROC_SELF_EXE, in Google Play Termux's neovim package, but I wasn't able to.

I have not yet attempted to install this repository github.com/termux/termux-exec-package into Google Play Termux to see what happens, or this PR, but after this somewhat unexpected result in Google Play Termux, I most likely will so that the results can be recorded.

@robertkirkman

robertkirkman commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

I can see that the github.com/termux-play-store/termux-exec has had some recent updates, and maybe some of these updates affect the result. It appears, I am guessing, that Google Play Termux's termux-exec remains in system-linker-exec mode at all times, but that its system-linker-exec may be different or updated compared to the system-linker-exec of F-Droid Termux termux-exec and maybe it's able to make nvim detect its executable path when F-Droid Termux's termux-exec does not;

image

I think these new details support these conclusions:

  • While using Google Play Termux, if it can somehow be detected from ADB entrypoints, directly or indirectly, that the current targetSdkVersion of the Termux is higher than 28, I wouldn't mind it having system linker exec always enabled whenever termux-exec is enabled in LD_PRELOAD, especially if it works fine with nvim, since Google Play Termux is supposed to be that way - though technically, even with Google Play Termux, ideally system-linker-exec should probably not be necessary in ADB shell entrypoints.
  • On the other hand, it doesn't seem appropriate for system-linker-exec to be getting enabled somehow by default when using ADB shell entrypoints with F-Droid Termux, since F-Droid Termux has targetSdkVersion 28, it doesn't need system-linker-exec, so the issue definitely remains a bug in F-Droid Termux as long as it's reproducible there.

@robertkirkman

robertkirkman commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

Google Play Termux's ADB shell entrypoints from run-as and KernelSU are setting TERMUX_EXEC__PROC_SELF_EXE in their environments when termux-exec is enabled in LD_PRELOAD (by just setting it, then running bash to activate it manually, since Google Play Termux, unlike F-Droid Termux, doesn't enable termux-exec using its login command, and rather sets it from Java code elsewhere), which is a variable that F-Droid Termux's termux-exec doesn't set unless it's in system-linker-exec mode, so it's safe to assume that Google Play Termux's termux-exec is also in system-linker-exec mode in ADB shell entrypoints when it doesn't need to be. What's remaining is to test what happens if I attempt to install F-Droid Termux termux-exec into Google Play Termux, since it's the only available maintained targetSdkVersion 29+ Termux at the moment, to try to check this PR's effects there.

@robertkirkman

robertkirkman commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

Google Play Termux termux-exec test results

Table: Does termux-exec enable system linker exec by default when launched within Google Play Termux?
Android Runtime entrypoints compared with Android Debug Bridge entrypoints:

type of termux-exec ART ADB
fornwall's Yes Yes
agnostic-apollo's Yes Yes
robertkirkman's original PR Yes No
robertkirkman's PR with changes by agnostic-apollo Yes No

Test done to check for the enablement of system linker exec in the current shell: env | grep TERMUX_EXEC__PROC_SELF_EXE (the test with nvim behaves differently between agnostic-apollo termux-exec system linker exec and fornwall termux-exec system linker exec)

When the postinst script of agnostic-apollo's termux-exec is run in Google Play Termux, no matter whether it is run from an ART entrypoint or an ADB entrypoint and no matter which version of agnostic-apollo's termux-exec is used, this prints every time:

termux-exec.postinst: Start
termux-exec.postinst: Setting primary Termux '$LD_PRELOAD' library
termux-exec.postinst: android_build_version_sdk: '37'
termux-exec: Setting primary Termux '$LD_PRELOAD' library in 'libtermux-exec-ld-preload.so' to '/data/data/com.termux/files/usr/lib/libtermux-exec-direct-ld-preload.so'
termux-exec.postinst: End

There's only one exception, which is with robertkirkman's PR with changes by agnostic-apollo. There, this message prints when the postinst script is run from an ART entrypoint:

termux-exec.postinst: Start
termux-exec.postinst: Setting primary Termux '$LD_PRELOAD' library
termux-exec.postinst: android_build_version_sdk: '37'
termux-exec: se_process_context_from_file: 'u:r:untrusted_app_34:s0:c162,c256,c512,c768'
termux-exec: Setting primary Termux '$LD_PRELOAD' library in 'libtermux-exec-ld-preload.so' to '/data/data/com.termux/files/usr/lib/libtermux-exec-direct-ld-preload.so'
termux-exec.postinst: End

but when the same postinst script is run from an ADB entrypoint instead, this prints:

termux-exec.postinst: Start
termux-exec.postinst: Setting primary Termux '$LD_PRELOAD' library
termux-exec.postinst: android_build_version_sdk: '37'
termux-exec: se_process_context_from_file: 'u:r:ksu:s0'
termux-exec: Cannot set primary Termux '$LD_PRELOAD' library as su/shell user was used to drop privileges/capabilities and uid to an unprivileged user '10162', but the process context 'u:r:ksu:s0' was not switched to Termux app's normal context
termux-exec: If updating 'termux-exec' package, then do it from a normal shell inside Termux app instead of from inside root or adb/run-as shells

The error message itself seems to be working as expected, but at least during this test, it doesn't appear that what the postinst script currently does involving the "Primary Termux '$LD_PRELOAD' library" is changing based on the different process contexts of the ART and ADB entrypoint types, at least when run from Google Play Termux. Is that expected, or should it actually change, and is something else going on that needs to be fixed?

@robertkirkman

Copy link
Copy Markdown
Member Author

I should probably write that even though I referred to you and me in the 3rd person in my last message, I don't use LLMs to do any writing of code or messages. I only wrote it that way intentionally because if I kept saying "your" and "my" in every other sentence, I think that would make the test results a lot harder to read.

@robertkirkman

robertkirkman commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

As far as I've ever seen, termux-exec-ld-preload-lib setup -v always prints that, termux-exec: Setting primary Termux '$LD_PRELOAD' library in 'libtermux-exec-ld-preload.so' to '/data/data/com.termux/files/usr/lib/libtermux-exec-direct-ld-preload.so' on F-Droid Termux as well, so I guess what I was expecting was for something different to happen when this (agnostic-apollo's termux-exec) is built, installed, and then run in Google Play Termux.

Explained in terms of your comment in the code, I have not yet observed this library being activated in any situation, regardless of the type of termux-exec package used, even in situations where I was expecting that it was probably supposed to activate:

image

@agnostic-apollo
agnostic-apollo force-pushed the alternative-entrypoint branch from 212b475 to 6bdf188 Compare August 5, 2026 11:04
@agnostic-apollo

Copy link
Copy Markdown
Member

The termux org termux-exec has 2 library variants (libtermux-exec-direct-ld-preload.so/libtermux-exec-linker-ld-preload.so) that are dynamically set by postinst script during apt update and bootstrap second stage by termux-exec-ld-preload-lib depending on if system linker exec should be enabled.

Both libraries support system linker exec and auto enables it if required based on android sdk/process context or if TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force is set. They currently do not have any differences. However, the plan is to add additional syscall hooks in the linker variant, like for realpath/readlink so that /proc/self/exe and /proc/$$/exe paths can be replaced with path in TERMUX_EXEC__PROC_SELF_EXE before syscall is executed. The TERMUX_EXEC__PROC_SELF_EXE variable is exported by libtermux-exec.so whenever it hooks a process execve call and system linker exec is used, it is not exported by the Termux app.

Additionally there are plans to add 2 more library variants direct-dynamic and linker-dynamic that will hook syscalls for dynamic variables, basically to repalce hardcoded build time /data/data/com.termux paths with actual app data directory path of the Termux app at runtime.

The playstore termux-exec already has readlink/realpath hooks to replace paths passed to them with path in TERMUX_EXEC__PROC_SELF_EXE before syscall is executed.

The playstore termux-exec always enables system linker exec as long as executable starts with TERMUX_BASE_DIR or equals /system/bin/sh, unless TERMUX_EXEC_OPTOUT is set as optout. It does not check any android sdk version or process context to "auto enable" like termux org termux-exec. The reason you saw the actual path for nvim in google play in those pictures would be because readlink/realpath are hooked to replace /proc/self/exe by the playstore termux-exec itself, package specific patches are not required.

Now back to what I originally meant in #39 (comment). The termux-exec-ld-preload-lib runs termux-exec-system-linker-exec should-enable to see if system linker is supposed to be enabled so that it sets libtermux-exec-direct-ld-preload.so or libtermux-exec-linker-ld-preload.so as primary library accordingly. Now suppose F-Droid/GitHub builds use targetSdkVersion > 28 and linker exec is required for them and app runs with untrusted_app context instead of untrusted_app_25/untrusted_app_27. If postinst script via bootstrap second stage or apt was running in normal Termux owned shell ran termux-exec-system-linker-exec should-enable, it would correctly return true as app_data_file_exec_exempted would not be set to true because the case statements wouldn't match if context is untrusted_app and libtermux-exec-linker-ld-preload.so would correctly be set as primary library instead of direct.

But now in this pull, you have added "u:r:runas_app:"*|"u:r:su:s0"|"u:r:ksu:s0"|"u:r:magisk:s0"|"u:r:shell:s0" to the case statement. That means that if the user were to run apt install termux-exec from run-as or su <termux_uid>, termux-exec-system-linker-exec should-enable would match the case statement and set app_data_file_exec_exempted to true, and then return false to termux-exec-ld-preload-lib, which would then set libtermux-exec-direct-ld-preload.so as primary library instead of linker. This would result in future additional readlink/realpath and other hooks to no longer be available in the Termux environment and would break things or cause problems that didn't exist before while linker variant was primary. Hence I added changes in termux-exec-ld-preload-lib so that setting primary library is not allowed for run-as/su contexts.

Hopefully that clears things up, let me know if I haven't answered something in your comments or if you have any new question.

I still haven't tested myself cause of time issues, thanks for testing that my error in termux-exec-ld-preload-lib works.

I have also replaced readlink with stat -c %N in termux-exec-system-linker-exec is-enabled so that future readlink hooks do not return unwanted path in TERMUX_EXEC__PROC_SELF_EXE and instead the actual symlink content.

@robertkirkman

robertkirkman commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

Hopefully that clears things up, let me know if I haven't answered something in your comments or if you have any new question.

Yes, it appears as though maybe you did not quite get to yet, or address the results I recorded where this did not happen:

If postinst script via bootstrap second stage or apt was running in normal Termux owned shell ran termux-exec-system-linker-exec should-enable, it would correctly return true as app_data_file_exec_exempted would not be set to true because the case statements wouldn't match if context is untrusted_app and libtermux-exec-linker-ld-preload.so would correctly be set as primary library instead of direct.

For me, this did not happen. Some more details of what did happen for me are posted above. Maybe I have set up the test in a strange way that caused a side effect somewhere, and your test later can reveal what went wrong.

agnostic-apollo and others added 7 commits August 6, 2026 04:27
…abled()` to `shouldEnableSystemLinkerExec()` in `TermuxExecLDPreload` and move `is-enabled` command to `should-enable` in `termux-exec-system-linker-exec` and add a new implementation for `is-enabled`

Also fixes reading of `TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE` variable without scoped `TERMUX_ENV__S_TERMUX_EXEC` prefix.
…ss contexts from system linker exec

Co-authored-by: @robertkirkman <rkirkman@termux.dev>
Co-authored-by: @agnostic-apollo <agnosticapollo@gmail.com>

Related pull termux/termux-core-package#2

Closes termux#40
…nt process contexts from system linker exec

Related commit termux@89422f43
…for commands like `termux-exec-system-linker-exec` which print the result to stdout and any non-command-output should be logged to stderr

The termux-app bootstrap second stage "client" has already been fixed to not abort if stderr is set.

- termux/termux-app@97f2537c
…EC__SYSTEM_LINKER_EXEC__MODE` env variable which does not check effective user id and add check in `force` mode to not use system linker exec if effective user id equals root (`0`) and shell (`2000`) user, as exec restrictions do not apply for them and would result in performance hit unnecessarily
…__SYSTEM_LINKER_EXEC__MODE` env variable

Related commit termux@f7450d01
@agnostic-apollo
agnostic-apollo force-pushed the alternative-entrypoint branch from 6bdf188 to 4c72fe4 Compare August 6, 2026 00:14
@agnostic-apollo

Copy link
Copy Markdown
Member

Thanks for the catch. Yes, direct variant always being set was not correct behaviour. It got broke in 8793fc7 as I had forgotten that termux-exec-system-linker-exec should-enable is supposed to output true/false to stdout, but after 8793fc7, log entries were also being logged to stdout, which broke the [ "$system_linker_exec_should_enable" != "true" ] check in termux-exec-ld-preload-lib. Fixed in 5d13876.


Following are the tests done and everything is working as expected now. I have add force_all mode as well in f7450d0.

### Normal Termux shell targetSdkVersion `= 28`

$ TERMUX_EXEC__LD_PRELOAD_LIB__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-ld-preload-lib setup
termux-exec.ld_preload_lib: se_process_context_from_file: 'u:r:untrusted_app_27:s0:c212,c256,c512,c768'
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'enable'
termux-exec.system_linker_exec: se_process_context_from_file: 'u:r:untrusted_app_27:s0:c212,c256,c512,c768'
termux-exec.system_linker_exec: app_data_file_exec_exempted: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'false'
termux-exec.ld_preload_lib: Setting primary Termux '$LD_PRELOAD' library in 'libtermux-exec-ld-preload.so' to '/data/data/com.termux/files/usr/lib/libtermux-exec-direct-ld-preload.so'


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/data/data/com.termux/files/usr/bin/coreutils'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'false'
false

$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'enable'
termux-exec.system_linker_exec: se_process_context_from_file: 'u:r:untrusted_app_27:s0:c212,c256,c512,c768'
termux-exec.system_linker_exec: app_data_file_exec_exempted: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'false'
false


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/apex/com.android.runtime/bin/linker64'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'true'
true

$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'force'
termux-exec.system_linker_exec: system_linker_exec_available: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'true'
true


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force_all TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/apex/com.android.runtime/bin/linker64'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'true'
true
$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force_all TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'force_all'
termux-exec.system_linker_exec: system_linker_exec_available: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'true'
true



### Normal Termux shell targetSdkVersion `> 28`

$ TERMUX_EXEC__LD_PRELOAD_LIB__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-ld-preload-lib setup
termux-exec.ld_preload_lib: se_process_context_from_file: 'u:r:untrusted_app:s0:c173,c256,c512,c768'
termux-exec.system_linker_exec: android_build_version_sdk: '33'
termux-exec.system_linker_exec: system_linker_exec_mode: 'enable'
termux-exec.system_linker_exec: se_process_context_from_file: 'u:r:untrusted_app:s0:c173,c256,c512,c768'
termux-exec.system_linker_exec: app_data_file_exec_exempted: 'false'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'true'
termux-exec.ld_preload_lib: Setting primary Termux '$LD_PRELOAD' library in 'libtermux-exec-ld-preload.so' to '/data/data/com.termux/files/usr/lib/libtermux-exec-linker-ld-preload.so'


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '33'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/apex/com.android.runtime/bin/linker64'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'true'
true

$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '33'
termux-exec.system_linker_exec: system_linker_exec_mode: 'enable'
termux-exec.system_linker_exec: se_process_context_from_file: 'u:r:untrusted_app:s0:c173,c256,c512,c768'
termux-exec.system_linker_exec: app_data_file_exec_exempted: 'false'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'true'
true


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '33'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/apex/com.android.runtime/bin/linker64'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'true'
true

$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '33'
termux-exec.system_linker_exec: system_linker_exec_mode: 'force'
termux-exec.system_linker_exec: system_linker_exec_available: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'true'
true


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force_all TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '33'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/apex/com.android.runtime/bin/linker64'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'true'
true

$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force_all TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '33'
termux-exec.system_linker_exec: system_linker_exec_mode: 'force_all'
termux-exec.system_linker_exec: system_linker_exec_available: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'true'
true



### Root shell

$ su
$ sh -c '. "/data/data/com.termux/files/usr/etc/termux/termux.env" && cd "$HOME" && exec login'


# TERMUX_EXEC__LD_PRELOAD_LIB__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-ld-preload-lib setup
termux-exec: Cannot set primary Termux '$LD_PRELOAD' library while running as root (0) user


# TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/data/data/com.termux/files/usr/bin/coreutils'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'false'
false

# TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'enable'
termux-exec.system_linker_exec: uid_to_exempt: '0'
termux-exec.system_linker_exec: app_data_file_exec_exempted: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'false'
false


# TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/data/data/com.termux/files/usr/bin/coreutils'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'false'
false

# TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'force'
termux-exec.system_linker_exec: system_linker_exec_available: 'true'
termux-exec.system_linker_exec: uid_to_exempt: '0'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'false'
false


# TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force_all TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/apex/com.android.runtime/bin/linker64'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'true'
true

# TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force_all TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'force_all'
termux-exec.system_linker_exec: system_linker_exec_available: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'true'



### Termux shell with 'u:r:magisk:s0' context after dropping privileges with root

$ su $(id -u)
$ sh -c '. "/data/data/com.termux/files/usr/etc/termux/termux.env" && cd "$HOME" && exec login'


$ TERMUX_EXEC__LD_PRELOAD_LIB__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-ld-preload-lib setup
termux-exec.ld_preload_lib: se_process_context_from_file: 'u:r:magisk:s0'
termux-exec: Cannot set primary Termux '$LD_PRELOAD' library as su/shell user was used to drop privileges/capabilities and uid to an unprivileged user '10212', but the process context 'u:r:magisk:s0' was not switched to Termux app's normal context
termux-exec: If updating 'termux-exec' package, then do it from a normal shell inside Termux app instead of from inside root or adb/run-as shells


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/data/data/com.termux/files/usr/bin/coreutils'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'false'
false

$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'enable'
termux-exec.system_linker_exec: se_process_context_from_file: 'u:r:magisk:s0'
termux-exec.system_linker_exec: app_data_file_exec_exempted: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'false'
false


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/apex/com.android.runtime/bin/linker64'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'true'
true

$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'force'
termux-exec.system_linker_exec: system_linker_exec_available: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'true'
true


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force_all TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/apex/com.android.runtime/bin/linker64'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'true'
true

$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force_all TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'force_all'
termux-exec.system_linker_exec: system_linker_exec_available: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'true'
true



### Termux shell with 'u:r:runas_app:s0' context after run-as command

$ su
$ run-as 'com.termux' --user 0 sh -c '. "/data/data/com.termux/files/usr/etc/termux/termux.env" && cd "$HOME" && exec login'


$ TERMUX_EXEC__LD_PRELOAD_LIB__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-ld-preload-lib setup
termux-exec.ld_preload_lib: se_process_context_from_file: 'u:r:runas_app:s0:c212,c256,c512,c768'
termux-exec: Cannot set primary Termux '$LD_PRELOAD' library as su/shell user was used to drop privileges/capabilities and uid to an unprivileged user '10212', but the process context 'u:r:runas_app:s0:c212,c256,c512,c768' was not switched to Termux app's normal context
termux-exec: If updating 'termux-exec' package, then do it from a normal shell inside Termux app instead of from inside root or adb/run-as shells


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/data/data/com.termux/files/usr/bin/coreutils'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'false'
false

$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'enable'
termux-exec.system_linker_exec: se_process_context_from_file: 'u:r:runas_app:s0:c212,c256,c512,c768'
termux-exec.system_linker_exec: app_data_file_exec_exempted: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'false'
false


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/apex/com.android.runtime/bin/linker64'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'true'
true

$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'force'
termux-exec.system_linker_exec: system_linker_exec_available: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'true'
true


$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force_all TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec is-enabled
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: stat_output: `'/proc/self/exe' -> '/apex/com.android.runtime/bin/linker64'`
termux-exec.system_linker_exec: system_linker_exec_is_enabled: 'true'
true

$ TERMUX_EXEC__SYSTEM_LINKER_EXEC__MODE=force_all TERMUX_EXEC__SYSTEM_LINKER_EXEC__LOG_LEVEL=2 /data/data/com.termux/files/usr/bin/termux-exec-system-linker-exec should-enable
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'force_all'
termux-exec.system_linker_exec: system_linker_exec_available: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'true'
true

I have found a new issue. If Termux uid owned shell is started with u:r:magisk:s0 context after dropping privileges with root, and termux-exec package is attempted to be updated, while it will abort due to check in termux-exec-ld-preload-lib, the /data/data/com.termux/files/usr/lib/libtermux-exec.so symlink is no longer accessible from normal Termux shell or run-as shell, as during dpkg restore, the selinux file context gets changed from u:object_r:app_data_file:s0:c212,c256,c512,c768 to u:object_r:app_data_file:s0 i.e MCS categories get stripped as u:r:magisk:s0 does not have them during write operation (guessing...).

SeLinux logs the following error after disabling SELinux rate limit with su -c auditctl -r 0: avc: denied { read } for name="libtermux-exec.so" dev="dm-46" ino=361842 scontext=u:r:untrusted_app_27:s0:c212,c256,c512,c768 tcontext=u:object_r:app_data_file:s0 tclass=lnk_file permissive=0 app=com.termux

### Normal Termux shell

$ cp /sdcard/termux-exec.deb $HOME/

$ ls -lZ "$PREFIX/lib/libtermux-exec.so"
lrwxrwxrwx. 1 u0_a212 u0_a212 u:object_r:app_data_file:s0:c212,c256,c512,c768 00 Aug  6 00:00 /data/data/com.termux/files/usr/lib/libtermux-exec.so -> libtermux-exec-ld-preload.so

$ ls -lZ "$PREFIX/lib/libtermux-exec-ld-preload.so"
-rw-------. 1 u0_a212 u0_a212 u:object_r:app_data_file:s0:c212,c256,c512,c768 52760 Aug  6 00:00 /data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so



### Termux shell with 'u:r:magisk:s0' context after dropping privileges with root

$ su $(id -u)
$ sh -c '. "/data/data/com.termux/files/usr/etc/termux/termux.env" && cd "$HOME" && exec login'

$ dpkg -i termux-exec.deb
(Reading database ... 4476 files and directories currently installed.)
Preparing to unpack termux-exec.deb ...
Unpacking termux-exec (1:2.4.0) over (1:2.4.0) ...
Setting up termux-exec (1:2.4.0) ...
termux-exec.postinst: Start
termux-exec.postinst: Setting primary Termux '$LD_PRELOAD' library
termux-exec.postinst: android_build_version_sdk: '35'
termux-exec.ld_preload_lib: se_process_context_from_file: 'u:r:magisk:s0'
termux-exec: Cannot set primary Termux '$LD_PRELOAD' library as su/shell user was used to drop privileges/capabilities and uid to an unprivileged user '10212', but the process context 'u:r:magisk:s0' was not switched to Termux app's normal context
termux-exec: If updating 'termux-exec' package, then do it from a normal shell inside Termux app instead of from inside root or adb/run-as shells
dpkg: error processing package termux-exec (--install):
 installed termux-exec package post-installation script subprocess returned error exit status 1
Errors were encountered while processing:
 termux-exec

# Can access symlink likely because context is 'u:r:magisk:s0'
$ ls -lZ "$PREFIX/lib/libtermux-exec.so"
lrwxrwxrwx. 1 u0_a212 u0_a212 u:object_r:app_data_file:s0 00 Aug  5 00:00 /data/data/com.termux/files/usr/lib/libtermux-exec.so -> libtermux-exec-ld-preload.so

$ exit
logout
2|:/data/data/com.termux/files/home
# exit



### Termux shell with 'u:r:runas_app:s0' context after run-as command

$ su
$ run-as 'com.termux' --user 0 sh -c '. "/data/data/com.termux/files/usr/etc/termux/termux.env" && cd "$HOME" && exec login'

$ dpkg -i termux-exec.deb
(Reading database ... 4560 files and directories currently installed.)
Preparing to unpack termux-exec.deb ...
Unpacking termux-exec (1:2.4.0) over (1:2.4.0) ...
dpkg: error processing archive termux-exec.deb (--install):
 failed to stat (dereference) existing symlink '/data/data/com.termux/files/usr/lib/libtermux-exec.so': Permission denied
Errors were encountered while processing:
 termux-exec.deb

# Cannot access symlink
$ ls -lZ "$PREFIX/lib/libtermux-exec.so"
ls: cannot read symbolic link '/data/data/com.termux/files/usr/lib/libtermux-exec.so': Permission denied
lrwxrwxrwx. 1 u0_a212 u0_a212 u:object_r:app_data_file:s0 00 Aug  5 00:00 /data/data/com.termux/files/usr/lib/libtermux-exec.so

$ stat -c %N "$PREFIX/lib/libtermux-exec.so"
'/data/data/com.termux/files/usr/lib/libtermux-exec.so'stat: cannot read symbolic link '/data/data/com.termux/files/usr/lib/libtermux-exec.so': Permission denied

$ su -c ls -lZ "$PREFIX/lib/libtermux-exec.so"
lrwxrwxrwx 1 u0_a212 u0_a212 u:object_r:app_data_file:s0  00 2026-08-00 00:00 /data/data/com.termux/files/usr/lib/libtermux-exec.so -> libtermux-exec-ld-preload.so

$ su -c stat -c %N "$PREFIX/lib/libtermux-exec.so"
/data/data/com.termux/files/usr/lib/libtermux-exec.so -> 'libtermux-exec-ld-preload.so'

$ ls -lZ "$PREFIX/lib/libtermux-exec-ld-preload.so"
-rwx------. 1 u0_a212 u0_a212 u:object_r:app_data_file:s0 53128 May 00  2025 /data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so

$ exit
logout
2|:/data/data/com.termux/files/home
# exit



### Normal Termux shell

# Cannot access symlink
$ ls -lZ "$PREFIX/lib/libtermux-exec.so"
ls: cannot read symbolic link '/data/data/com.termux/files/usr/lib/libtermux-exec.so': Permission denied
lrwxrwxrwx. 1 u0_a212 u0_a212 u:object_r:app_data_file:s0 00 Aug  5 00:00 /data/data/com.termux/files/usr/lib/libtermux-exec.so

$ dpkg -i termux-exec.deb
(Reading database ... 4476 files and directories currently installed.)
Preparing to unpack termux-exec.deb ...
Unpacking termux-exec (1:2.4.0) over (1:2.4.0) ...
dpkg: error processing archive termux-exec.deb (--install):
 failed to stat (dereference) existing symlink '/data/data/com.termux/files/usr/lib/libtermux-exec.so': Permission denied
termux-exec.postinst: Start
termux-exec.postinst: End
Errors were encountered while processing:
 termux-exec.deb

$ su -c restorecon -DFv /data/data/com.termux/files/usr/lib/libtermux-exec.so
su -c restorecon -DFv /data/data/com.termux/files/usr/lib/libtermux-exec.so
SELinux: Loaded file context from:
                /system/etc/selinux/plat_file_contexts
                /system_ext/etc/selinux/system_ext_file_contexts
                /vendor/etc/selinux/vendor_file_contexts
SELinux:  Relabeling /data/data/com.termux/files/usr/lib/libtermux-exec.so from u:object_r:app_data_file:s0 to u:object_r:app_data_file:s0:c212,c256,c512,c768.

$ ls -lZ "$PREFIX/lib/libtermux-exec.so"
lrwxrwxrwx. 1 u0_a212 u0_a212 u:object_r:app_data_file:s0:c212,c256,c512,c768 00 Aug  6 00:00 /data/data/com.termux/files/usr/lib/libtermux-exec.so -> libtermux-exec-ld-preload.so

# Failure now for a different symlink
$ dpkg -i termux-exec.deb
(Reading database ... 4476 files and directories currently installed.)
Preparing to unpack termux-exec.deb ...
Unpacking termux-exec (1:2.4.0) over (1:2.4.0) ...
dpkg: error processing archive termux-exec.deb (--install):
 failed to stat (dereference) existing symlink '/data/data/com.termux/files/usr/libexec/installed-tests/termux-exec/lib/termux-exec_nos_c/tre/scripts/termux/api/termux_exec/service/ld_preload/direct/exec/files/print-args-binary.sym': Permission denied
termux-exec.postinst: Start
termux-exec.postinst: End
Errors were encountered while processing:
 termux-exec.deb

The issue does not occur if run-as is used to attempt installation as u:r:runas_app:s0:c212,c256,c512,c768 process context already has the categories, at least on Android 15, just like u:r:untrusted_app_27:s0:c212,c256,c512,c768.

### Normal Termux shell

$ cp /sdcard/termux-exec.deb $HOME/



### Termux shell with 'u:r:runas_app:s0' context after run-as command

$ su
$ run-as 'com.termux' --user 0 sh -c '. "/data/data/com.termux/files/usr/etc/termux/termux.env" && cd "$HOME" && exec login'

$ dpkg -i termux-exec.deb
(Reading database ... 4473 files and directories currently installed.)
Preparing to unpack termux-exec.deb ...
Unpacking termux-exec (1:2.4.0) over (1:2.3.0) ...
Setting up termux-exec (1:2.4.0) ...
termux-exec.postinst: Start
termux-exec.postinst: Setting primary Termux '$LD_PRELOAD' library
termux-exec.postinst: android_build_version_sdk: '35'
termux-exec.ld_preload_lib: se_process_context_from_file: 'u:r:runas_app:s0:c212,c256,c512,c768'
termux-exec: Cannot set primary Termux '$LD_PRELOAD' library as su/shell user was used to drop privileges/capabilities and uid to an unprivileged user '10212', but the process context 'u:r:runas_app:s0:c212,c256,c512,c768' was not switched to Termux app's normal context
termux-exec: If updating 'termux-exec' package, then do it from a normal shell inside Termux app instead of from inside root or adb/run-as shells
dpkg: error processing package termux-exec (--install):
 installed termux-exec package post-installation script subprocess returned error exit status 1
Errors were encountered while processing:
 termux-exec

$ exit
logout
1|:/data/data/com.termux/files/home
# exit



### Normal Termux shell

$ ls -lZ "$PREFIX/lib/libtermux-exec.so"
lrwxrwxrwx. 1 u0_a212 u0_a212 u:object_r:app_data_file:s0:c212,c256,c512,c768 00 Aug  6 00:00 /data/data/com.termux/files/usr/lib/libtermux-exec.so -> libtermux-exec-ld-preload.so

$ dpkg -i termux-exec.deb
(Reading database ... 4476 files and directories currently installed.)
Preparing to unpack termux-exec.deb ...
Unpacking termux-exec (1:2.4.0) over (1:2.4.0) ...
Setting up termux-exec (1:2.4.0) ...
termux-exec.postinst: Start
termux-exec.postinst: Setting primary Termux '$LD_PRELOAD' library
termux-exec.postinst: android_build_version_sdk: '35'
termux-exec.ld_preload_lib: se_process_context_from_file: 'u:r:untrusted_app_27:s0:c212,c256,c512,c768'
termux-exec.system_linker_exec: android_build_version_sdk: '35'
termux-exec.system_linker_exec: system_linker_exec_mode: 'enable'
termux-exec.system_linker_exec: se_process_context_from_file: 'u:r:untrusted_app_27:s0:c212,c256,c512,c768'
termux-exec.system_linker_exec: app_data_file_exec_exempted: 'true'
termux-exec.system_linker_exec: system_linker_exec_should_enable: 'false'
termux-exec.ld_preload_lib: Setting primary Termux '$LD_PRELOAD' library in 'libtermux-exec-ld-preload.so' to '/data/data/com.termux/files/usr/lib/libtermux-exec-direct-ld-preload.so'
termux-exec.postinst: End

Additionally, dpkg has no checks for running as root, like apt does, which completely breaks $LD_PRELOAD and no command can be run anymore.

### Root shell

$ su
$ sh -c '. "/data/data/com.termux/files/usr/etc/termux/termux.env" && cd "$HOME" && exec login'

# dpkg -i termux-exec.deb
(Reading database ... 4473 files and directories currently installed.)
Preparing to unpack termux-exec.deb ...
Unpacking termux-exec (1:2.4.0) over (1:2.3.0) ...
Setting up termux-exec (1:2.4.0) ...
termux-exec.postinst: Start
termux-exec.postinst: Setting primary Termux '$LD_PRELOAD' library
termux-exec.postinst: android_build_version_sdk: '35'
termux-exec: Cannot set primary Termux '$LD_PRELOAD' library while running as root (0) user
dpkg: error processing package termux-exec (--install):
 installed termux-exec package post-installation script subprocess returned error exit status 1
Errors were encountered while processing:
 termux-exec

# exit
logout
1|:/data/data/com.termux/files/home
# exit


### Normal Termux shell

$ whoami
CANNOT LINK EXECUTABLE "whoami": library "/data/data/com.termux/files/usr/lib/libtermux-exec-ld-preload.so" not found: needed by main executable

To fix these, dpkg should have root uid check added, and both dpkg and apt should add the same process context checks (u:r:su:s0/u:r:ksu:s0/u:r:magisk:s0) added in termux-exec-ld-preload-lib via 89422f4 to prevent files to get broken, perhaps even u:r:runas_app: depending on if it sets the categories on older Android versions correctly or not. The getSeProcessContextFromFile() function can be used as used by shouldEnableSystemLinkerExec() if apt/dpkg cannot/should not be linked to libandroid-selinux to run getcon().

@robertkirkman

robertkirkman commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

Thank you for explaining, and before now I did not know about that problem.

I only tested creating and editing normal files from both entrypoints, not symbolic links, and it seems that normal files are not affected by that problem, only symbolic links, so I never knew that problem existed.

Currently, I have noticed that the command sudo setenforce 0 at any time (something that I am pretty sure all devices that can use su are also able to run) is able to work around that problem, with the caveats that, just like using su is probably less secure to begin with than using run-as, using SELinux Permissive further reduces security, and also relying on that would result in polluting the Termux with symbolic links that are missing MCS categories, meaning that practically speaking in that configuration SELinux Permissive would become an indefinite necessity until the Termux is wiped and reset back to normal.

I'm trying to figure out if there is any way to write code that can detect either whether SELinux is Enforcing, or whether the symbolic link problem itself is reproducible, while the active process context is the one where the error message needs to be shown by apt and dpkg, potentially so that I could try to add a condition to only fail and print the error message in the event that the symbolic link problem is currently reproducible on the device, but I'm not sure whether that's possible.

Some custom ROMs do not support SELinux Enforcing, so there are at least some cases where I believe the users are used to SELinux Permissive and would feel restricted if something blocks them in a way that should only block SELinux Enforcing devices.

@robertkirkman

Copy link
Copy Markdown
Member Author

run-as 'com.termux' --user 0 sh -c '. "/data/data/com.termux/files/usr/etc/termux/termux.env" && cd "$HOME" && exec login'

it's an interesting way to set it up, but one of the reasons I avoided that in my own tests is because that results in there being a TERMUX__SE_PROCESS_CONTEXT variable in the environment with a value that is mismatched from the value shown by /proc/self/attr/current, and I wasn't sure whether that could cause any unexpected behavior. Do you think that particular discrepancy would be fine?

~ $ env | grep PROCESS
TERMUX__SE_PROCESS_CONTEXT=u:r:untrusted_app_27:s0:c117,c256,c512,c768
~ $ cat /proc/self/attr/current 
u:r:runas_app:s0:c117,c256,c512,c76

@robertkirkman

robertkirkman commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

I have found this way https://stackoverflow.com/questions/72868065/how-to-check-if-selinux-is-enforcing-or-permissive , like this:

#include <stdio.h>
#include <selinux/selinux.h>

int main(void) {
    printf("Is SELinux Enforcing: %s\n", is_selinux_enabled() && security_getenforce()
        ? "yes" : "no");
    return 0;
}

It works to detect whether SELinux is Enforcing without root, unlike the regular getenforce command.

If you aren't already working on the check and error message for apt and dpkg that you mentioned is necessary, then I can try to make that and insert this check alongside the check for whether the current process context is one of u:r:su:s0, u:r:ksu:s0 or u:r:magisk:s0 to make the outcome determined by both factors (as well as the check for whether the user ID is 0)

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.

2 participants