SELinux Debugging Mastery
Security-Enhanced Linux (SELinux) implements Mandatory Access Control (MAC) in Android. It restricts access to files, sockets, and properties based on explicit security contexts, regardless of standard Linux Discretionary Access Control (DAC) permissions.
Reading AVC Denials
When SELinux blocks an action, it logs an Access Vector Cache (AVC) denial. These are visible in dmesg or logcat.
A typical denial looks like this:
avc: denied { read } for pid=1234 comm="surfaceflinger" name="config.txt" dev="sda1" ino=5678 scontext=u:r:surfaceflinger:s0 tcontext=u:object_r:vendor_configs_file:s0 tclass=file permissive=0
Breaking down the denial:
- Action:
{ read }(What was attempted) - Subject (
scontext):u:r:surfaceflinger:s0(Who attempted it) - Target (
tcontext):u:object_r:vendor_configs_file:s0(What was accessed) - Class (
tclass):file(The object type) - Status:
permissive=0(0 means blocked, 1 means logged but allowed)
Writing Correct Allow Rules
To resolve the denial, you must write an SELinux policy rule. The tool audit2allow can automate this, but manual writing is highly recommended to prevent overly broad permissions.
The correct rule for the above denial is:
allow surfaceflinger vendor_configs_file:file { read open getattr };
Note: If a process needs read, it almost always needs open and getattr as well.
The neverallow Compliance Verification
Android enforces strict architectural boundaries using neverallow rules. For example, a third-party app should never access hardware device nodes directly.
If you attempt to compile a policy that violates a neverallow rule, the build will fail during m selinux_policy.
libsepol.report_failure: neverallow on line 450 of system/sepolicy/public/domain.te violated by allow surfaceflinger block_device:blk_file { read };
You cannot simply remove the neverallow (doing so will fail the Android Compatibility Test Suite (CTS)). Instead, you must redesign the architecture. If surfaceflinger needs data from a block device, it must request it via a HAL over Binder, rather than reading the file directly.
SELinux in HAL Bring-up
When creating a new Hardware Abstraction Layer (HAL), SELinux requires several steps:
- Define the Context: Assign a context to the executable.
/vendor/bin/hw/android.hardware.myhal-service u:object_r:hal_myhal_exec:s0 - Define the Domain: Create a
.tefile to transition the executable into its own domain.type hal_myhal, domain; type hal_myhal_exec, exec_type, vendor_file_type, file_type; init_daemon_domain(hal_myhal) - Grant Binder Access: Allow the HAL to register with the Binder
servicemanager.
During early bring-up, it is common to set the device to permissive mode to collect all denials at once:
adb shell setenforce 0
adb logcat | grep avc
Crucially, devices must ship in Enforcing mode (setenforce 1) to pass Google certification.