AOSP Expert & Production Engineering
3 min read

SELinux Debugging Mastery

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:

  1. Define the Context: Assign a context to the executable.
    /vendor/bin/hw/android.hardware.myhal-service u:object_r:hal_myhal_exec:s0
    
  2. Define the Domain: Create a .te file 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)
    
  3. 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.