The Danger of Broad Permissions
You are debugging a system service crash. The logcat output shows an SELinux denial because your process cannot write to a specific file. Granting write access to the entire data directory feels like the fastest fix. You add a broad allow rule and the crash disappears.
But you just gave compromised processes a free pass to read user data. In a large project like Android, thousands of developers write policy concurrently. We need a way to stop dangerous permissions from merging into the codebase.
Android solves this using neverallow rules. While an allow rule grants access, a neverallow rule defines a strict boundary that no other rule can cross. Think of it as a compile-time assertion for your SELinux policy.
When you build the platform, the policy compiler reads every rule. If you attempt to add an allow rule that conflicts with an existing neverallow assertion, the compiler immediately halts the build. It refuses to generate a final policy file.
This hard stop forces developers to find precise, secure solutions. You cannot rely on broad workarounds when the system refuses to compile them. Catching bad rules on your local machine is only half the battle. We also have to prevent external partners from shipping bad policies.
Enforcing the Boundary
Device manufacturers routinely modify the AOSP SELinux policy for their specific hardware. We must guarantee that these vendor modifications do not weaken core platform security. Without external enforcement, a vendor could just delete the neverallow rules to bypass build errors.
Android enforces these rules through the checkpolicy compiler and the Compatibility Test Suite (CTS). These tools act as strict gatekeepers.
The flowchart below visualizes how the compiler evaluates policy files during a build. It helps clarify exactly where the compiler catches conflicts before a bad policy can ship.
Notice how the compiler acts as a funnel for all rules. It guarantees that any conflicting allow rule results in an immediate build failure rather than a compiled policy.
During a platform build, checkpolicy scans all local policy files. CTS extends this protection to physical devices by extracting their compiled policies. It then runs checkpolicy against the extracted rules using Google's core assertions. If a vendor policy violates a platform assertion, the device fails certification.
This dual-check system guarantees that bad rules cannot survive. A compromised policy will either break the local build or fail CTS. You will inevitably hit one of these build failures yourself.
Fixing a Policy Violation
You will eventually trigger a violation during a local build. The error output explicitly names the conflicting rules, showing your new allow rule alongside the core neverallow rule that rejected it. Your first instinct might be to modify the core assertion to bypass the error.
Unless you are refactoring core AOSP infrastructure, modifying Google's boundary rules is always the wrong approach. You must instead refine your target type.
The build log will highlight exactly which rule you violated. You will see an error similar to this when attempting to grant a media service broad write access.
neverallow mediaserver { system_data_file core_data_file_type }:file { write append };
Common Mistake: Engineers often try to dodge this error by changing the source domain of their process. This breaks other functionality and introduces new vulnerabilities. Always fix the permission scope, not the process identity.
When a service needs to read a file, do not grant access to a generic label like system_data_file. Create a highly specific type for your data. Label the file correctly and grant your service access only to that new type. If the policy structurally forbids direct access, route your request through an intermediary service that already holds the required permissions.
Fixing the permission scope solves most policy violations locally. But introducing entirely new hardware abstractions requires more than just refining labels. That situation forces you to write custom SELinux domains from scratch.