MagiskSU Request Flow: How Root Access Is Granted to Android Apps
Magisk grants root access through a Unix-domain socket IPC protocol where the su binary serializes a SuRequest to the Magisk daemon, which verifies caller credentials and SELinux contexts before optionally prompting the user and spawning a privileged process with setuid(0).
The MagiskSU request flow enables Android applications to elevate privileges securely without modifying system partitions. As implemented in the topjohnwu/Magisk open-source repository, this architecture separates privilege escalation into distinct client, daemon, and UI components that communicate via structured IPC messages. The flow ensures that every root request undergoes credential verification, policy database checks, and explicit user confirmation before executing commands as UID 0.
Step 1: Request Construction in the su Client
When an app invokes su, the client binary located at native/src/core/su/su.cpp initiates the request flow. The su_client_main function parses command-line arguments—including target UID, command strings (-c), and SELinux contexts—then constructs a SuRequest object via SuRequest::New().
The populated structure contains critical fields:
- Caller identity: UID, PID, and GID obtained from
getuid()andgetpid() - Target credentials: Desired user ID (default 0 for root) and supplementary groups
- Execution context: Command string, shell preference, and SELinux context (
context) - Capability flags:
drop_capfor capability bounding set reduction
After construction, the client serializes the request and transmits it to the Magisk daemon via send_request(RequestCode::SUPERUSER, …) over the MAGISKD_SOCKET Unix-domain socket. The native/src/core/su/connect.rs module handles this IPC client implementation, writing the binary-encoded request through the socket.
// Conceptual flow derived from native/src/core/su/su.cpp
SuRequest req = SuRequest::New();
req.target_uid = 0; // Request root UID
req.command = "id"; // Command to execute post-grant
req.context = "u:r:su:s0"; // SELinux context
send_request(RequestCode::SUPERUSER, &req);
Step 2: Daemon Reception and Credential Verification
The Magisk daemon, implemented in native/src/core/daemon.rs, listens for incoming connections in handle_requests(). Upon receiving a RequestCode::SUPERUSER message, it dispatches control to su_daemon_handler(client, cred), passing the Unix stream and peer credentials.
Inside the handler, the daemon performs critical security validations:
- Credential extraction: Captures the caller's UID, GID, and PID from the
UCredstructure - SELinux label inspection: Retrieves the peer's security context via
getsockopt(SO_PEERSEC)to verify the requesting domain - Policy database lookup: Queries the
SuInfodatabase (SuManager) to determine if the requesting package has pre-existing allow/deny rules
If the database contains no definitive rule for the calling UID, the daemon prepares an interactive authorization flow.
// High-level logic from native/src/core/daemon.rs su_daemon_handler
fn su_daemon_handler(&self, client: UnixStream, cred: UCred) {
let req: SuRequest = client.read_decodable().unwrap();
let secontext = getsockopt(&client, SO_PEERSEC).unwrap();
// Check SuManager database
match self.policy_lookup(cred.uid) {
Policy::Allow => self.spawn_root_process(&req),
Policy::Deny => client.send(RespondCode::DENIED),
Policy::Ask => self.prompt_user(&req, client),
}
}
Step 3: User Authorization via Superuser UI
When policy requires user confirmation, the daemon broadcasts a MAGISK_SU_ACTION intent to the Superuser management application. This triggers the UI component defined in app/core/src/main/res/layout/fragment_superuser_md2.xml, displaying a dialog with strings sourced from app/core/src/main/res/values/strings.xml (such as su_request_title).
The dialog presents:
- Requesting package name and UID
- Command to be executed
- Timeout settings (
request_timeout)
The daemon blocks on the socket awaiting the manager's response. When the user selects Allow or Deny, the app writes back either RespondCode::GRANTED or RespondCode::DENIED through the same socket connection. If granted, the flow continues to process spawning; if denied, the daemon returns a permission failure to the client.
Step 4: Privilege Escalation and Execution
Upon receiving RespondCode::GRANTED, the daemon executes the privilege transition in a forked process. The implementation applies layered security controls:
- Identity transition: Calls
setuid(target_uid)andsetgid()to assume the requested user identity (typically root/0) - Group assignment: Sets supplementary groups via
setgroups()if specified in the request - Capability bounding: Optionally drops capabilities using
prctl(PR_CAPBSET_DROP, …)whendrop_capis enabled - SELinux context: Applies the requested security domain via
setcon(req.context) - Execution: Replaces the process image with the requested command using
execve()
The su client in native/src/core/su/su.cpp waits for the daemon to complete this sequence, then relays the exit status of the executed command back to the original caller. If the request was denied at any stage, su exits with code 1 and outputs "Permission denied".
# Typical invocation triggering the full MagiskSU request flow
$ su -c "whoami"
# Daemon verifies credentials, prompts user, then outputs:
root
Summary
- MagiskSU request flow begins in
native/src/core/su/su.cppwhere the client constructs aSuRequestand transmits it via Unix-domain socket to the Magisk daemon. - The daemon in
native/src/core/daemon.rsvalidates caller credentials throughgetsockopt(SO_PEERSEC)and checks theSuInfopolicy database before determining if user interaction is required. - User authorization occurs through the Superuser UI app, which responds with
RespondCode::GRANTEDorRespondCode::DENIEDvia socket IPC. - Upon approval, the daemon spawns a privileged process using
setuid(0),setcon(), andexecve()to execute the requested command as root. - Denied requests result in immediate termination with exit code 1, ensuring unauthorized apps cannot bypass the permission model.
Frequently Asked Questions
How does Magisk verify the identity of apps requesting root access?
Magisk retrieves the caller's UID, PID, and SELinux security context directly from the socket connection using getsockopt(SO_PEERSEC) and the UCred structure passed to su_daemon_handler in native/src/core/daemon.rs. These kernel-provided credentials cannot be forged by the requesting application, ensuring the daemon authenticates the true identity of the caller before consulting the policy database.
What happens if the user is not present to approve a root request?
If the SuInfo database contains no pre-existing rule and the user does not respond to the Superuser prompt within the timeout period (defined in app/core/src/main/res/values/strings.xml as request_timeout), the daemon treats the lack of response as a denial. The su client receives RespondCode::DENIED and exits with code 1, preventing automatic grants during unattended operation.
Can apps bypass the MagiskSU request flow to obtain root silently?
No. The architecture requires the Magisk daemon (daemon.rs) to fork and execute the privileged process after explicit authorization. Without the daemon's spawn_root_process function performing the setuid(0) transition and execve() call, the app cannot obtain root capabilities. The Unix-domain socket IPC and policy checks in native/src/core/su/su.cpp ensure every request routes through the authenticated flow.
Where does Magisk store the allow/deny policies for root requests?
Magisk maintains the root policy database in the SuManager component, referenced as SuInfo in the daemon code. The su_daemon_handler function queries this SQLite-backed store—located in the Magisk internal data directory—to check if a specific UID has been whitelisted, blacklisted, or requires interactive prompting on each request.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →