Android Upgrades, Custom ROMs (LineageOS), & Kernels

Troubleshooting SELinux Permissive Bootloops: A Step-by-Step Fix for Custom Android Builds

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: The Crucial Role of SELinux in Android Security

Security-Enhanced Linux (SELinux) is a mandatory access control (MAC) system that plays a pivotal role in Android’s security architecture. It defines strict rules on what processes can access what resources (files, devices, network sockets, etc.). Unlike discretionary access control (DAC), where access is determined by file ownership and permissions, SELinux operates on labels or "contexts." Every process and every file object in Android has an associated SELinux context. When a custom Android build, such as a LineageOS derivative or a custom kernel, fails to boot and gets stuck in a "permissive bootloop," it almost invariably points to an improperly configured or missing SELinux policy.

A permissive bootloop occurs when critical system services, prevented from accessing necessary resources by SELinux, continuously crash and restart. While the device might technically boot to a point where the kernel is active, the Android system (zygote, system server, etc.) fails to initialize fully. In a permissive state, SELinux logs denials but doesn’t enforce them, allowing the system to potentially boot, albeit with security risks. However, if the policy is so broken that even basic file access for `init` or critical HALs is denied, the system can still fail to come up even in permissive mode, leading to a frustrating and hard-to-diagnose bootloop.

Understanding SELinux Contexts and AVC Denials

At the heart of SELinux troubleshooting are "contexts" and "Access Vector Cache (AVC) denials." Each Android component (an app, a service, a system process) runs within a specific SELinux domain (e.g., untrusted_app, system_server, init). Similarly, every file, directory, or device node has a type context (e.g., system_file, vendor_data_file, device). An AVC denial occurs when a subject (process in a domain) attempts an action (permission like read, write, execute) on an object (file with a type) that is not explicitly allowed by the loaded SELinux policy.

A typical AVC denial looks like this:

avc: denied { read } for pid=1234 comm="[email protected]" name="some_device" dev="tmpfs" ino=5678 scontext=u:r:hal_foo_server:s0 tcontext=u:object_r:some_device_type:s0 tclass=chr_file permissive=1

Let’s break it down:

  • avc: denied { read }: The requested permission was ‘read’.
  • pid=1234 comm="[email protected]": The process attempting the action.
  • name="some_device" dev="tmpfs" ino=5678: The object’s name and details.
  • scontext=u:r:hal_foo_server:s0: The source context (the domain of the process).
  • tcontext=u:object_r:some_device_type:s0: The target context (the type of the object).
  • tclass=chr_file: The class of the object (e.g., file, dir, chr_file, socket).
  • permissive=1: Indicates that SELinux is currently in permissive mode; the action was logged but not blocked. If it were `permissive=0`, the action would have been blocked.

Step-by-Step Fix for Permissive Bootloops

Step 1: Accessing Device Logs via Recovery

Since your device is bootlooping, you cannot access logs via a running Android system. You’ll need to boot into your custom recovery (e.g., TWRP) or a debug-enabled AOSP recovery that allows ADB access.

  1. Boot to Recovery: Power off your device. Press and hold the appropriate key combination (often Volume Down + Power or Volume Up + Power) to enter recovery mode.
  2. Enable ADB: Once in recovery, connect your device to your computer via USB. On your computer, open a terminal and run adb devices. You should see your device listed. If not, ensure ADB drivers are installed and ADB is enabled in recovery (usually on by default in custom recoveries).
  3. Pull Critical Logs: The most crucial logs for SELinux are found in the kernel ring buffer (`dmesg`) and potentially the audit log.Execute the following commands:
adb shell dmesg > dmesg.logadb pull /sys/fs/selinux/avc_cache ./avc_cache.log # Might not exist or be emptyadb pull /data/misc/audit/audit.log ./audit.log # Only if auditd is running and logs are flushed

Focus primarily on `dmesg.log` as it captures kernel-level SELinux denials from early boot. Open `dmesg.log` and search for "avc: denied" or "selinux."

Step 2: Identifying Critical Denials and Their Source

Carefully analyze the `dmesg.log` for AVC denials. When dealing with a bootloop, prioritize denials from critical system services that attempt to start early in the boot process. Look for processes like `init`, `ueventd`, `servicemanager`, `hwservicemanager`, and various HAL (Hardware Abstraction Layer) services (e.g., `[email protected]`).

Multiple denials might appear. Start with the earliest ones or those related to core system components. Sometimes, fixing one denial reveals another, cascading up the boot process.

Step 3: Crafting SELinux Policy Rules (.te files)

Based on the identified AVC denials, you need to write new SELinux policy rules. These rules are typically defined in `.te` (type enforcement) files within your device’s `sepolicy` directory (e.g., `device/<vendor>/<device>/sepolicy_vndr`).

Let’s use the example denial from before:

avc: denied { read } for pid=1234 comm="[email protected]" name="some_device" dev="tmpfs" ino=5678 scontext=u:r:hal_foo_server:s0 tcontext=u:object_r:some_device_type:s0 tclass=chr_file permissive=1

To fix this, you would add a rule that grants the `hal_foo_server` domain the `read` permission on objects of type `some_device_type` with class `chr_file`. Create or modify a `.te` file (e.g., `hal_foo_server.te`) in your custom policy directory:

# device/<vendor>/<device>/sepolicy_vndr/hal_foo_server.teallow hal_foo_server some_device_type:chr_file { read };

Here are some common types of rules you might need to add:

  • File/Directory Access:allow <source_domain> <target_type>:<class> { <permissions> }; (e.g., read, write, getattr, open, execute, search, add_name, create)
  • Process Execution:allow <source_domain> <target_domain>:process { <permissions> }; (e.g., transition, execmem, fork, sigkill)
  • Binder IPC:allow <source_domain> <target_domain>:binder { <permissions> }; (e.g., call, transfer)
  • Setting File Contexts: Sometimes, the issue isn’t a missing permission but an incorrect file context. If a file is created by one process but expected by another with a different context, you might need to add a `file_contexts` entry.

For file contexts, you might need to update a `file_contexts` file (e.g., `vendor_file_contexts`) in your sepolicy directory:

/vendor/bin/foo               u:object_r:vendor_foo_exec:s0/vendor/etc/foo.conf            u:object_r:vendor_foo_config_file:s0

These define the default context for files matching the given path.

Step 4: Compiling and Flashing the New SePolicy

After modifying your `.te` files, you need to compile the new policy and flash it to your device.

  1. Rebuild SePolicy: Navigate to the root of your Android source tree and rebuild your device’s `sepolicy`. The exact command depends on your build system. For most AOSP-based builds, it involves rebuilding `boot.img` or `vendor_boot.img` which encapsulates the `sepolicy`.
    . build/envsetup.shlunch <your_device_codename>-userdebug # or -user/engm <sepolicy_target> # e.g., m bootimage or m vendor_bootimage

    The `sepolicy` is usually part of `boot.img` for older Android versions or `vendor_boot.img` for devices supporting Generic System Image (GSI) and Project Treble (Android 8.0+).

    Ensure your `device.mk` or `BoardConfig.mk` correctly includes your custom `sepolicy` directories (e.g., `BOARD_SEPOLICY_DIRS`, `BOARD_VENDOR_SEPOLICY_DIRS`).

  2. Flash the Image: Once the new `boot.img` or `vendor_boot.img` is built (typically found in `out/target/product/<device>/`), you need to flash it to your device.

For `boot.img` (older devices/non-Treble):

adb reboot bootloaderfastboot flash boot <path_to_boot.img>fastboot reboot

For `vendor_boot.img` (Treble-enabled devices, Android 10+):

adb reboot bootloaderfastboot flash vendor_boot <path_to_vendor_boot.img>fastboot reboot

After flashing, reboot the device and monitor its behavior. If it still bootloops, repeat the process: pull logs, identify new denials, craft new rules, and re-flash. It’s often an iterative process.

Best Practices and Common Pitfalls

  • Specificity: Always aim for the most specific SELinux rules possible. Overly broad rules (e.g., allowing a domain to `read` any `file`) undermine security and can mask underlying issues.
  • Incremental Changes: Add one or a few rules at a time. This makes it easier to pinpoint which rule fixed which denial and avoids introducing new regressions.
  • Understanding Contexts: Ensure you correctly identify the source and target contexts from the AVC denials. Misidentifying these will lead to ineffective rules.
  • File Contexts: Sometimes, processes might fail because a file or directory has the wrong SELinux context. Use `ls -Z` (from ADB shell after a partial boot or from recovery) to inspect file contexts. If they are incorrect, you might need to fix them using `restorecon -Rv /path/to/files` (if the policy defines the correct context for the path) or by adding/modifying `file_contexts` entries.
  • `neverallow` Rules: Be aware that your device’s base policy or AOSP `neverallow` rules might conflict with your custom rules. These `neverallow` rules cannot be overridden and will prevent your policy from compiling.

Conclusion

Troubleshooting SELinux permissive bootloops in custom Android builds requires a systematic approach, a solid understanding of SELinux fundamentals, and patience. By diligently analyzing AVC denials from `dmesg`, crafting precise policy rules, and iteratively building and flashing your `sepolicy`, you can overcome these security-related boot issues and achieve a fully functional, secure custom Android experience. Remember, a robust SELinux policy is not just about getting the device to boot; it’s about ensuring the integrity and security of your custom operating system.

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner