This answer says:

Because of the way directory/file permissions are configured on Android, you need to have the su binary on your /system partition in order for it to work. Putting elsewhere will not suffice, because it will not have the permissions it needs to actually allow processes to switch users.

What is the mechanism that causes binaries in /system to have special privileges?
Specifically, if I moved /system/xbin/su to /data/xbin/su (and changed anything relevant to point to /data/xbin/su instead of /system/xbin/su), what would be different?

My guess is that these privileges are enforced by SELinux.
I searched Android's sources, and found in platform/system/sepolicy/private/file_contexts:

/system(/.*)?       u:object_r:system_file:s0

and in platform/system/sepolicy/public/domain.te:

allow { appdomain coredomain } system_file:file { execute read open getattr map };

However, I also found in platform/system/sepolicy/private/file_contexts:

/system/xbin/su     u:object_r:su_exec:s0

and in platform/system/sepolicy/public/domain.te:

# Nobody should be able to execute su on user builds.
# On userdebug/eng builds, only dumpstate, shell, and
# su itself execute su.
neverallow { domain userdebug_or_eng(`-dumpstate -shell -su') } su_exec:file no_x_file_perms;

So I am confused.


Since Android is based on Linux kernel, getting root access by executing /system/xbin/su - like one can do on a Linux system - used to work in good old days but not now. The story is a bit twisted.

Special thing about /system/xbin/su isn't that it's in /system partition, but the set-user-ID-root (SUID) bit set on this binary makes it special. SUID (Set owner User ID up on execution) is a special type of permission on Linux kernel that grants the permissions of the file owner to the new process rather than inheriting permissions from parent process who runs it. Since the owner of /system/xbin/su is root (UID 0), it can switch the effective UID of process to 0 when executed, hence enabling calling process to have root access.
This is a part of Discretionary Access Control (DAC); one of Linux kernel's security features.

Implemented since KitKat, SELinux can control who executes su binary, by defining Access Vector rules:

Object: /system/xbin/su u:object_r:su_exec:s0
Subject: service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
And rule something like: allow su su_exec : file { read getattr map execute entrypoint open }

It's evident that only adb daemon when running as root, will be able to execute su binary. But adbd can't be run as root (1, 2) on final user builds, neither su binary is included on user builds. So, /system/xbin/su is intended only for developers to debug engineering or userdebug builds of a ROM.
Similarly some other programs like dumpstate and shell can make typetransition to run with context u:r:su:s0 while executing su for debugging purpose, as you have mentioned.

Keeping SELinux aside, on user build of a ROM that is intended for end users, placing su under /system won't give any advantage, even with set-user-ID-root. A normal app can't elevate its privileges to root by executing su because:

  • Starting with Jelly Bean, Android switched to file capabilities (a capability is a subset of root privileges) instead of relying on set-user-ID type of security vulnerabilities. A more secure mechanism: Ambient capabilities has also been introduced in Android Oreo.
  • System daemons and services can make use of file capabilities to gain process capabilities (see under Transformation of capabilities during execve) but apps can't do that either because they run with process control attribute NO_NEW_PRIVS set, ignoring set-user-ID as well as file capabilities. SUID is also ignored by mounting /system and /data with nosuid option for all apps.
  • UID can be switched only if calling process has SETUID/SETGID capability in its Bounding set (one of the 5 capabilities categories a process can have). But Android apps are made to run with all capabilities already dropped in all sets.
  • Starting with Oreo, apps' ability to change UID/GID has been further suppressed by blocking certain syscalls using seccomp filters.

ADB daemon, however runs with CAP_SETUID and CAP_SETGID. But adb shell too can't get us capabilities by executing a SUID-root su binary because NOROOT and NO_SETUID_FIXUP security bits are set on adbd process.

Syscalls used in these sandboxing methods: cap_set_proc, setresuid, setresgid, sys_seccomp, prctl (CAPBSET_DROP, SET_NO_NEW_PRIVS, SET_SECUREBITS, SET_SECCOMP) are all part of Minijail.

So if we want to have privileges with UID 0 and all 38 capabilities of Linux kernel, we need a rooting solution that can deal with all above described security measures. Since the standalone su binaries stopped working with the release of Jelly Bean, a transition was made to su daemon mode.

Now on a rooted phone, a fully privileged daemon runs from the very start of booting process (in fact init is replaced). When an app needs root access, it executes su binary provided by the rooting solution. This su doesn't change UID/GID on its own, but just connects to super user daemon through a UNIX socket and asks to provide the requesting app a root shell with all capabilities. In order to interact with user to grant/deny su requests from apps, su daemon is hooked with an app that can display user interface prompts. A database of granted/denied permissions is built by su daemon for future use.
In addition to that, SELinux denials are also handled by defining SELinux policy allow-rules when rooting the phone.

New methods of root like Magisk work in system-less mode i.e. without modifying /system partition. Only boot partition - that contains kernel and initramfs - is modified to inject su daemon service and new SELiux policies. A locked bootloader won't boot modified boot.img, so bootloader needs to be unlocked. For more details regarding this, see Unlocking Bootloader. However there have been some rooting hacks that worked by bypassing above described security implementations without going for proper way of rooting. Most of such exploits and vulnerabilities in Android OS have been fixed over time. Here is a good write up on this subject.
So, placing su binary under /system or any other location won't give any benefit.

PS: Please ignore so many links, they are for my own future reference.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.