termux-exec icon indicating copy to clipboard operation
termux-exec copied to clipboard

Avoid executing ELF files directly

Open fornwall opened this issue 2 years ago • 79 comments

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-exec containing 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=1 to get verbose logging to stderr
    • This log output may interfere with programs
  • export TERMUX_EXEC_OPTOUT=1 to opt out from termux-exec - it will not do any modification to execve calls

Helping with testing: Known issues

  • Statically linked executables such as zig does not work (linker errors with has 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 SESSION button, and click FAILSAFE in 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

Updates

  • 2023-10-17 (file te-997.deb, apt show termux-exec should show version 9: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/exec replacement.
  • 2023-10-05
    • Various fixes to edge cases and logging

fornwall avatar Oct 02 '23 13:10 fornwall

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.

agnostic-apollo avatar Oct 02 '23 14:10 agnostic-apollo

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...

sylirre avatar Oct 02 '23 14:10 sylirre

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.

ETERNALBLUEbullrun avatar Oct 02 '23 14:10 ETERNALBLUEbullrun

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

agnostic-apollo avatar Oct 02 '23 14:10 agnostic-apollo

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.

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.

fornwall avatar Oct 02 '23 14:10 fornwall

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.

fornwall avatar Oct 02 '23 14:10 fornwall

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.

agnostic-apollo avatar Oct 02 '23 14:10 agnostic-apollo

@ETERNALBLUEbullrun be advised, you will get a temporary ban for further unrelated comments.

agnostic-apollo avatar Oct 02 '23 15:10 agnostic-apollo

@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.

ETERNALBLUEbullrun avatar Oct 02 '23 15:10 ETERNALBLUEbullrun

That solution may be problematic. Linker on old Android version does not allow to do this. But I am not sure when it starts.

twaik avatar Oct 02 '23 16:10 twaik

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.

chenxiaolong avatar Oct 02 '23 16:10 chenxiaolong

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

Grimler91 avatar Oct 02 '23 18:10 Grimler91

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 exec restriction 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.

sylirre avatar Oct 02 '23 18:10 sylirre

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.

fornwall avatar Oct 02 '23 21:10 fornwall

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 avatar Oct 03 '23 00:10 truboxl

@truboxl Is it possible to configure zig to build dynamically linked binaries instead for termux packages?

fornwall avatar Oct 03 '23 00:10 fornwall

@fornwall I can look into it if zig upstream does support it...

truboxl avatar Oct 03 '23 12:10 truboxl

Will add ncdu2 later 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.

TomJo2000 avatar Oct 03 '23 12:10 TomJo2000

Would you mind linking the source repository? I can't seem to find it.

Same as ncdu 1.x https://dev.yorhel.nl/download/

truboxl avatar Oct 03 '23 13:10 truboxl

Oh I didn't realize its by the same author, thanks.

TomJo2000 avatar Oct 03 '23 13:10 TomJo2000

@fornwall I can look into it if zig upstream does support it...

@truboxl Thanks, that would be great!

fornwall avatar Oct 04 '23 12:10 fornwall

Help to test this would be great! See the PR description.

fornwall avatar Oct 04 '23 16:10 fornwall

Would https://github.com/pkgxdev/pkgx also help with this problem?

luisdavim avatar Oct 14 '23 08:10 luisdavim

Would https://github.com/pkgxdev/pkgx also help with this problem?

No.

twaik avatar Oct 14 '23 08:10 twaik

@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

sylirre avatar Oct 14 '23 10:10 sylirre

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.

landfillbaby avatar Oct 16 '23 12:10 landfillbaby

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.

landfillbaby avatar Oct 17 '23 03:10 landfillbaby

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.

fornwall avatar Oct 17 '23 14:10 fornwall

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.

fornwall avatar Oct 17 '23 14:10 fornwall

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.

agnostic-apollo avatar Oct 17 '23 14:10 agnostic-apollo