Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No more exec from data folder on targetAPI >= Android Q #1072

Closed
n0n3m4 opened this issue Mar 18, 2019 · 308 comments
Closed

No more exec from data folder on targetAPI >= Android Q #1072

n0n3m4 opened this issue Mar 18, 2019 · 308 comments

Comments

@n0n3m4
Copy link

n0n3m4 commented Mar 18, 2019

The ability to run execve() on files within an application's home directory will be removed in target API > 28.
Here is the issue on Google bug tracker:
https://issuetracker.google.com/issues/128554619
As expected it is yet another "working-as-intended", furthermore, as commented in the issue,

Relying on exec() may be problematic in future Android versions.

This seems to completely break Termux in a long-term, as all its packages contain executables.

@ghost
Copy link

ghost commented Mar 18, 2019

This definetly the worst thing that may happen.
Though, in AVD with Android Q Preview Termux still works:

Screenshot_1552922384

Screenshot_1552922415

@ghost ghost added the information label Mar 18, 2019
@thestinger
Copy link

I brought up that this was forbidden and would likely become part of the SELinux policy last year (9 Apr 2018): #655.

As I stated then,

They rewrote the policy to be much more explicitly about this being disallowed, and it can be expected that they'll not only act on it for the Play Store but via SELinux policy.

They intend to support cases like Termux by having apps like Termux upload their packages as separate apps to the Play Store, with it initiating user installs / uninstalls of those apps.

Staying ahead of the game (i.e. future SELinux policy enforcement) would involve not extracting the executables manually but rather running them from where the apk package installer extracts libraries. Being able to execute app_data_file either as an executable or a library is on the way out in the long term.

It doesn't fundamentally break Termux, but rather Termux would need to switch to an alternative approach for distributing packages. Using apt within the app sandbox to install code isn't going to be viable anymore. The restrictions are no doubt going to be tightened up in the future too, and it can be expected that libraries will need to be mapped from the read-only app data directory or apk too, rather than the read-write home directory working. It wouldn't be necessary to split absolutely everything into assorted apks, but rather there could be apks for groups of related packages based on size. Termux can initiate the apk installs itself too, after prompting the user to allow it as an unknown source. It's an inconvenience for sure, but not a deal breaker.

Someone there accused me of concern trolling, but I was genuinely giving an insider heads up about likely changes on the horizon bringing w^x enforcement for data in storage. I use Termux and also work on Android OS security, so I wanted to have a conversation about how this could be addressed long before it was going to become a problem. I provided enforcement of the same kind of restrictions in my own downstream work, but with a toggle to turn it off for apps like Termux. I would have loved to have an alternate option for installing packages compatible with the security feature, and that would have prepared Termux for the eventual enforcement of this feature upstream.

It's worth noting that there are still holes in the SELinux policy, which can be used to bypass it by jumping through some hoops, but the intention is clearly to move towards enforcing w^x and enforcing the official rules via the implementation. See https://play.google.com/about/privacy-security-deception/malicious-behavior/.

The following are explicitly prohibited:

[...]
Apps or SDKs that download executable code, such as dex files or native code, from a source other than Google Play.
[...]

Termux could also ship the necessary scripting for generating a Termux package apk from executables/libraries so that users could create their own and continue running their own code, albeit less conveniently.

@thestinger
Copy link

@xeffyr This only applies to apps targeting API > 28. Termux will continue to work indefinitely as long as it continues to target API 28. However, there's a timeline for phasing out old API targets on the Play Store about a year after they're introduced. It then becomes impossible to upload a new version of the app without updating the target API level.

https://developer.android.com/distribute/best-practices/develop/target-sdk

Distributing the app outside the Play Store would still be possible, but the OS has the concept of a minimum supported target API level already (ro.build.version.min_supported_target_sdk). It's currently 17 in Android P and is being increased to 23 in Android Q. It currently only produces a warning, but it won't necessarily remain that way. They don't want targeting an old API level or distributing outside the Play Store to be a loophole for bypassing privacy and security restrictions tied to API level.

@Grimler91
Copy link
Member

@thestinger

Someone there accused me of concern trolling

I apologize for how issue #655 was received, I guess people freaked out and decided to shoot the messenger. We should have moderated it better.

It's currently 17 in Android P and is being increased to 23 in Android Q

I guess we have a year or two (at least) to figure out how to deal with this then

@thestinger
Copy link

You can probably stay at API 28 until around November 2020 for the Play Store assuming the timeline at https://developer.android.com/distribute/best-practices/develop/target-sdk is followed again for API 29.

It would be even longer until using API 28 broke outside the Play Store via a stricter future version of ro.build.version.min_supported_target_sdk doing more than warning. Probably at least 2 years and likely significantly longer.

So, it's not like it's going to suddenly break when Android Q comes out, since it's only for API 29+.

@its-pointless
Copy link

@thestinger

Termux could also ship the necessary scripting for generating a Termux package apk from executables/libraries so that users could create their own and continue running their own code, albeit less conveniently.

That is horrible in terms of usability. Also wouldn't the apks need to be signed with original apk key?

@ghost
Copy link

ghost commented Mar 19, 2019

Not only signed with original key. It seems that executables will have to be placed to native lib directory:

While exec() no longer works on files within the application home directory, it continues to be supported for files within the read-only /data/app directory. In particular, it should be possible to package the binaries into your application's native libs directory and enable android:extractNativeLibs=true, and then call exec() on the /data/app artifacts. A similar approach is done with the wrap.sh functionality, documented at https://developer.android.com/ndk/guides/wrap-script#packaging_wrapsh .

@ghost
Copy link

ghost commented Mar 19, 2019

Termux compiled with targetSdkVersion 29:

Termux not working on Android Q


Exec is allowed from native lib directory:
Binaries in /lib folder on Android Q
But this directory is read-only.

Distributing packages in APKs will either require to have hundreds of these APKs published to the Google Play / F-Droid or put multiple packages into single APK up to maximal size (~ 100MB). Technically it is possible to have very large APKs, but they cannot be distributed with Google Play and maybe F-Droid.

But anyway, distributing all packages in APKs is weird:

  • Such distribution may be a bit confusing from user's side.
  • Much longer waiting for updates, in case if distribution is done from Google Play.

Also, we should not forget that user environment will be broken:

  • It won't be possible to compile stuff in the Termux.
  • If somehow managed to compile, this stuff will be useless anyway.
  • Most stuff installable with pip, gem, cpan will be broken.

@thestinger
Copy link

Python, Ruby, Perl, etc. written by users will still work without a hassle because it's not native code. They won't be able to execute it as ./script.py but they can still run it as python3 script.py. I don't see any way that would ever stop working. Libraries without native code are also going to continue working without being in the native library directory too, since they don't get mapped as executable.

In the future, native libraries will likely all need to be in the native library directory to work but for API 29 they're only enforcing it for executables. I expect the enforcement will happen for native libraries next year, since they already added an auditallow rule to warn in the logs when it occurs.

Distributing packages in APKs will either require to have hundreds of these APKs published to the Google Play / F-Droid or put multiple packages into single APK up to maximal size (~ 100MB). Technically it is possible to have very large APKs, but they cannot be distributed with Google Play and maybe F-Droid.

Yes, that's what I was saying would need to happen: bundling groups of related packages inside of shared uid apks signed with the same key. For users to be able to extend it with their own native code, they would need to have their own build of Termux signed with their own key, along with scripting for generating new extension apks out of their code. There might be a better approach than my suggestion. It was just intended as a starting point.

It will also still be possible to interpret native code from the app data directory. For example, I think valgrind executable_in_app_data will continue working, even though ./executable_in_app_data will not, since valgrind acts as a machine code interpreter. Similarly, running native code inside QEMU would obviously still work.

@caleb-allen
Copy link

Is this a potential use case for Dynamic Delivery using App Bundles?

A dynamic feature module could be either a bundled group of related packages, or, if still feasible, each separate package. The submitted app bundle would include all available packages, where the base installation is only core packages, and pkg install would request the dynamic feature from Google Play.

This would likely require a pretty robust build system to keep the Google Play listing's packages in parity with termux-packages, perhaps as a separate listing from termux-core.

I see this as a way to comply with Google Play while still allowing a package manager-like function, potentially with another mechanism for installations outside of Google Play or from source that more closely resembles the current mechanism.

@thestinger
Copy link

thestinger commented Mar 19, 2019

Is this a potential use case for Dynamic Delivery using App Bundles?

It could work, if dynamic feature modules are able to ship additional executables / libraries to be extracted into the native libraries directory. It's not clear if that's possible. It appears the limit is 500MB via an app bundle, rather than 100MB, so it's a limited solution:

Publishing with Android App Bundles also increases the app size limit to 500MB without having to use APK expansion files. Keep in mind, this limit applies only to the actual download size, not the publishing size. So, users can download apps as large as 500MB and, with dynamic delivery, all of that storage is used for only the code and resources they need to run the app. When you combine this with support for uncompressed native libraries, larger apps, such as games, can reduce disk usage and increase user retention.

I see this as a way to comply with Google Play while still allowing a package manager-like function, potentially with another mechanism for installations outside of Google Play or from source that more closely resembles the current mechanism.

I don't think it's going to be able to be much different outside the Play Store, since the SELinux policy restrictions on executing only read-only native code still apply for apps outside the Play Store and Android variants without the Play Store or Play Services. It would make sense to work around this issue at the same time as coming into compliance with the Play Store policy on downloading executable code, but they don't necessarily need to be solved at the same time. I think it would be best to choose a path that will work both inside and outside the Play Store. I don't know how realistic it is to support the app bundle and dynamic feature modules outside Play, since there isn't an existing distribution option with support for it and it's not clear how much is available without Play Services, etc.

@caleb-allen
Copy link

caleb-allen commented Mar 19, 2019

Sure--I supposed my suggestion was intended more as a solution to the SELinux policy restrictions by using Google Play, but it does hinge on 1) the ability to ship additional executables (I suspect their example of games would make that true, but it is still a question) and 2) reliance on Google Play as a complete distribution system and being subject to their rules and whims.

It's a difficult problem, no doubt. I suspect that the timing of App Bundles and the native executable restriction policy are not entirely unrelated, and a solution to downloading and executing arbitrary native code without going through Google Play isn't immediately obvious.

bundling groups of related packages inside of shared uid apks signed with the same key.

Seems to be the quickest and simplest solution at this point, with the above mentioned restrictions which may annihilate realistic usability.

@corbinlc
Copy link

corbinlc commented Mar 19, 2019

What if you bundled PRoot (and busybox and a bare minimum of what is required to get a PRooted system setup and running) with the app itself and then had everything run through PRoot. Could PRoot be modified to allow executing outside of the lib directory? It has been proven twice before to be able to get around limitations of the sdcard being mounted noexec.

This is the most recent example of how PRoot can defeat noexec: termux/proot@ebcfe01

Notice what is not blocked here: https://android-review.googlesource.com/c/platform/system/sepolicy/+/804149

There would be a performance impact of course, but maybe it allows the party to go on.

I work on UserLAnd and we always use PRoot. This is where my mind went when I heard of this limitation. Thought I would share.

@ghost
Copy link

ghost commented Mar 19, 2019

If proot works, this means that we can left packages as-is. Proot also means that Termux will no longer be prefixed which is good as a lot of patches can be dropped + less headache with compiling software by user.

Will try to package proot into Termux APK and run on Android Q AVD to test this.

@n0n3m4
Copy link
Author

n0n3m4 commented Mar 19, 2019

I think that proot (or other theoretically possible packages like this) will be broken no earlier than Google forbids any kind of JIT in 3rd-party applications, so this is a very nice idea. I'm still not sure if Google will ever dare to do such thing.

@ghost
Copy link

ghost commented Mar 19, 2019

forbids any kind of JIT

If Google do this, it has to forbid javascript in browsers too.

@corbinlc
Copy link

PRoot will certainly execute from the apps lib directory, just like busybox did above, but you will need to play with the proot version I mentioned above. It is @michalbednarski 's work, so he might be able to comment on how suited he thinks it might be for this purpose.

@ghost
Copy link

ghost commented Mar 19, 2019

@corbinlc Original proot worked. Even this anti-noexec extension not needed.

Proot 1

So under proot everything works fine:
Proot 1

No need to distribute packages in APKs or bundles. Everything that is needed is to launch shell under proot.

@thestinger
Copy link

thestinger commented Mar 19, 2019

Notice what is not blocked here: https://android-review.googlesource.com/c/platform/system/sepolicy/+/804149

Note that execute for app_data_file (mapping as PROT_EXEC) had an auditallow rule added after this commit as they're planning on removing that in the near future, perhaps next year. It will currently log audit warnings leading up to the likely eventual removal.

The remaining holes in the SELinux policy are tied to the ART JIT and other JIT compilers: ashmem execute (an implementation detail that could change) and execmem (in-memory rwx code and rw -> rx transitions). ART supports using full AOT compilation instead of the JIT compiler, but it's not their current choice.

Chromium-based browsers including the WebView use execmem in the isolated_app domain, not untrusted_app, so even without an Apple-like approach only permitting the bundled browser and WebView for other apps to use, that wouldn't be a direct blocker. I would be surprised if they removed execmem for untrusted_app in the near future, but it could happen over the longer term, especially if they simply restrict it to a variant of isolated_app.

The original issue that I filed (#655) was about the general problem of this being in violation of their policy, before there was a partial implementation of a technical mechanism enforcing it for native code. There can never be full enforcement at a technical level since interpreters will continue to work even once native code execution is forbidden.

If Google do this, it has to forbid javascript in browsers too.

They permit JavaScript via the exception in the policy for isolated virtual machines:

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 (e.g. dex, JAR, .so files) from a source other than Google Play. This restriction does not apply to code that runs in a virtual machine and has limited access to Android APIs (such as JavaScript in a webview or browser).

https://play.google.com/about/privacy-security-deception/malicious-behavior/

The result at a technical level would be that they need to keep around execmem for a variant of the isolated_app SELinux domain used by browsers, but not untrusted_app. At the moment, both isolated_app and untrusted_app permit execmem, but Chromium doesn't use it in untrusted_app. ART uses the untrusted_app execmem when configured to do JIT compilation via the relevant system properties. It's fully optional though, and still supports full AOT compilation as was done in Android 6.

@corbinlc
Copy link

corbinlc commented Mar 19, 2019

@xeffyr I guess that makes sense, since PRoot already deals with execve. The new work is only needed for files on a noexec partition. That is great news for everyone.

@corbinlc
Copy link

@thestinger interesting comment about them potentially removing the ability to mmap(PROT_EXEC) for files outside of the app's lib directory in the future. I guess we live to fight another day (or more likely more than a year).

Do you know if they are planning on preventing mprotect(PROT_EXEC)? Where do you see the audit call you mentioned related to mmap(PROT_EXEC)? I don't know where the these exist in the Android source tree.

@thestinger
Copy link

Files in the app home directory are labelled as app_data_file and this is the audit rule generating warnings for mapping the data as executable:

https://android.googlesource.com/platform/system/sepolicy/+/08450264ae3f917f6b8e4091d6fedf84ef8d796f/private/untrusted_app_all.te#27

They add auditallow rules leading up to eventually removing it from the policy. You can try loading a library from app data with API 29 and you'll see an audit warning in the logs.

In-memory code generation via rwx pages or rw -> rx transitions is classified as execmem, which is something they'd like to remove in the long-term but it's currently used by the ART JIT. It's not used by Chromium within untrusted_app domains, since it only does that within the sandboxed renderers which use isolated_app domains. They could remove it from the main isolated_app and require that apps opt-in to having it available via a special domain. It should be expected that it will be eventually happen, since this is a very standard form of security hardening and they've been slowly moving towards it.

They already removed execmem for some of the base system including system_server. Their security team would obviously like to remove it everywhere, but for now the performance vs. memory vs. storage usage characteristics of mixed AOT and JIT compilation for ART are winning out over the security advantages of pure AOT compilation with execmem disallowed.

@thestinger
Copy link

The only reason they're starting with requiring executables to be in the native library directory rather than native libraries (which is the main purpose of the directory) is because that will impact more apps, so the transition will be more painful. Apps can also disable native library extraction and map native libraries from the apk directly, since the linker supports mapping libraries from zips directly as long as they meet the requirements (page aligned and not compressed). They have a pass for properly aligning them in the apk build system.

@thestinger
Copy link

thestinger commented Mar 19, 2019

It will actually log the warning about native libraries in the data directory for all API levels.

Here's where they continue to allow running executables from app_data_file for API < 28:

https://android.googlesource.com/platform/system/sepolicy/+/08450264ae3f917f6b8e4091d6fedf84ef8d796f/private/untrusted_app_27.te#30

Even though it's allowed, they do still log audit warnings about it for API < 29.

Also you can see that for API < 26, even more is permitted, since they've tightened up these policies for API 26+ in the past too:

https://android.googlesource.com/platform/system/sepolicy/+/08450264ae3f917f6b8e4091d6fedf84ef8d796f/private/untrusted_app_25.te

For example, API 26+ forbid execmod which means things like rx -> rw -> rx transitions for files mapped from storage. You can only do those kinds of transitions in-memory via execmem now. They've removed execmem for a lot of the base system but not yet apps, which is as I mentioned primarily due to the ART JIT and it'd also be a major backwards incompatible change. Finishing this up by removing the remaining 3 or so holes in the policy is definitely on their radar. It's just hard to get it done due to compatibility issues and performance/memory/storage vs. security tradeoffs made elsewhere that they'd need to decide are less important (and I definitely think they will, eventually).

@corbinlc
Copy link

This is great info @thestinger

@esminis
Copy link

esminis commented Mar 23, 2019

One more thing to mention Google is planning to deny any type of exec, read here: https://issuetracker.google.com/issues/128554619

Relying on exec() may be problematic in future Android versions.

The steps they took:

  1. Deny downloaded code execution using policy
  2. Now denying execution from /data/data/[app]
  3. Next step would probably be denying any exec, removing Runtime.exec / ProcessBuilder, ...

So any solution implemented here will probably not work next year or even earlier. Essentially they will kill apps like Termux, server apps, ... unless you build everything using jni / ndk.

@ghost
Copy link

ghost commented Mar 23, 2019

Essentially they will kill apps like Termux, server apps, ... unless you build everything using jni / ndk.

Not kill, if we switch to QEMU (system mode). It should be possible to turn QEMU into shared library (Limbo PC app did this) and attach serial consoles as terminals. Performance will be worse, but still usable.

That is, of course, if QEMU doesn't use mmap(PROT_EXEC), execmem or other similar things which may be blacklisted.

@thestinger
Copy link

One more thing to mention Google is planning to deny any type of exec

That's not backed up by what you're linking and I don't think it's true.

Deny downloaded code execution using policy

Executing downloaded code was denied by policy for a long time too. I only opened an issue when the wording became substantially stronger and considered malicious regardless of why it's done. I think Termux has been in violation of the policy as long as it has existed, but it became more clear that it was serious a year ago.

Next step would probably be denying any exec, removing Runtime.exec / ProcessBuilder, ...
So any solution implemented here will probably not work next year or even earlier. Essentially they will kill apps like Termux, server apps, ... unless you build everything using jni / ndk.

So any solution implemented here will probably not work next year or even earlier. Essentially they will kill apps like Termux, server apps, ... unless you build everything using jni / ndk.

Spawning processes with exec based on the native library directory is fully allowed by their app policy and documented as a supported feature. A random Google engineer on the bug tracker urging you to be cautious about using exec is good advice and doesn't imply there's any plan to remove it. Mapping code from outside the native library directory and apk as exec is definitely on the way out, and class loading is probably on their radar. There is not much they could do about third party interpreters beyond forbidding it in policy. The general policy is that executing arbitrary code not included with the apk is only allowed if it runs in a well sandboxed virtual machine, with the Chromium web sandbox as a baseline to compare since it's allowed through that exception.

They have no security reason to remove it now that they enforce w^x for it. Relying on executing system executables is a bad idea and it'd make sense for them to continue stripping down what is allowed.

By saying it's problematic, they are not saying that it's going to be forbidden, but rather that it doesn't fit well into a model where apps are often killed at any time and then respawned with the expectation that they saved all of their state to be restored, etc. That model can be more aggressive on some devices, which is what makes it problematic. Switching away from the app can result in all these native processes getting killed, potentially losing data. In practice, it's fine, especially with a foreground service to prioritize keeping the app alive. It also applies to a lot more than native processes. A long-running foreground service doesn't conform to the activity life cycle either. Essentially the same potential for problems there.

Not kill, if we switch to QEMU (system mode). It should be possible to turn QEMU into shared library (Limbo PC app did this) and attach serial consoles as terminals. Performance will be worse, but still usable.

I also don't think it's true.

That is, of course, if QEMU doesn't use mmap(PROT_EXEC), execmem or other similar things which may be blacklisted.

I think QEMU does rely on execmem. I'm not sure that it can be used without it. So, while that will currently work, it may not in the long term. I do think executing files from the native library directory is going to continue working in the long term. Using an emulator is certainly a viable approach though, but obviously slow, especially without execmem at some future point.

The viable long-term approach is using documented ways of doing things, and they have documentation showing how to execute something from the native library directory, along with that being fully supported for native libraries.

@thestinger
Copy link

Well I wish it was not not true but, doubt it following recent actions by Google / Android.

It's an issue tracker thread aimed at productively finding solutions to future compatibility issues starting with API 29. There's no point of being hysterical / dramatic and treating understandable security changes as an unpredictable, draconian crackdown on apps. The compatibility issue is unfortunate. There are trade-offs to changes like this, and this is the downside to it. The upside is making apps more secure against exploitation, while not hurting the vast majority of them.

It was denied by policy not so long ago - a year or two.

It was the policy that all code was signed and must come from Google Play for a long time. It was stated in multiple locations in different ways, and reworded into stronger wording over time. For example, here's one case that the policy is stated in 2013:

https://web.archive.org/web/20130730033518/http://play.google.com/about/developer-content-policy.html

An app downloaded from Google Play may not modify, replace or update its own APK binary code using any method other than Google Play's update mechanism.

The intent is all apps don't execute code that was signed and delivered through Google Play, other than outside of a sandboxed virtual machine environment (such as web content).

Yes, it has been possible to play semantic games with all variants of the wording, including the very aggressively worded version introduced in the malicious apps policy. It's best to understand the intent of the policy and comply with that, rather than trying to find loopholes. It has been worded different ways even at the same time, on different policy pages.

So essentially strip down everything and allow only some games, ad/spam-ware apps to run, that would be really secure. w^x does not change security in most cases on Android cause most of security/privacy issues are created by pure Java / Kotlin apps.

That's not what I said and I don't know where you're getting that. I said relying on system executables is a bad idea rather than shipping them with the app. For example, using the system toolbox / toybox would be a bad idea even if the SELinux policy allows it. It's not part of the official API.

It remains possible to execute native code, but the implementation permits fewer violations of the policy than it did before, not as an attempt to enforce the policy but to mitigate vulnerabilities in apps.

It's expected that code is shipped in the apk, and either executed from the apk or the native library directory. You aren't supposed to execute Java code from outside an installed apk / app bundle component either. That isn't impacted by SELinux policy right now. They could teach the class loader to enforce rules, but as I mentioned, they fundamentally cannot prevent people from breaking the rules with interpreters via a technical approach.

As I explained above, this has nothing to do with protecting the system from apps or protecting user data from apps but rather improving security for apps themselves. That's certainly something important and is a real world problem. There have been many cases of apps exploited due to them executing code from app data. They introduced a partial mitigation for it, in a way that only truly breaks compatibility with apps they'd already defined as violating their policies. That's why it made sense for them to do this. Yes, it's unfortunate for Termux, but they aren't doing it to be evil.

You mean "problematic in future" Runtime.exec? :)

I mean exactly what I said. Apps executing system executables are relying on a non-public API and can definitely expect problems, just like using private APIs elsewhere. You're reading something and twisting the meaning into what you want to see based on your already negative outlook. You view these security improvements as something bad. It's a balance between different needs, and the balance is moving towards security. That will make some people upset, but it's what most people want. There has always been this balance in place, and most people think Android was way too permissive with apps and needs to continue changing.

I think the only viable in future approach is compile everything using NDK and execute using JNI, but in light of Fuschia / Chrome OS changes it is very unclear.

Fuchsia is not Linux and is effectively a different platform. They may end up implementing a very complete layer for Linux binary compatibility. The CTS requires Linux compatibility so if they want full Android support they need to provide it. That's their problem to figure out. Executing native code is still possible there, but it's not Linux and currently isn't compatible with Linux at the ABI or API level. ChromeOS simply ships Android as a whole in a container and provides an Android compatible Linux kernel. It's just another kind of Android device and is CTS compatible.

The low-level portions of Android like Bionic and SELinux policy are entirely developed in the open rather than having code dumps. I also talk with members of their security team fairly regularly. It's not a mystery what they are working on and where they are planning on taking it in the future. It's not surprising that they partially implemented a very standard security mitigation used across other operating systems in various forms (not just iOS), and it's in line with their existing policy. You can look through the SELinux policy repository and see commit messages and code comments describing many of the future breaking changes.

They released the v2 sandbox (targetSandboxVersion) with Nougat and among other things it disabled app_data_file execution as a whole. That was essentially a preview of future changes.

There are a few long-term options. One is figuring out a way to ship code as part of apks and a way to offer users the option of generating an apk wrapping their native code in a usable way. Another is using a virtual machine to run everything, which would also work on non-Linux platforms, but supporting those is probably never going to be necessary. Those platforms would need to implement Linux compatibility to be Android compatible. This would be slower, especially if support for using a JIT compiler in untrusted_app goes away. It could be moved to isolated_app, but that would be hard.

@ghost
Copy link

ghost commented Nov 18, 2020

Also, regarding shared libraries in $PREFIX, --> https://support.google.com/googleplay/android-developer/answer/9888379?hl=en&ref_topic=9877467

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 (e.g. dex, JAR, .so files) from a source other than Google Play.

@ghost
Copy link

ghost commented Nov 18, 2020

Shared libraries seems like under SELinux restrictions too. (have avc granted because Termux using SDK 28)

11-18 14:57:32.120  6072  6072 W perl    : type=1400 audit(0.0:80671): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/perl5/5.32.0/aarch64-android/CORE/libperl.so" dev="dm-0" ino=1470621 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 14:57:42.540  6073  6073 W clear   : type=1400 audit(0.0:80677): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libtermux-exec.so" dev="dm-0" ino=1436457 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 14:57:45.820  6076  6076 W clear   : type=1400 audit(0.0:80683): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libncursesw.so.6.2" dev="dm-0" ino=1437010 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api

Probably because SELinux does not restrict execve specifically but rather executable code.

So placing shared libraries into $PREFIX are out of scope in any way.


UPD: test without termux-exec for clean results:

11-18 15:05:30.840  6139  6139 W ls      : type=1400 audit(0.0:80764): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libandroid-support.so" dev="dm-0" ino=1437036 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:33.830  6141  6141 W command-not-fou: type=1400 audit(0.0:80769): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libc++_shared.so" dev="dm-0" ino=1437038 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:37.680  6144  6144 W pkg     : type=1400 audit(0.0:80774): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libncursesw.so.6.2" dev="dm-0" ino=1437010 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:37.680  6144  6144 W pkg     : type=1400 audit(0.0:80775): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libandroid-support.so" dev="dm-0" ino=1437036 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:39.510  6157  6157 W find    : type=1400 audit(0.0:80815): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libandroid-support.so" dev="dm-0" ino=1437036 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:40.540  6181  6181 W apt-config: type=1400 audit(0.0:81008): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/liblz4.so.1.9.2" dev="dm-0" ino=1440485 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:40.540  6181  6181 W apt-config: type=1400 audit(0.0:81009): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libgpg-error.so" dev="dm-0" ino=1437037 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:41.560  6245  6245 W apt-config: type=1400 audit(0.0:81278): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libc++_shared.so" dev="dm-0" ino=1437038 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:41.560  6245  6245 W apt-config: type=1400 audit(0.0:81279): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libz.so.1.2.11" dev="dm-0" ino=1437031 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:42.600  6303  6303 W apt-config: type=1400 audit(0.0:81546): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libgcrypt.so" dev="dm-0" ino=1436638 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:42.600  6303  6303 W apt-config: type=1400 audit(0.0:81547): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libapt-private.so" dev="dm-0" ino=1436752 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:43.620  6361  6361 W apt-config: type=1400 audit(0.0:81814): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libiconv.so" dev="dm-0" ino=1437084 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api
11-18 15:05:43.620  6361  6361 W apt-config: type=1400 audit(0.0:81815): avc: granted { execute } for path="/data/data/com.termux/files/usr/lib/libandroid-glob.so" dev="dm-0" ino=1437006 scontext=u:r:untrusted_app_27:s0:c138,c256,c512,c768 tcontext=u:object_r:app_data_file:s0:c138,c256,c512,c768 tclass=file app=com.termux.api

@shadmansaleh
Copy link

Shared libraries are loaded by dynamic linker which is restricted by allowed "namespaces". Need a custom loader and is a W^X violation anyway.

Got it.

@finagolfin
Copy link
Member

finagolfin commented Nov 18, 2020

OK, so here's the final proposal, extended from what I wrote above, which I believe would work under Play Store restrictions as laid out by @xeffyr above:

Build all Termux packages and ship only the top 50-100 (ie how many ever fit under the apk size limit) most popular packages' native executables from the old com.termux/files/usr/bin/ folder and shared libraries from the old com.termux/files/usr/lib/ with the apk, but preinstalled in the SELinux-safe com.termux/lib/ directory instead. The Termux apk also comes with those popular packages' compressed .deb files, but the .debs no longer contain executables or shared libraries, and only these are uncompressed by apt/dpkg into $PREFIX when pkg install is run.

When a .deb package is installed, modify apt to simply stick a symbolic link in $PREFIX/bin/git pointing at lib/aarch64/bin/git in the read-only library directory for any bin/ executables, but it will unpack all other package files normally.

Next, create Termux package set apks, similar to the current addon apks like Termux API, that install all executables and shared libraries from that package set in com.termux/lib/ and ship the executable/library-free .deb packages for apt/dpkg/pkg to install, and distribute these apks from the Play Store.

If addon apks signed by com.termux are allowed to install executables and shared libraries in com.termux/lib/, this scheme should work and allow us to keep using apt/pkg for much of package management, while offloading the current high distribution costs to google. 😄

@ghost
Copy link

ghost commented Nov 18, 2020

that install executables and shared libraries from that package set in com.termux/lib/

Not installs, but symlinks. See above why.

Userspace /data/data/com.termux/lib is a symlink to RO & system-owned directory in /data/app/com.termux-* and is not present on Android 10+ anyway.

Don't forget that we need to keep $PREFIX structure.

Probably won't be easier than current full in-APK variant.

@finagolfin
Copy link
Member

Not installs, but symlinks.

As in Termux addon apks cannot place executables in com.termux/lib (or whatever that directory is really symlinked to), but can place symlinks in com.termux/lib to those executables in their own separate addon apk's storage, which can then be execve'd as part of Termux? Whatever the exact location, the key question is whether the Termux addon apks can add executables and shared libraries somewhere that can then be run under Termux. If so, this will work.

Don't forget that we need to keep $PREFIX structure.

Shouldn't be a problem if a symlink invoked in $PREFIX/bin is allowed to run an executable in com.termux/lib or other SELinux-approved directories.

Probably won't be easier than current full in-APK variant.

That all depends on how much packaging and file verification logic in apt/dpkg would need to be duplicated for "full in-APK" installation to work as well. My guess is that this apt/dpkg approach would be a lot less work.

@ghost
Copy link

ghost commented Nov 18, 2020

Shouldn't be a problem if a symlink invoked in $PREFIX/bin is allowed to run an executable

That's actually how Termux branch android-10 works. Executable is placed to system managed location and symlinked to $PREFIX.

Symbolic link is only a pointer and is not subject for SELinux restriction.

@RalfWerner
Copy link

@buttaface I am not sure whether you have already installed this 29 Artifact on your device (preferably with Android>9). If not, I recommend doing this to make the discussion more practical.

@finagolfin
Copy link
Member

I am not sure whether you have already installed this 29 Artifact on your device

No, I don't have any Android 10 or 11 devices right now, nor do I ever pop into an emulator, but I will be replacing my main Android device within the next couple months and I don't root my devices, so these new Android restrictions will soon affect my use of Termux.

@RalfWerner
Copy link

I don't have any Android 10 or 11 devices ... but I will be replacing my main Android device ... will soon affect ...

That's good! The Artifact of the Termux branch android-10 (29) can also be installed on old devices (minSdkVersion=24) to study the effects but without 29 restriktions. Good luck with your new device (affect only in 29 Termux).

@zacharee
Copy link

I'm not sure if this has been mentioned before, but /data/user_de/0/PACKAGE_NAME is readable, writeable, and executable in Android 11. I tested with an app targeting API 29 and 30 on Android 11, and was able to execute binaries in that directory.

I don't think that the PACKAGE_NAME directory always exists, so it needs to be created and set to be readable/writeable/executable before placing binaries in it.

My code:

assets.open("ASSET_NAME").buffered().use { input ->
    val pkgDir = File("/data/user_de/0", packageName)

    if (!pkgDir.exists()) {
        pkgDir.mkdir()
    }

    pkgDir.setReadable(true)
    pkgDir.setExecutable(true)

    val asset = File(pkgDir, "ASSET_NAME")

    asset.outputStream().buffered().use { output ->
        input.copyTo(output)
    }

    asset.setReadable(true)
    asset.setExecutable(true)

    //ASSET_NAME can now be executed
}

@ghost
Copy link

ghost commented Dec 11, 2020

@zacharee /data/user_de/0 is nowhere mentioned in SDK documentation as application data directory. Thus, your solution is a HACK.

Accepted solution is only one that meets following criteria:

  • Uses things officially mentioned in SDK documentations.
  • Do not violate Play Store policy, i.e. no downloadable code, everything must be distributed as part of APK file.
  • Solution must be viable in long-term.

Any solution that DO NOT meet these criteria will not be better than sticking to target API 28 and continuing to use apt.

@kdrag0n
Copy link
Contributor

kdrag0n commented Dec 11, 2020

Using user_de for app data is documented: https://developer.android.com/training/articles/direct-boot

It doesn't help with the Play Store policy violation, but it's another way to execute code with minimal hacks.

@ghost
Copy link

ghost commented Dec 11, 2020

@kdrag0n It is not documented as application data storage at https://developer.android.com/training/data-storage/. Your link is about different things.

Finally, it is very likely that /data/user_de will get its noexec to at some point.

it's another way to execute code with minimal hacks.

Thanks, but Termux currently has its own solution which takes much less hassle - stay with API 28. Jumping from one hack to another is not a solution for Android 10 execve problem.

@fieldlab
Copy link

There seems to be an obvious problem for Termux users here- shared directories are difficult, and shared executable directories are impossible except for this one case. This case seems intentional on Google's part. If you look at the folder ownership in /Android/data you see all the bizarre auto-generated user names for every installed package from play store, and you know why your Termux user name is some ugly random characters.

The big problem is none of the work in Termux can be exposed to the rest of Android or used or even seen by the operator outside the locked down command line sandbox. I have to copy every file manually from Termux to a shared folder, and the shared folder is then owned by root and is noexec, and when I copy a folder in Termux to shared storage, the ownership and all permissions are wiped out without warning and cannot be reset without copying it back and reconstructing manually, which is rude.

It would be nice to run some nicer Android code editor on a source file or script in Termux, and compile and run it in Termux. Currently, impossible except maybe in /Android/com.termux.

I can't comment on the security situation, but Termux needs to have working folders owned by Termux, executable in Termux, also visible to other applications (and the system owner). /Android/com.termux could be such a sandbox or dmz.

@finagolfin
Copy link
Member

There was some talk about trying some alternatives last week by the lead Termux dev on gitter, let us see what he decides to do. There's only so much we can do about new OS restrictions though.

@hltdev8642
Copy link

this is a LONGSHOT... but could it be possible to implement each package as an in-app purchase? (free, obviously)

I don't have any real experience implementing IAPs so apologies if this is a completely bogus idea! I am also (blindly) assuming that IAP data can be downloaded remotely. I doubt this will be helpful, but I figure I mine as well mention it (on the small chance it is a "good" idea... =] )

@ghost
Copy link

ghost commented Dec 26, 2020

Executable code must come within the APK file. Mentioned countless times in this thread, since this is the only way to avoid SELinux issue.

@hltdev8642
Copy link

Sorry... figured that was the case (I almost canceled my post because of that exact thought). I'll leave this thread alone now as I'm sure your incredibly tired of getting alerts from it Lol...

@iCodeShit
Copy link

@xeffyr Can we get exectuable permission in /data/local/tmp? If so we couldn't we use it for proot sessions? It's also a part of data folder and hardly any app would erase it. and as far as i know android wont break access to tmp folder for now as its used for running scripts and tools.

@TheBrokenRail
Copy link

@xeffyr Can we get exectuable permission in /data/local/tmp? If so we couldn't we use it for proot sessions? It's also a part of data folder and hardly any app would erase it. and as far as i know android wont break access to tmp folder for now as its used for running scripts and tools.

I'm pretty sure apps can't write to that anymore.

@ghost
Copy link

ghost commented Jan 4, 2021

Can we get exectuable permission in /data/local/tmp?

@iCodeShit No, apps can't write to /data/local/tmp it is usable only by ADB or rooted shell. Becoming a root-only app is not a way that @termux will choose for now.

Even if it is possible to put files here by regular app, that won't be a proper solution to the problem since this is still W^X violation.

We already have chosen in-APK packaging as the most correct solution and some work already done in https://github.com/termux/termux-app/tree/android-10 - it works, but needs some UX-related fixes. Other solutions posted here are either non-viable (like proot), inadequate (like convert everything to WASM) or just unproven ideas from people who do not really understand this issue.

@zx2c4
Copy link

zx2c4 commented Jan 6, 2021

I hope I'm not jumping into the koolaid without knowing the flavor, but in case it hasn't been mentioned yet: what's preventing you from just shipping a linker in userspace? You'd put the linker executable in the apk, so it works with api29, and then hijack execve calls to actually load the userspace linker path. Glibc's linker actually supports this directly, for example:

zx2c4@thinkpad ~ $ /lib64/ld-linux-x86-64.so.2 /bin/echo hello userspace linker
hello userspace linker
zx2c4@thinkpad ~ $ strace -e execve /lib64/ld-linux-x86-64.so.2 /bin/echo hello userspace linker
execve("/lib64/ld-linux-x86-64.so.2", ["/lib64/ld-linux-x86-64.so.2", "/bin/echo", "hello", "userspace", "linker"], 0x7fff1273cd60 /* 65 vars */) = 0
hello userspace linker
+++ exited with 0 +++
zx2c4@thinkpad ~ $

As you can see, there's only one call to execve, not two.

On the other hand, if those selinux rules are actually preventing the creation of executable mappings or something, maybe the problem is trickier than this, hence hundreds of comments above.

@ghost
Copy link

ghost commented Jan 6, 2021

what's preventing you from just shipping a linker in userspace? You'd put the linker executable in the apk

Same case as with proot - W^X violation. Code must be stored within the APK file, so will be managed by Android OS, be read-only and do not conflict with SELinux.

Okay, I see no one bother to understand where exactly the problem and more important that solution is already in development - https://github.com/termux/termux-app/tree/android-10.

I'm locking this issue permanently now because discussion already went to the dead end.


Proper solution must comply with Android OS security features and not attempt to use any holes.

@ghost ghost locked and limited conversation to collaborators Jan 6, 2021
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.