| Dan Willemsen | 269a8c7 | 2017-05-03 17:15:47 -0700 | [diff] [blame] | 1 | // Copyright 2017 Google Inc. All rights reserved. | 
|  | 2 | // | 
|  | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 4 | // you may not use this file except in compliance with the License. | 
|  | 5 | // You may obtain a copy of the License at | 
|  | 6 | // | 
|  | 7 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 8 | // | 
|  | 9 | // Unless required by applicable law or agreed to in writing, software | 
|  | 10 | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 12 | // See the License for the specific language governing permissions and | 
|  | 13 | // limitations under the License. | 
|  | 14 |  | 
|  | 15 | package build | 
|  | 16 |  | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 17 | import ( | 
|  | 18 | "bytes" | 
|  | 19 | "os" | 
|  | 20 | "os/exec" | 
|  | 21 | "os/user" | 
| Diego Wilson | 10e564a | 2020-04-15 17:42:59 +0000 | [diff] [blame] | 22 | "path/filepath" | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 23 | "strings" | 
|  | 24 | "sync" | 
| Dan Willemsen | 269a8c7 | 2017-05-03 17:15:47 -0700 | [diff] [blame] | 25 | ) | 
|  | 26 |  | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 27 | type Sandbox struct { | 
|  | 28 | Enabled              bool | 
|  | 29 | DisableWhenUsingGoma bool | 
| Dan Willemsen | 25e6f09 | 2019-04-09 10:22:43 -0700 | [diff] [blame] | 30 |  | 
|  | 31 | AllowBuildBrokenUsesNetwork bool | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 32 | } | 
|  | 33 |  | 
|  | 34 | var ( | 
|  | 35 | noSandbox    = Sandbox{} | 
|  | 36 | basicSandbox = Sandbox{ | 
|  | 37 | Enabled: true, | 
|  | 38 | } | 
|  | 39 |  | 
|  | 40 | dumpvarsSandbox = basicSandbox | 
|  | 41 | katiSandbox     = basicSandbox | 
|  | 42 | soongSandbox    = basicSandbox | 
|  | 43 | ninjaSandbox    = Sandbox{ | 
|  | 44 | Enabled:              true, | 
|  | 45 | DisableWhenUsingGoma: true, | 
| Dan Willemsen | 25e6f09 | 2019-04-09 10:22:43 -0700 | [diff] [blame] | 46 |  | 
|  | 47 | AllowBuildBrokenUsesNetwork: true, | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 48 | } | 
|  | 49 | ) | 
|  | 50 |  | 
|  | 51 | const nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail" | 
|  | 52 |  | 
|  | 53 | var sandboxConfig struct { | 
|  | 54 | once sync.Once | 
|  | 55 |  | 
|  | 56 | working bool | 
|  | 57 | group   string | 
| Diego Wilson | a22240b | 2020-04-02 18:11:28 +0000 | [diff] [blame] | 58 | srcDir  string | 
|  | 59 | outDir  string | 
|  | 60 | distDir string | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 61 | } | 
|  | 62 |  | 
| Dan Willemsen | 269a8c7 | 2017-05-03 17:15:47 -0700 | [diff] [blame] | 63 | func (c *Cmd) sandboxSupported() bool { | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 64 | if !c.Sandbox.Enabled { | 
|  | 65 | return false | 
|  | 66 | } | 
|  | 67 |  | 
|  | 68 | // Goma is incompatible with PID namespaces and Mount namespaces. b/122767582 | 
|  | 69 | if c.Sandbox.DisableWhenUsingGoma && c.config.UseGoma() { | 
|  | 70 | return false | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 | sandboxConfig.once.Do(func() { | 
|  | 74 | sandboxConfig.group = "nogroup" | 
|  | 75 | if _, err := user.LookupGroup(sandboxConfig.group); err != nil { | 
|  | 76 | sandboxConfig.group = "nobody" | 
|  | 77 | } | 
|  | 78 |  | 
| Diego Wilson | 10e564a | 2020-04-15 17:42:59 +0000 | [diff] [blame] | 79 | // These directories will be bind mounted | 
|  | 80 | // so we need full non-symlink paths | 
| Diego Wilson | a22240b | 2020-04-02 18:11:28 +0000 | [diff] [blame] | 81 | sandboxConfig.srcDir = absPath(c.ctx, ".") | 
| Diego Wilson | 10e564a | 2020-04-15 17:42:59 +0000 | [diff] [blame] | 82 | if derefPath, err := filepath.EvalSymlinks(sandboxConfig.srcDir); err == nil { | 
|  | 83 | sandboxConfig.srcDir = absPath(c.ctx, derefPath) | 
|  | 84 | } | 
| Diego Wilson | a22240b | 2020-04-02 18:11:28 +0000 | [diff] [blame] | 85 | sandboxConfig.outDir = absPath(c.ctx, c.config.OutDir()) | 
| Diego Wilson | 10e564a | 2020-04-15 17:42:59 +0000 | [diff] [blame] | 86 | if derefPath, err := filepath.EvalSymlinks(sandboxConfig.outDir); err == nil { | 
|  | 87 | sandboxConfig.outDir = absPath(c.ctx, derefPath) | 
|  | 88 | } | 
| Diego Wilson | a22240b | 2020-04-02 18:11:28 +0000 | [diff] [blame] | 89 | sandboxConfig.distDir = absPath(c.ctx, c.config.DistDir()) | 
| Diego Wilson | 10e564a | 2020-04-15 17:42:59 +0000 | [diff] [blame] | 90 | if derefPath, err := filepath.EvalSymlinks(sandboxConfig.distDir); err == nil { | 
|  | 91 | sandboxConfig.distDir = absPath(c.ctx, derefPath) | 
|  | 92 | } | 
| Diego Wilson | a22240b | 2020-04-02 18:11:28 +0000 | [diff] [blame] | 93 |  | 
| Diego Wilson | a5d9653 | 2020-04-06 22:02:38 +0000 | [diff] [blame] | 94 | sandboxArgs := []string{ | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 95 | "-H", "android-build", | 
|  | 96 | "-e", | 
|  | 97 | "-u", "nobody", | 
|  | 98 | "-g", sandboxConfig.group, | 
| Diego Wilson | a22240b | 2020-04-02 18:11:28 +0000 | [diff] [blame] | 99 | "-R", "/", | 
| Spandan Das | 0506361 | 2021-06-25 01:39:04 +0000 | [diff] [blame] | 100 | // Mount tmp before srcDir | 
|  | 101 | // srcDir is /tmp/.* in integration tests, which is a child dir of /tmp | 
|  | 102 | // nsjail throws an error if a child dir is mounted before its parent | 
| Diego Wilson | a22240b | 2020-04-02 18:11:28 +0000 | [diff] [blame] | 103 | "-B", "/tmp", | 
| Spandan Das | 2d99704 | 2022-11-04 20:58:18 +0000 | [diff] [blame] | 104 | c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir, | 
| Diego Wilson | a22240b | 2020-04-02 18:11:28 +0000 | [diff] [blame] | 105 | "-B", sandboxConfig.outDir, | 
| Diego Wilson | a5d9653 | 2020-04-06 22:02:38 +0000 | [diff] [blame] | 106 | } | 
|  | 107 |  | 
|  | 108 | if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) { | 
|  | 109 | //Mount dist dir as read-write if it already exists | 
|  | 110 | sandboxArgs = append(sandboxArgs, "-B", | 
|  | 111 | sandboxConfig.distDir) | 
|  | 112 | } | 
|  | 113 |  | 
|  | 114 | sandboxArgs = append(sandboxArgs, | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 115 | "--disable_clone_newcgroup", | 
|  | 116 | "--", | 
|  | 117 | "/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`) | 
| Diego Wilson | a5d9653 | 2020-04-06 22:02:38 +0000 | [diff] [blame] | 118 |  | 
|  | 119 | cmd := exec.CommandContext(c.ctx.Context, nsjailPath, sandboxArgs...) | 
|  | 120 |  | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 121 | cmd.Env = c.config.Environment().Environ() | 
|  | 122 |  | 
|  | 123 | c.ctx.Verboseln(cmd.Args) | 
|  | 124 | data, err := cmd.CombinedOutput() | 
|  | 125 | if err == nil && bytes.Contains(data, []byte("Android Success")) { | 
|  | 126 | sandboxConfig.working = true | 
|  | 127 | return | 
|  | 128 | } | 
|  | 129 |  | 
| Dan Willemsen | 1871d88 | 2020-03-02 20:36:04 +0000 | [diff] [blame] | 130 | c.ctx.Println("Build sandboxing disabled due to nsjail error.") | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 131 |  | 
|  | 132 | for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") { | 
|  | 133 | c.ctx.Verboseln(line) | 
|  | 134 | } | 
|  | 135 |  | 
|  | 136 | if err == nil { | 
|  | 137 | c.ctx.Verboseln("nsjail exited successfully, but without the correct output") | 
|  | 138 | } else if e, ok := err.(*exec.ExitError); ok { | 
|  | 139 | c.ctx.Verbosef("nsjail failed with %v", e.ProcessState.String()) | 
|  | 140 | } else { | 
|  | 141 | c.ctx.Verbosef("nsjail failed with %v", err) | 
|  | 142 | } | 
|  | 143 | }) | 
|  | 144 |  | 
|  | 145 | return sandboxConfig.working | 
| Dan Willemsen | 269a8c7 | 2017-05-03 17:15:47 -0700 | [diff] [blame] | 146 | } | 
|  | 147 |  | 
|  | 148 | func (c *Cmd) wrapSandbox() { | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 149 | wd, _ := os.Getwd() | 
|  | 150 |  | 
|  | 151 | sandboxArgs := []string{ | 
|  | 152 | // The executable to run | 
|  | 153 | "-x", c.Path, | 
|  | 154 |  | 
|  | 155 | // Set the hostname to something consistent | 
|  | 156 | "-H", "android-build", | 
|  | 157 |  | 
|  | 158 | // Use the current working dir | 
|  | 159 | "--cwd", wd, | 
|  | 160 |  | 
|  | 161 | // No time limit | 
|  | 162 | "-t", "0", | 
|  | 163 |  | 
|  | 164 | // Keep all environment variables, we already filter them out | 
|  | 165 | // in soong_ui | 
|  | 166 | "-e", | 
|  | 167 |  | 
| Dan Willemsen | 3a4dbd6 | 2019-01-16 23:02:24 -0800 | [diff] [blame] | 168 | // Mount /proc read-write, necessary to run a nested nsjail or minijail0 | 
|  | 169 | "--proc_rw", | 
|  | 170 |  | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 171 | // Use a consistent user & group. | 
|  | 172 | // Note that these are mapped back to the real UID/GID when | 
|  | 173 | // doing filesystem operations, so they're rather arbitrary. | 
|  | 174 | "-u", "nobody", | 
|  | 175 | "-g", sandboxConfig.group, | 
|  | 176 |  | 
|  | 177 | // Set high values, as nsjail uses low defaults. | 
|  | 178 | "--rlimit_as", "soft", | 
|  | 179 | "--rlimit_core", "soft", | 
|  | 180 | "--rlimit_cpu", "soft", | 
|  | 181 | "--rlimit_fsize", "soft", | 
|  | 182 | "--rlimit_nofile", "soft", | 
|  | 183 |  | 
| Diego Wilson | a22240b | 2020-04-02 18:11:28 +0000 | [diff] [blame] | 184 | // For now, just map everything. Make most things readonly. | 
|  | 185 | "-R", "/", | 
|  | 186 |  | 
| Dan Willemsen | 1612e26 | 2020-05-01 16:26:56 -0700 | [diff] [blame] | 187 | // Mount a writable tmp dir | 
|  | 188 | "-B", "/tmp", | 
|  | 189 |  | 
| Spandan Das | a3639e6 | 2021-05-25 19:14:02 +0000 | [diff] [blame] | 190 | // Mount source | 
| Spandan Das | 2d99704 | 2022-11-04 20:58:18 +0000 | [diff] [blame] | 191 | c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir, | 
| Diego Wilson | a22240b | 2020-04-02 18:11:28 +0000 | [diff] [blame] | 192 |  | 
|  | 193 | //Mount out dir as read-write | 
|  | 194 | "-B", sandboxConfig.outDir, | 
|  | 195 |  | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 196 | // Disable newcgroup for now, since it may require newer kernels | 
|  | 197 | // TODO: try out cgroups | 
|  | 198 | "--disable_clone_newcgroup", | 
|  | 199 |  | 
|  | 200 | // Only log important warnings / errors | 
|  | 201 | "-q", | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 202 | } | 
| Dan Willemsen | 25e6f09 | 2019-04-09 10:22:43 -0700 | [diff] [blame] | 203 |  | 
| Spandan Das | a3639e6 | 2021-05-25 19:14:02 +0000 | [diff] [blame] | 204 | // Mount srcDir RW allowlists as Read-Write | 
|  | 205 | if len(c.config.sandboxConfig.SrcDirRWAllowlist()) > 0 && !c.config.sandboxConfig.SrcDirIsRO() { | 
|  | 206 | errMsg := `Product source tree has been set as ReadWrite, RW allowlist not necessary. | 
|  | 207 | To recover, either | 
|  | 208 | 1. Unset BUILD_BROKEN_SRC_DIR_IS_WRITABLE #or | 
|  | 209 | 2. Unset BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST` | 
|  | 210 | c.ctx.Fatalln(errMsg) | 
|  | 211 | } | 
|  | 212 | for _, srcDirChild := range c.config.sandboxConfig.SrcDirRWAllowlist() { | 
|  | 213 | sandboxArgs = append(sandboxArgs, "-B", srcDirChild) | 
|  | 214 | } | 
|  | 215 |  | 
| Diego Wilson | a5d9653 | 2020-04-06 22:02:38 +0000 | [diff] [blame] | 216 | if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) { | 
|  | 217 | //Mount dist dir as read-write if it already exists | 
|  | 218 | sandboxArgs = append(sandboxArgs, "-B", sandboxConfig.distDir) | 
|  | 219 | } | 
|  | 220 |  | 
| Dan Willemsen | 25e6f09 | 2019-04-09 10:22:43 -0700 | [diff] [blame] | 221 | if c.Sandbox.AllowBuildBrokenUsesNetwork && c.config.BuildBrokenUsesNetwork() { | 
|  | 222 | c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork) | 
|  | 223 | c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork()) | 
|  | 224 | sandboxArgs = append(sandboxArgs, "-N") | 
| Colin Cross | aa812d1 | 2019-06-19 13:33:24 -0700 | [diff] [blame] | 225 | } else if dlv, _ := c.config.Environment().Get("SOONG_DELVE"); dlv != "" { | 
|  | 226 | // The debugger is enabled and soong_build will pause until a remote delve process connects, allow | 
|  | 227 | // network connections. | 
|  | 228 | sandboxArgs = append(sandboxArgs, "-N") | 
| Dan Willemsen | 25e6f09 | 2019-04-09 10:22:43 -0700 | [diff] [blame] | 229 | } | 
|  | 230 |  | 
|  | 231 | // Stop nsjail from parsing arguments | 
|  | 232 | sandboxArgs = append(sandboxArgs, "--") | 
|  | 233 |  | 
| Dan Willemsen | 63663c6 | 2019-01-02 12:24:44 -0800 | [diff] [blame] | 234 | c.Args = append(sandboxArgs, c.Args[1:]...) | 
|  | 235 | c.Path = nsjailPath | 
|  | 236 |  | 
|  | 237 | env := Environment(c.Env) | 
|  | 238 | if _, hasUser := env.Get("USER"); hasUser { | 
|  | 239 | env.Set("USER", "nobody") | 
|  | 240 | } | 
|  | 241 | c.Env = []string(env) | 
| Dan Willemsen | 269a8c7 | 2017-05-03 17:15:47 -0700 | [diff] [blame] | 242 | } |