Android strongly encourages OEMs to test their SELinux implementations thoroughly. As manufacturers implement SELinux, they should apply the new policy to a test pool of devices first.
After applying a new policy, make sure SELinux is running in the correct
mode on the device by issuing the command getenforce
.
This prints the global SELinux mode: either Enforcing or Permissive. To
determine the SELinux mode for each domain, you must examine the corresponding
files or run the latest version of sepolicy-analyze
with the
appropriate (-p
) flag, present in
/platform/system/sepolicy/tools/
.
Read denials
Check for errors, which are routed as event logs to dmesg
and logcat
and are viewable locally on the device. Manufacturers
should examine the SELinux output to dmesg
on these devices and
refine settings prior to public release in permissive mode and eventual switch
to enforcing mode. SELinux log messages contain avc:
and so may
easily be found with grep
. It is possible to capture the ongoing
denial logs by running cat /proc/kmsg
or to capture denial logs
from the previous boot by running
cat /sys/fs/pstore/console-ramoops
.
SELinux error messages are rate-limited after boot complete to avoid swamping
the logs. To make sure you see all the relevant messages you can disable this
by running adb shell auditctl -r 0
.
With this output, manufacturers can readily identify when system users or components are in violation of SELinux policy. Manufacturers can then repair this bad behavior, either by changes to the software, SELinux policy, or both.
Specifically, these log messages indicate what processes would fail under enforcing mode and why. Here is an example:
avc: denied { connectto } for pid=2671 comm="ping" path="/dev/socket/dnsproxyd" scontext=u:r:shell:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket
Interpret this output like so:
- The
{ connectto }
above represents the action being taken. Together with thetclass
at the end (unix_stream_socket
), it tells you roughly what was being done to what. In this case, something was trying to connect to a unix stream socket. - The
scontext (u:r:shell:s0)
tells you what context initiated the action. In this case this is something running as the shell. - The
tcontext (u:r:netd:s0)
tells you the context of the action’s target. In this case, that’s a unix_stream_socket owned bynetd
. - The
comm="ping"
at the top gives you an additional hint about what was being run at the time the denial was generated. In this case, it’s a pretty good hint.
Another example:
adb shell su root dmesg | grep 'avc: '
Output:
<5> type=1400 audit: avc: denied { read write } for pid=177 comm="rmt_storage" name="mem" dev="tmpfs" ino=6004 scontext=u:r:rmt:s0 tcontext=u:object_r:kmem_device:s0 tclass=chr_file
Here are the key elements from this denial:
- Action - the attempted action is highlighted in brackets,
read write
orsetenforce
. - Actor - The
scontext
(source context) entry represents the actor, in this case thermt_storage
daemon. - Object - The
tcontext
(target context) entry represents the object being acted upon, in this case kmem. - Result - The
tclass
(target class) entry indicates the type of object being acted upon, in this case achr_file
(character device).
Dump iser and kernel stacks
In some cases, the information contained in the event log isn't sufficient to pinpoint the origin of the denial. It is often useful to gather the call chain, including kernel and userspace, to better understand why the denial occurred.
Recent kernels define a tracepoint named avc:selinux_audited
. Use Android
simpleperf
to enable this tracepoint and capture the callchain.
Supported configuration
- Linux kernel >= 5.10, in particular Android Common Kernel branches
mainline
and
android12-5.10
are supported.
The android12-5.4
branch is also supported. You may use
simpleperf
to determine if the tracepoint is defined on your device:adb root && adb shell simpleperf list | grep avc:selinux_audited
. For other kernel versions, you may cherry pick commits dd81662 and 30969bc. - It should be possible to reproduce the event you are debugging. Boot time events aren't supported using simpleperf; however you may still be able to restart the service to trigger the event.
Capture the call chain
The first step is to record the event using simpleperf record
:
adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"
Then, the event that caused the denial should be triggered. After that, the recording should
be stopped. In this example, by using Ctrl-c
, the sample should have been captured:
^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.
Finally, simpleperf report
may be used to inspect the captured stacktrace.
For instance:
adb shell -t "cd /data/local/tmp && su root simpleperf report -g --full-callgraph" [...] Children Self Command Pid Tid Shared Object Symbol 100.00% 0.00% dmesg 3318 3318 /apex/com.android.runtime/lib64/bionic/libc.so __libc_init | -- __libc_init | -- main toybox_main toy_exec_which dmesg_main klogctl entry_SYSCALL_64_after_hwframe do_syscall_64 __x64_sys_syslog do_syslog selinux_syslog slow_avc_audit common_lsm_audit avc_audit_post_callback avc_audit_post_callback
The call chain above is a unified kernel and userspace call chain. It gives you with a better
view of the code flow by starting the trace from userspace all the way down to the kernel where
the denial happens. For more information on simpleperf
, see the
Simpleperf Executable commands reference
Switch to permissive
SELinux enforcement can be disabled with adb on userdebug or eng builds. To do so,
first switch ADB to root by running adb root
. Then, to disable SELinux
enforcement, run:
adb shell setenforce 0
Or at the kernel command line (during early device bring-up):
androidboot.selinux=permissive
androidboot.selinux=enforcing
Or through bootconfig in Android 12:
androidboot.selinux=permissive
androidboot.selinux=enforcing
Use audit2allow
The audit2allow
tool takes dmesg
denials and
converts them into corresponding SELinux policy statements. As such, it can
greatly speed SELinux development.
To use it, run:
adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy
Nevertheless, care must be taken to examine each potential addition for
overreaching permissions. For example, feeding audit2allow
the
rmt_storage
denial shown earlier results in the following
suggested SELinux policy statement:
#============= shell ============== allow shell kernel:security setenforce; #============= rmt ============== allow rmt kmem_device:chr_file { read write };
This would grant rmt
the ability to write kernel memory, a
glaring security hole. Often the audit2allow
statements are only a
starting point. After employing these statements, you may need to change the
source domain and the label of the target, as well as incorporate proper
macros, to arrive at a good policy. Sometimes the denial being examined should
not result in any policy changes at all; rather the offending app
should be changed.