How Android’s Binder Uses SELinux to Enforce Secure IPC Permissions
This article examines Android’s Binder inter‑process communication mechanism, detailing how SELinux integrates with the /dev/binder driver, the role of ServiceManager as ContextManager, the definition of binder‑related permissions, and the kernel‑level security hooks that enforce SEAndroid checks during binder transactions.
Introduction
Binder is Android’s native inter‑process communication (IPC) mechanism that operates through the /dev/binder device driver. Unlike shared memory, sockets, or pipes, Binder’s implementation is tightly coupled with the Linux Security Module (LSM) framework, allowing SELinux to control its permissions.
1. SELinux Overview
SELinux (Security‑Enhanced Linux) is a Mandatory Access Control (MAC) system built on LSM. Every kernel object receives a security context, and access decisions are based on type enforcement, Multi‑Level Security, and Role‑Based Access Control (RBAC). In Android, SELinux is referred to as SEAndroid.
The SELinux access flow can be visualized as follows:
2. Binder‑Related Permissions
In the kernel source common/security/selinux/include/classmap.h, Binder permissions are defined:
impersonate : Allows a process to masquerade as another when communicating.
call : Permission for a client to invoke a service.
set_context_mgr : Grants the ability to act as a ContextManager (used by ServiceManager and hwservicemanager).
transfer : Allows the transfer of Binder objects between processes.
Older Android versions gave every app the unconfined_domain with call, set_context_mgr and transfer permissions, which is no longer the case; modern builds require explicit permission declarations.
3. ServiceManager and ContextManager Permissions
ServiceManager holds a privileged position in the Binder ecosystem. Its SELinux rules are located in system/sepolicy/public/servicemanager.te and include:
The rules grant ServiceManager the ability to set itself as a ContextManager and to transfer Binder objects to a set of domains (init, vendor_init, hwservicemanager, etc.).
4. ServiceManager Startup Process
The native ServiceManager binary resides in frameworks/native/cmds/servicemanager/. Key source files are main.cpp, ServiceManager.cpp, and Access.cpp. The startup sequence is:
Open /dev/binder (driver).
Call becomeContextManager() to register as the ContextManager.
Enter an event loop waiting for client requests.
Relevant code snippets:
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
ps->becomeContextManager();The becomeContextManager() function (found in frameworks/native/libs/binder/ProcessState.cpp) creates a flat_binder_object with the flag FLAT_BINDER_FLAG_TXN_SECURITY_CTX and issues an ioctl to the driver.
5. The Access Class
The Access class encapsulates SELinux callbacks and permission checks. Its constructor registers a callback structure ( selinux_callback) and verifies that SELinux is enabled.
Two frequently used methods are: getCallingContext() – returns a CallingContext struct with pid, uid, and sid. canFind(), canAdd(), canList() – ultimately call actionAllowedFromLookup(), which uses selabel_lookup() and selinux_check_access() to decide if an operation is permitted.
Example of the core permission check:
int selinux_check_access(const char *scon, const char *tcon,
const char *class, const char *perm,
void *aux);6. Binder Transaction Flow
When a client invokes a remote method, the following steps occur:
A ProcessState object opens the binder driver and creates a thread pool.
Each thread holds an IPCThreadState instance that manages reads/writes. IPCThreadState::transact() (implemented in BpBinder.cpp) calls writeTransactionData(), which builds a binder_transaction_data structure containing one or more flat_binder_object entries.
The driver receives the data via ioctl(BINDER_WRITE_READ, &bwr), where bwr holds write and read buffers.
The service side reads the transaction, invokes onTransact(), and processes the request.
Key structures:
7. SEAndroid Permission Checks Within Binder Transactions
The kernel’s LSM hook security_binder_transfer_binder is invoked when a flat_binder_object of type BINDER, WEAK_BINDER, HANDLE, or WEAK_HANDLE is transferred. The hook list is populated via security_add_hooks and ultimately calls selinux_binder_transfer_binder, which performs:
Lookup of source and target security contexts.
Invocation of avc_has_perm to verify the BINDER__TRANSFER permission (class SECCLASS_BINDER).
Thus, every Binder object transfer is subject to a SELinux policy check before the kernel permits the operation.
8. Summary
Binder is the cornerstone of Android IPC, and SELinux (SEAndroid) provides a comprehensive permission framework that secures Binder interactions. ServiceManager, as the sole ContextManager, registers services and enforces set_context_mgr rights. Permission verification occurs deep in the kernel via LSM hooks, ensuring that only authorized processes can transfer Binder objects. The design is extensible: additional security hooks can be added to the security_hook_heads.binder_transfer_binder list, allowing vendors to implement custom checks beyond the default SELinux policy.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
OPPO Kernel Craftsman
Sharing Linux kernel-related cutting-edge technology, technical articles, technical news, and curated tutorials
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
