How Magisk Handles Mount Namespaces: Global, Requester, and Isolate Modes Explained

Magisk isolates root-privileged processes by running them inside a mount namespace, offering three distinct modes—Global, Requester, and Isolate—that control whether the root session shares the system's init namespace, inherits the requesting app's namespace, or creates a completely private mount view.

The topjohnwu/Magisk repository implements this namespace isolation in its native core to balance security, compatibility, and user flexibility. Understanding how Magisk handles mount namespaces requires examining the enum definitions in Rust, the database persistence layer, and the C++ daemon logic that executes the namespace switches.

The Three Mount Namespace Modes

Magisk defines three mount namespace behaviors via the MntNsMode enum in native/src/core/lib.rs (lines 93-97). The default mode is Requester (MntNsMode::Requester), as specified in the Default implementation at lines 87-91.

Global Mode

In Global mode, the root session enters the global mount namespace shared by the init process. This provides the same mount view seen by the core system, including the real /system layout and all system-wide mounts.

This mode is activated when the user passes the -M or --mount-master flag to the su command. According to the source in native/src/core/su/su.cpp (lines 52-71), this sets the target PID to 0, which exec_root_shell() interprets as a request for the global namespace.

Requester Mode

Requester mode is the default behavior. The root session inherits the mount namespace of the process that requested root access (the PID that invoked su).

When parsing command-line arguments in su.cpp, if the -t <PID> flag is provided, the daemon uses that specific PID's namespace. Otherwise, it defaults to the caller's PID. The daemon logs this with LOGD("su: use namespace of pid=[%d]\n", req.target_pid) before calling switch_mnt_ns(req.target_pid) to enter the target namespace.

Isolate Mode

Isolate mode creates a brand-new private mount namespace detached from the rest of the system. This provides the strongest isolation, ensuring the root session cannot see mounts from other apps or processes.

When this mode is selected (either via Magisk Settings or forced under specific conditions), exec_root_shell() performs a sequence of operations:

  1. Enters the requester's namespace via switch_mnt_ns()
  2. Calls xunshare(CLONE_NEWNS) to create a fresh namespace
  3. Executes xmount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr) to make the new namespace private and prevent mount propagation

Namespace Configuration Storage

Magisk persists the user's preferred mount namespace mode in its internal SQLite database. The setting is stored under the key mnt_ns (represented as DbEntryKey::SuMntNs in the Rust code).

The database interactions are handled in native/src/core/db.rs (lines 48-55). The daemon reads the current mode via MagiskD::get_db_setting() and writes updates through set_db_setting(). This allows the mount namespace behavior to survive daemon restarts and persist across reboots.

Command-Line Interface and Request Parsing

The su binary serves as the command-line interface for namespace selection. Located at native/src/core/su/su.cpp, the parser translates user flags into SuRequest fields:

Option Namespace Effect
-M or --mount-master Forces Global mode (uses init's namespace)
-t <PID> Forces Requester mode for the specific PID
No flag (with Isolate setting) Creates a new Isolate namespace

The exec_root_shell() function processes these requests with logic that handles edge cases. For example, if -M (Global) is combined with -t (explicit PID), the daemon falls back to Requester mode rather than using the global namespace.

Daemon Implementation and Namespace Switching

The actual namespace transition occurs in exec_root_shell() within native/src/core/su/su.cpp. This function implements a switch statement that handles each MntNsMode variant:

switch (mode) {
    case MntNsMode::Global:
        LOGD("su: use global namespace\n");
        break;
    case MntNsMode::Requester:
        LOGD("su: use namespace of pid=[%d]\n", req.target_pid);
        switch_mnt_ns(req.target_pid);
        break;
    case MntNsMode::Isolate:
        LOGD("su: use new isolated namespace\n");
        switch_mnt_ns(req.target_pid);
        xunshare(CLONE_NEWNS);
        xmount(nullptr, "/", nullptr,
               MS_PRIVATE | MS_REC, nullptr);
        break;
}

The low-level namespace switching is performed by switch_mnt_ns(int pid), defined in native/src/base/base.cpp (lines 89-106). This function implements a robust fallback mechanism:

  1. First attempts modern kernel APIs: pidfd_open() followed by setns()
  2. Falls back to the legacy method: opening /proc/<pid>/ns/mnt and calling setns() on the resulting file descriptor

This ensures compatibility across Android kernel versions while maintaining the ability to enter arbitrary process namespaces.

Practical Usage Examples

Here are concrete examples demonstrating each mount namespace mode:


# Default Requester mode - inherits the calling app's namespace

$ su

# Global mode - shares the init process mount namespace

$ su -M

# Explicitly enter the namespace of a specific process (PID 1234)

$ su -t 1234

# Isolate mode - creates a private namespace (when configured in settings)

$ su

In Global mode, tools like mount show the complete system mount tree identical to the init process view. In Requester mode, the shell sees the same mounts as the requesting application, preserving app-specific storage views. In Isolate mode, the shell sees only the base rootfs mounts with no visibility into other process mounts.

Summary

  • Magisk implements three mount namespace modes—Global, Requester, and Isolate—defined in native/src/core/lib.rs as the MntNsMode enum.
  • The default Requester mode inherits the namespace of the calling PID, while Global (-M flag) uses the init namespace and Isolate creates a private namespace.
  • Configuration persists in the SQLite database via DbEntryKey::SuMntNs, handled in native/src/core/db.rs.
  • The su daemon processes namespace requests in exec_root_shell() and executes switches via switch_mnt_ns() in native/src/base/base.cpp, using pidfd_open with a /proc fallback.

Frequently Asked Questions

What is the default mount namespace mode in Magisk?

The default mode is Requester (MntNsMode::Requester), as implemented in the Default trait for the enum in native/src/core/lib.rs. This means that unless you specify otherwise with -M or the Magisk Settings are changed, every root session inherits the mount namespace of the application that called su.

When should I use Global mount namespace mode?

Use Global mode (activated with su -M) when you need access to the system's true mount layout, such as when running tools that depend on the real /system partition structure or when debugging system-level mount issues. This mode places your shell in the same namespace as the init process, bypassing any app-specific mount views.

How does Magisk handle namespace switching on older Android kernels?

The switch_mnt_ns() function in native/src/base/base.cpp implements a dual-path approach. It first attempts to use the modern pidfd_open() syscall combined with setns(). If the kernel lacks pidfd support, it automatically falls back to opening /proc/<pid>/ns/mnt directly and calling setns() on that file descriptor, ensuring compatibility with older Android versions.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →