Robert Sesek | e63d873 | 2024-02-02 22:10:01 | [diff] [blame] | 1 | # Chrome Android Sandbox Design |
| 2 | |
| 3 | This document discusses the sandbox and process architecture of Chrome on |
| 4 | Android. Like Chrome's desktop platforms, Android uses a sandbox to isolate and |
| 5 | de-privilege untrustworthy computing principals. However, the overall |
| 6 | architecture is materially different than on desktop platforms, while still |
| 7 | arriving close to the same end result. |
| 8 | |
| 9 | ## Overall Architecture |
| 10 | |
| 11 | The Chrome Android browser process is the main trusted principal in the system, |
| 12 | it manages and communicates with a network of processes over inter-process |
| 13 | communication (IPC). The major features of the security architecture are: |
| 14 | |
| 15 | - The browser process is protected by the [Android app |
| 16 | sandbox](https://source.android.com/docs/security/app-sandbox), which isolates |
| 17 | each installed application on the system. |
| 18 | - Chrome launches its helper processes as Android |
| 19 | [Services](https://developer.android.com/reference/android/app/Service). |
| 20 | - For sandboxed helper processes, the sandbox follows a similar bilayer design |
| 21 | as the desktop Linux sandbox. The layer-one sandbox is provided by the |
| 22 | [`android:isolatedProcess`](https://developer.android.com/guide/topics/manifest/service-element#isolated) |
| 23 | attribute on the Service's manifest definition. |
| 24 | - Chrome [applies](https://source.chromium.org/chromium/chromium/src/+/main:sandbox/linux/seccomp-bpf-helpers/seccomp_starter_android.h) |
| 25 | a layer-two sandbox in the form of a Seccomp-BPF system call filter. |
| 26 | |
| 27 |  |
| 28 | |
| 29 | <!-- Source: https://docs.google.com/drawings/d/11oVsYx_TCrPMglMMW009IFFCStPrLPFbAIVNPp647Lw/edit --> |
| 30 | |
| 31 | ## Processes |
| 32 | |
| 33 | The below sections discuss three IPC transport mechanisms: Binder, UNIX sockets, |
| 34 | and Mojo. Binder is Android's primary IPC mechanism, implemented with a kernel |
| 35 | driver and a user `ioctl` client. UNIX socket IPC is used between certain |
| 36 | Android OS components, and it underpins Chrome's Mojo IPC mechanism. [Mojo |
| 37 | IPC](../../mojo/README.md) is the main IPC system used in Chrome to communicate |
| 38 | between components. |
| 39 | |
| 40 | ### Browser Process |
| 41 | |
| 42 | The Chrome browser process on Android is unique compared to the security |
| 43 | architecture of Chrome on other platforms. On desktop systems, all applications |
| 44 | typically run as the same user principal (UID), and each application often has |
| 45 | the same view of the global filesystem and equal access to the files within. |
| 46 | |
| 47 | On Android, each installed application is given a distinct user ID (UID) and a |
| 48 | restricted, scoped storage location. Applications only interact with each other |
| 49 | using the high-level |
| 50 | [Intents](https://developer.android.com/guide/components/intents-filters) IPC |
| 51 | system. |
| 52 | |
| 53 | The implications of this are that Chrome on Android should generally be |
| 54 | less trusting of data coming from other apps. Similarly, Chrome should either |
| 55 | trust or take care to sanitize data it sends to other apps. For more details see |
| 56 | [Android IPC Security Considerations](android-ipc.md). |
| 57 | |
| 58 | ### Helper Processes |
| 59 | |
| 60 | Chrome uses helper processes to segment and isolate the work it performs on |
| 61 | behalf of websites and the user. On desktop platforms, Chrome's browser process |
| 62 | has a parent-child relationship to the helper processes. But on Android, all |
| 63 | process creation goes through the Activity Manager and a zygote. The entrypoint |
| 64 | in Chrome for these processes is the |
| 65 | [`ContentChildProcessService`](https://source.chromium.org/chromium/chromium/src/+/main:content/public/android/java/src/org/chromium/content/app/ContentChildProcessService.java;drc=4e1b7bc33d42b401d7d9ad1dcba72883add3e2af), |
| 66 | which has both privileged (un-sandboxed) and sandboxed subclass variants. |
| 67 | |
| 68 | The zygote architecture is similar to the [one used on |
| 69 | Linux](../linux/zygote.md), but the Android OS uses it for all processes on the |
| 70 | system. This design exists because starting the Android JVM from scratch in each |
| 71 | new process would be expensive, and the zygote enables cloning the running JVM |
| 72 | to improve performance and reduce memory usage by sharing non-dirty memory |
| 73 | pages. |
| 74 | |
| 75 | A consequence of this design is that process creation on Android is more |
| 76 | involved than on other operating systems. Rather than a system call (or two, |
| 77 | e.g. `fork()` + `execve()`), process creation in Android involves several IPCs |
| 78 | through the system: |
| 79 | |
| 80 | 1. Browser sends a Binder IPC to Activity Manager for [`Context.bindService()`](https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.Context.BindServiceFlags,%20java.util.concurrent.Executor,%20android.content.ServiceConnection)) |
| 81 | 2. Activity Manager sends the zygote an IPC over a privileged UNIX socket to fork a process |
| 82 | 3. Zygote responds to Activity Manager with the forked process ID |
| 83 | 4. New process sends a Binder IPC to Activity Manager to connect with it |
| 84 | 5. Activity Manager sends a Binder IPC to the new process with information about the APK to load |
| 85 | 6. Activity Manager sends a Binder IPC channel connecting the new process to the browser process |
| 86 | process for [`ServiceConnection.onServiceConnected`](https://developer.android.com/reference/android/content/ServiceConnection#onServiceConnected(android.content.ComponentName,%20android.os.IBinder)) |
| 87 | |
| 88 | In Chrome, after the process is created, the browser process sends a file |
| 89 | descriptor to the new process over Binder to establish Chrome's Mojo IPC. From |
| 90 | then on, Chrome primarily uses Mojo IPC between the browser and the helper |
| 91 | process. The Binder IPC channel is retained in order to control the lifetime of |
| 92 | the helper process. |
| 93 | |
| 94 | ### Zygote |
| 95 | |
| 96 | Starting with Android API v29, the OS offers applications the ability to create |
| 97 | per-app zygotes. An `isolatedProcess <service>` can be declared with |
| 98 | [`android:useAppZygote`](https://developer.android.com/reference/android/R.styleable#AndroidManifestService_useAppZygote) |
| 99 | and provide a [`ZygotePreload`](https://developer.android.com/reference/android/app/ZygotePreload) |
| 100 | implementation. This instructs the OS to spawn a new zygote, from the system |
| 101 | zygote, that will be used to launch all instances of that Service. By giving the |
| 102 | application the ability to participate via `ZygotePreload`, the application can |
| 103 | acquire resources and perform initialization that is common to all future |
| 104 | processes. This extends the benefits of lower initialization time and greater |
| 105 | memory sharing from the system zygote to individual applications. |
| 106 | |
| 107 | ## Sandboxing |
| 108 | |
| 109 | Similar to the [Linux sandbox design](../linux/sandboxing.md), which uses a |
| 110 | bilayer sandbox, Chrome on Android also uses two technologies to secure |
| 111 | low-privilege processes. |
| 112 | |
| 113 | ### SELinux |
| 114 | |
| 115 | The Android OS applies an [SELinux policy](https://source.android.com/docs/security/features/selinux) |
| 116 | to every process on the system. A full explanation of SELinux is outside the |
| 117 | scope of this document, but at a high-level it is a [mandatory access control |
| 118 | mechanism](https://csrc.nist.gov/glossary/term/mandatory_access_control) that |
| 119 | enforces a capability sandbox. Every object represented in the kernel has an |
| 120 | associated security label, and the system policy specifies what subjects can do |
| 121 | to objects. For Chrome, SELinux acts as the layer-one sandbox. |
| 122 | |
| 123 | System services have per-service policies, but applications and their components |
| 124 | are usually assigned to one of three domains: |
| 125 | |
| 126 | - [**untrusted_app**](https://cs.android.com/android/platform/superproject/main/+/main:system/sepolicy/private/untrusted_app.te;drc=4aad91d920ad42a7374e7bbd4cb9a50de4e85efb) |
| 127 | is the default policy for most user-installed apps |
| 128 | - [**priv_app**](https://source.android.com/docs/core/permissions/perms-allowlist) |
| 129 | is the used for system applications that require powerful capabilities (e.g. |
| 130 | backup/restore) |
| 131 | - [**isolated_app**](https://cs.android.com/android/platform/superproject/main/+/main:system/sepolicy/private/isolated_app.te;drc=941ba723baceac19151560e8a1d2830b9be6493c) |
| 132 | is a restrictive sandbox that can be applied via the `<service |
| 133 | android:isolatedProcess="true">` tag in an application's manifest |
| 134 | |
| 135 | In Chrome, the browser process runs under the **untrusted_app** SELinux domain, |
| 136 | which enforces separation between distinct apps on the system. |
| 137 | |
| 138 | Chrome's sandboxed helper processes (e.g. renderers, utility processes, |
| 139 | sandboxed Mojo services) run under **isolated_app**. When the Android OS starts |
| 140 | an **isolated_app** process, it also assigns the process to an ephemeral UID, to |
| 141 | separate it from other POSIX security principals. The **isolated_app** SELinux |
| 142 | domain prevents the process from accessing virtually all system services, |
| 143 | devices, the network, and most of the filesystem. |
| 144 | |
| 145 | Chrome's un-sandboxed helper processes (e.g. GPU, un-sandboxed Mojo services) |
| 146 | run under **untrusted_app** and the same UID as the browser process. This means |
| 147 | there is no privilege separation between un-sandboxed helper processes and the |
| 148 | browser process. |
| 149 | |
| 150 | A unique difference with Android compared to desktop platforms is that the |
| 151 | operating system controls the sandbox policy (as a result of it being a |
| 152 | mandatory access control mechanism). This means that Chrome cannot modify the |
| 153 | policy, nor create gradations of sandboxes as can be done on other operating |
| 154 | systems. A process on Android runs at either the same security principal as the |
| 155 | browser, or in the tightly sandboxed **isolated_app** domain. |
| 156 | |
| 157 | ### Seccomp-BPF |
| 158 | |
| 159 | [Seccomp-BPF](https://www.kernel.org/doc/html/v4.19/userspace-api/seccomp_filter.html) |
| 160 | is the second layer of the sandbox, which performs kernel attack surface |
| 161 | reduction. By removing system calls, or filtering specific arguments of system |
| 162 | calls, some of the complexity provided by the Linux kernel can be walled off |
| 163 | from a low-privilege process. |
| 164 | |
| 165 | The [policy applied to |
Andrew Mitchell | da2d50ea | 2024-04-15 11:09:25 | [diff] [blame] | 166 | Android](https://source.chromium.org/chromium/chromium/src/+/main:sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.h;l=25;drc=f18d3489a59d34c92a8d96ef6a7e7279198a8ec6) |
Robert Sesek | e63d873 | 2024-02-02 22:10:01 | [diff] [blame] | 167 | is based on the desktop Linux policy. But, additional system calls are permitted |
| 168 | compared to desktop Linux. The major differences are to allow the JVM to |
| 169 | function properly and to account for differences in Bionic (Android Libc) vs |
| 170 | Glibc (standard Linux). |
| 171 | |
| 172 | Additionally, the Android OS applies a [seccomp-bpf filter to all |
| 173 | applications](https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/seccomp/seccomp_policy.cpp;l=305;drc=704772bda034448165d071f68b6aeca716f4220e). |
| 174 | This policy is necessarily looser (since it applies to all applications) than |
| 175 | the one applied by Chrome. But, seccomp-bpf policies stack and can only be made |
| 176 | more restrictive. |