# MagiskSU Request Flow: How Root Access Is Granted to Android Apps

> Understand the MagiskSU request flow for granting root access to Android apps. Learn how Magisk secures privileged operations through IPC and user prompts.

- Repository: [John Wu/Magisk](https://github.com/topjohnwu/Magisk)
- Tags: internals
- Published: 2026-03-05

---

**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`](https://github.com/topjohnwu/Magisk/blob/main/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()` and `getpid()`
- **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_cap` for 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`](https://github.com/topjohnwu/Magisk/blob/main/native/src/core/su/connect.rs) module handles this IPC client implementation, writing the binary-encoded request through the socket.

```cpp
// 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`](https://github.com/topjohnwu/Magisk/blob/main/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:

1. **Credential extraction**: Captures the caller's UID, GID, and PID from the `UCred` structure
2. **SELinux label inspection**: Retrieves the peer's security context via `getsockopt(SO_PEERSEC)` to verify the requesting domain
3. **Policy database lookup**: Queries the `SuInfo` database (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.

```rust
// 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`](https://github.com/topjohnwu/Magisk/blob/main/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`](https://github.com/topjohnwu/Magisk/blob/main/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:

1. **Identity transition**: Calls `setuid(target_uid)` and `setgid()` to assume the requested user identity (typically root/0)
2. **Group assignment**: Sets supplementary groups via `setgroups()` if specified in the request
3. **Capability bounding**: Optionally drops capabilities using `prctl(PR_CAPBSET_DROP, …)` when `drop_cap` is enabled
4. **SELinux context**: Applies the requested security domain via `setcon(req.context)`
5. **Execution**: Replaces the process image with the requested command using `execve()`

The `su` client in [`native/src/core/su/su.cpp`](https://github.com/topjohnwu/Magisk/blob/main/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".

```bash

# 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.cpp`](https://github.com/topjohnwu/Magisk/blob/main/native/src/core/su/su.cpp) where the client constructs a `SuRequest` and transmits it via Unix-domain socket to the Magisk daemon.
- The daemon in [`native/src/core/daemon.rs`](https://github.com/topjohnwu/Magisk/blob/main/native/src/core/daemon.rs) validates caller credentials through `getsockopt(SO_PEERSEC)` and checks the `SuInfo` policy database before determining if user interaction is required.
- User authorization occurs through the Superuser UI app, which responds with `RespondCode::GRANTED` or `RespondCode::DENIED` via socket IPC.
- Upon approval, the daemon spawns a privileged process using `setuid(0)`, `setcon()`, and `execve()` 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`](https://github.com/topjohnwu/Magisk/blob/main/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`](https://github.com/topjohnwu/Magisk/blob/main/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`](https://github.com/topjohnwu/Magisk/blob/main/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`](https://github.com/topjohnwu/Magisk/blob/main/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.