Avoid executing ELF files directly
This is change to work around the Android 10 R^W violation (execution of binary files are not allowed).
See the README for more information.
Helping with testing
Help to test this would be great! For context and more information, see Problem 1 and Solution 1 in the updated README.
NOTE: May totally break your system and need a reinstallation (or require a manual repair in a failsafe session - see below).
Steps (on aarch64 - let me know if you want to test on some other arch):
- Be sure you have updated packages:
pkg up
- Take a backup (if this is not a installation that you can reinstall termux quickly):
cp $PREFIX/lib/libtermux-exec.so $PREFIX/lib/libtermux-exec.so.bak
- Download and install a test build of
termux-execcontaining this code:curl -o ~/te-997.deb https://fornwall.me/te-997.deb && apt install --reinstall ~/te-997.deb
After this, new terminal sessions will be using the updated termux-exec version. A way to check this is by running ls -l /proc/self/exe - it should show a symlink pointing to a path ending in bin/linker64, since the linker is what is being executed.
Try things out (run scripts, compile programs, etc) and report if something does not work as a comment in this issue!
This should only affect behaviour on Android 10+ devices, but if you have an old Android version you are welcome to test that as well - that nothing has changed there.
Helping with testing: Debugging tools
export TERMUX_EXEC_DEBUG=1to get verbose logging to stderr- This log output may interfere with programs
export TERMUX_EXEC_OPTOUT=1to opt out from termux-exec - it will not do any modification toexecvecalls
Helping with testing: Known issues
- Statically linked executables such as
zigdoes not work (linker errors withhas unexpected e_type: 2)
Helping with testing: To restore
If things are not completely broken, apt install --reinstall termux-exec=1:1.0 will install the stable version of termux-exec.
If the installation is completely broken, so you can't even start a shell:
- Start a failsafe session by dragging out the drawer from the left, long press on the
NEW SESSIONbutton, and clickFAILSAFEin the dialog that shows up. cd $PREFIX/lib && cp libtermux-exec.so.bak libtermux-exec.so- You can now start a normal shell session again
- Restore the termux-exec package properly with:
apt install --reinstall termux-exec=1:1.0
- Restore the termux-exec package properly with:
Updates
- 2023-10-17 (file
te-997.deb,apt show termux-execshould show version9:9.7):- Various fixes to edge cases and logging
- Introduce
TERMUX_SELF_EXE, the absolute path to the executed file, which can be used as a/proc/self/execreplacement.
- 2023-10-05
- Various fixes to edge cases and logging
Very interesting solution. But yeah, it likely won't be acceptable by play store policies, but could be tried. Although not sure if android could add further selinux restrictions to prevent this.
Btw are you sure in testing, you uninstalled all termux apps, rebooted, and reinstalled, since otherwise untrusted_app_27 selinux process would still be assigned to the app, you can confirm with Termux in-app settings -> About, just making sure.
The executable being /system/bin/linker64 issues may be significant though.
But yeah, it likely won't be acceptable by play store policies, but could be tried.
As Play Store accepts apps that run binaries by proot and have target SDK 29+, I doubt that would be an issue. Maybe they consider the whole purpose of app (e.g. terminal emulators run binaries and need exec but games do not), who knows...
Allows to run GCC/Clang for compilation of your own programs, and to run your own programs? Under GPL, all Linux distribuitions should allow that much to us.
I think it may be more likely that they don't know proot is being used, otherwise if someone took interest, proot and this clearly does violate the policy.
An app distributed via Google Play may not modify, replace, or update itself using any method other than Google Play's update mechanism. Likewise, an app may not download executable code (such as dex, JAR, .so files) from a source other than Google Play. This restriction does not apply to code that runs in a virtual machine or an interpreter where either provides indirect access to Android APIs (such as JavaScript in a webview or browser). Apps or third-party code, like SDKs, with interpreted languages (JavaScript, Python, Lua, etc.) loaded at run time (for example, not packaged with the app) must not allow potential violations of Google Play policies.
https://support.google.com/googleplay/android-developer/answer/9888379?hl=en#zippy=%2Cexamples-of-common-violations
Btw are you sure in testing, you uninstalled all termux apps, rebooted, and reinstalled, since otherwise
untrusted_app_27selinux process would still be assigned to the app, you can confirm with Termux in-app settings -> About, just making sure.The executable being
/system/bin/linker64issues may be significant though.
Thanks, I noticed the behaviour you described as well, was really confusing first! But now I can confirm that I am indeed testing with effective targetSdk of 34! I'll share a branch of termux-app for testing shortly.
I think we should focus on the technical issue here, and leave Google Play policy out of the discussion, since I think that's a separate one. This change is hopefully good on its own, for bumping targetSdk also for distribution outside of Google Play.
Thanks, I noticed the behaviour you described as well, was really confusing first! But now I can confirm that I am indeed testing with effective targetSdk of 34! I'll share a branch of termux-app for testing shortly.
Welcome. Android also sometimes assigns latest target sdk SE_INFO, even if using older target sdk, and requires a reboot to fix. Have added checks for that too locally. I am far ahead of termux-app master locally, most of the non-terminal stuff has been rewritten/refactored, will be pushing in coming weeks when I manage to complete the changes.
leave Google Play policy out of the discussion
Fair enough. targetSdkVersion is far important, I also don't care too much about playstore either.
@ETERNALBLUEbullrun be advised, you will get a temporary ban for further unrelated comments.
@ETERNALBLUEbullrun be advised, you will get a temporary ban for further unrelated comments.
Relation of Linux GPL quotation (that Linux derivatives must allow compilation) was response to comment about how compilation was a Google Store policy violation.
That solution may be problematic. Linker on old Android version does not allow to do this. But I am not sure when it starts.
That solution may be problematic. Linker on old Android version does not allow to do this. But I am not sure when it starts.
Looks like it was introduced in https://android.googlesource.com/platform/bionic/+/8f639a40966c630c64166d2657da3ee641303194, which was first included in Android 8.
That solution may be problematic. Linker on old Android version does not allow to do this. But I am not sure when it starts.
Looks like it was introduced in https://android.googlesource.com/platform/bionic/+/8f639a40966c630c64166d2657da3ee641303194, which was first included in Android 8.
Only supporting android >= 8 (or even a version or two higher) for an app re-introduced to google play seems reasonable to me
Android 10+ solution is not needed to be used on older versions. It can be enabled on-demand, possibly with letting user to opt-out from this solution. Don't forget that:
- Not everyone uses Android 10 or higher there. Some people actually use Termux to make obsolete devices useful, e.g. to run home server.
- Some people have SELinux in permissive mode, whether intentionally or because custom ROM doesn't support enforcing. In this case
execrestriction doesn't work.
Though I found one problem with this solution: users will no longer be able to run static binaries. When I attempt to run static binary, I get error unexpected e_type: 2. Can't remember any Termux package using static binaries, but sideloaded third-party prebuilt software definitely will no longer run without proot.
Looks like it was introduced in https://android.googlesource.com/platform/bionic/+/8f639a40966c630c64166d2657da3ee641303194, which was first included in Android 8.
@chenxiaolong Thanks for finding that commit, great information!
Only supporting android >= 8 (or even a version or two higher) for an app re-introduced to google play seems reasonable to me
Android 10+ solution is not needed to be used on older versions. It can be enabled on-demand, possibly with letting user to opt-out from this solution
The code currently checks android_get_device_api_level() >= 29, so for older devices (pre Android 10) it doesn't wrap the call in the linker, but directly executes the file as before.
Can't remember any Termux package using static binaries
~ $ /system/bin/linker64 $(command -v zig)
error: "/data/data/com.termux/files/usr/lib/zig/zig" has unexpected e_type: 2
All ziglang binaries are static binaries using musl libc. Can filter out which packages using zig compiler with termux_setup_zig.
Will add ncdu2 later which is also written in zig. Likely a sign of more packages will experiment ziglang.
@truboxl Is it possible to configure zig to build dynamically linked binaries instead for termux packages?
@fornwall I can look into it if zig upstream does support it...
Will add
ncdu2later which is also written in zig. Likely a sign of more packages will experiment ziglang.
Would you mind linking the source repository? I can't seem to find it.
Would you mind linking the source repository? I can't seem to find it.
Same as ncdu 1.x https://dev.yorhel.nl/download/
Oh I didn't realize its by the same author, thanks.
@fornwall I can look into it if zig upstream does support it...
@truboxl Thanks, that would be great!
Help to test this would be great! See the PR description.
Would https://github.com/pkgxdev/pkgx also help with this problem?
Would https://github.com/pkgxdev/pkgx also help with this problem?
No.
@luisdavim pkgx not only irrelevant to this issue and would not solve it, but also would not work properly in Termux at all.
Termux is a specialized distribution and sideloaded binaries compiled for standard Linux systems won't work. See https://wiki.termux.com/wiki/Differences_from_Linux
This pull request is a possible workaround for execve() system call restriction by Android OS which is applicable when Termux app is compiled with target SDK level 29 or higher. See following tickets for some background:
- https://issuetracker.google.com/issues/128554619
- https://github.com/termux/termux-app/issues/1072
- https://github.com/termux/termux-app/issues/2155
stuff that uses /proc/*/cmdline and /proc/*/comm is affected as well as /proc/*/exe. probably more of procfs too...
and would this break ltrace for example? running eg ltrace /system/bin/linker64 $PREFIX/bin/cat /dev/null doesn't output anything.
so. testing. here's a c file:
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
char exe[PATH_MAX + 1] = {0};
if(readlink("/proc/self/exe", exe, PATH_MAX) != -1)
printf("/proc/self/exe: `%s`\n", exe);
FILE *file = fopen("/proc/self/cmdline", "r");
if(file) {
char *str = NULL;
size_t len = 0;
for(size_t x = 0; getdelim(&str, &len, '\0', file) != -1; x++)
printf("/proc/self/cmdline[%zu]: `%s`\n", x, str);
fclose(file);
free(str);
}
}
compiling and running it: with original termux-exec.so (unsetting LD_PRELOAD has identical results):
$ ./proctest hello
/proc/self/exe: `/data/data/com.termux/files/home/repos/tmp/termux-exec-test/proctest`
/proc/self/cmdline[0]: `./proctest`
/proc/self/cmdline[1]: `hello`
$ PATH="$PATH:$PWD" proctest hello
/proc/self/exe: `/data/data/com.termux/files/home/repos/tmp/termux-exec-test/proctest`
/proc/self/cmdline[0]: `proctest`
/proc/self/cmdline[1]: `hello`
$ "$PWD/proctest" hello
/proc/self/exe: `/data/data/com.termux/files/home/repos/tmp/termux-exec-test/proctest`
/proc/self/cmdline[0]: `/data/data/com.termux/files/home/repos/tmp/termux-exec-test/proctest`
/proc/self/cmdline[1]: `hello`
$ /system/bin/linker64 "$PWD/proctest" hello
/proc/self/exe: `/apex/com.android.runtime/bin/linker64`
/proc/self/cmdline[0]: `/system/bin/linker64`
/proc/self/cmdline[1]: `/data/data/com.termux/files/home/repos/tmp/termux-exec-test/proctest`
/proc/self/cmdline[2]: `hello`
with te-996 termux-exec.so in a new session:
$ ./proctest hello
/proc/self/exe: `/apex/com.android.runtime/bin/linker64`
/proc/self/cmdline[0]: `./proctest`
/proc/self/cmdline[1]: `/data/data/com.termux/files/home/repos/tmp/termux-exec-test/proctest`
/proc/self/cmdline[2]: `hello`
$ PATH="$PATH:$PWD" proctest hello
/proc/self/exe: `/apex/com.android.runtime/bin/linker64`
/proc/self/cmdline[0]: `proctest`
/proc/self/cmdline[1]: `/data/data/com.termux/files/home/repos/tmp/termux-exec-test/proctest`
/proc/self/cmdline[2]: `hello`
$ "$PWD/proctest" hello
/proc/self/exe: `/apex/com.android.runtime/bin/linker64`
/proc/self/cmdline[0]: `/data/data/com.termux/files/home/repos/tmp/termux-exec-test/proctest`
/proc/self/cmdline[1]: `/data/data/com.termux/files/home/repos/tmp/termux-exec-test/proctest`
/proc/self/cmdline[2]: `hello`
$ /system/bin/linker64 "$PWD/proctest" hello
/proc/self/exe: `/apex/com.android.runtime/bin/linker64`
/proc/self/cmdline[0]: `/system/bin/linker64`
/proc/self/cmdline[1]: `/data/data/com.termux/files/home/repos/tmp/termux-exec-test/proctest`
/proc/self/cmdline[2]: `hello`
why are both the 1st and 2nd arguments the exe?? this only happens once a new bash session is loaded after installing the new one (even if said bash session is within another bash session using the original termux-exec) and even if LD_PRELOAD is unset from within it.
llvm patch as i've done it works with both no linker wrap and the current broken way but will need to be changed once it's fixed.
procps patch and psmisc patch work i think? since they don't need absolute paths. attow those are the only 3 patched. procps patch doesn't fix ps -o exe though.
$ ps -o exe
EXE
/data/data/com.termux/files/usr/bin/bash
/apex/com.android.runtime/bin/linker64
$ ps -o args
COMMAND
/data/data/com.termux/files/usr/bin/bash -l
ps /data/data/com.termux/files/usr/bin/ps -o args
note the last line reflects the same issue i mentioned above. these are the only 3 patched so far afaik. also this does break ltrace and i'm pretty sure there's no way to fix it while using the linker wrapper.
what are the advantages to bumping the target sdk of the app anyway? to me it seems like it's just not worth it and those patches should be reverted. what does the main termux app need from android 10+? again the packages are separate and could be updated to a newer android version and just we warn the users about it. and could whatever it is that's needed from android 10+ not be worked around by adding it to termux-api app? is it just an attempt to get it back on google play? if so i really doubt it'll work. like. google add a w^x thing and we add a hacky workaround and upload it to their store. they reject it and in a future update remove said workaround. it isn't worth breaking what works. people who are inclined to want to use this app to begin with are going to be able to find it through f-droid or github.
what are the advantages to bumping the target sdk of the app anyway?
The main driver is being able to distribute the app using Google Play. Unfortunately a lot of users across the world are not able to (either due to lack of knowledge, or due to running locked down phones - this is quite common in several countries) install apps from outside of Google Play.
Also, I think regardless of Google Play distribution, bumping the target SDK is necessary in the long run - it's just not possible for Termux to remain on an old targetSdk indefinitely. Devices will stop supporting old targetSdk in the long run, as a security measure - having targetSdk is a temporary workaround to get old, unsecure (in the eyes of Google) behaviour - it won't be supported definitely.
This is not a perfect solution, but I think it's the best one, and good enough to work without any issues for most people in the end.
why are both the 1st and 2nd arguments the exe??
In this case:
/proc/self/exe: `/apex/com.android.runtime/bin/linker64`
/proc/self/cmdline[0]: `proctest`
/proc/self/cmdline[1]: `/data/data/com.termux/files/home/repos/tmp/termux-exec-test/proctest`
Here is cmdline[0] set to proctest so that programs that looks arg argv0 can see the expected value.
cmdline[1] is set to the absolute path to the executable - this is the argument to linker64, so it knows which file to load and execute.
Please used scoped variables, like TERMUX_EXEX__SELF_EXE (preferable) or TERMUX__SELF_EXE with two underscore. Will comment about targetsdk change later when I get back to laptop.