blob: 1edbe21210139c7c579e771f80e8e66e767dd2d0 [file] [log] [blame]
Dan Willemsen269a8c72017-05-03 17:15:47 -07001// 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
15package build
16
Dan Willemsen63663c62019-01-02 12:24:44 -080017import (
18 "bytes"
19 "os"
20 "os/exec"
21 "os/user"
Diego Wilson10e564a2020-04-15 17:42:59 +000022 "path/filepath"
Dan Willemsen63663c62019-01-02 12:24:44 -080023 "strings"
24 "sync"
Dan Willemsen269a8c72017-05-03 17:15:47 -070025)
26
Dan Willemsen63663c62019-01-02 12:24:44 -080027type Sandbox struct {
28 Enabled bool
29 DisableWhenUsingGoma bool
Dan Willemsen25e6f092019-04-09 10:22:43 -070030
31 AllowBuildBrokenUsesNetwork bool
Dan Willemsen63663c62019-01-02 12:24:44 -080032}
33
34var (
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 Willemsen25e6f092019-04-09 10:22:43 -070046
47 AllowBuildBrokenUsesNetwork: true,
Dan Willemsen63663c62019-01-02 12:24:44 -080048 }
49)
50
Taylor Santiagoca30e082024-07-30 13:48:43 -070051const (
52 nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail"
Taylor Santiagoca30e082024-07-30 13:48:43 -070053)
Dan Willemsen63663c62019-01-02 12:24:44 -080054
55var sandboxConfig struct {
56 once sync.Once
57
58 working bool
59 group string
Diego Wilsona22240b2020-04-02 18:11:28 +000060 srcDir string
61 outDir string
62 distDir string
Dan Willemsen63663c62019-01-02 12:24:44 -080063}
64
Dan Willemsen269a8c72017-05-03 17:15:47 -070065func (c *Cmd) sandboxSupported() bool {
Dan Willemsen63663c62019-01-02 12:24:44 -080066 if !c.Sandbox.Enabled {
67 return false
68 }
69
70 // Goma is incompatible with PID namespaces and Mount namespaces. b/122767582
71 if c.Sandbox.DisableWhenUsingGoma && c.config.UseGoma() {
72 return false
73 }
74
75 sandboxConfig.once.Do(func() {
76 sandboxConfig.group = "nogroup"
77 if _, err := user.LookupGroup(sandboxConfig.group); err != nil {
78 sandboxConfig.group = "nobody"
79 }
80
Diego Wilson10e564a2020-04-15 17:42:59 +000081 // These directories will be bind mounted
82 // so we need full non-symlink paths
Diego Wilsona22240b2020-04-02 18:11:28 +000083 sandboxConfig.srcDir = absPath(c.ctx, ".")
Diego Wilson10e564a2020-04-15 17:42:59 +000084 if derefPath, err := filepath.EvalSymlinks(sandboxConfig.srcDir); err == nil {
85 sandboxConfig.srcDir = absPath(c.ctx, derefPath)
86 }
Diego Wilsona22240b2020-04-02 18:11:28 +000087 sandboxConfig.outDir = absPath(c.ctx, c.config.OutDir())
Diego Wilson10e564a2020-04-15 17:42:59 +000088 if derefPath, err := filepath.EvalSymlinks(sandboxConfig.outDir); err == nil {
89 sandboxConfig.outDir = absPath(c.ctx, derefPath)
90 }
Diego Wilsona22240b2020-04-02 18:11:28 +000091 sandboxConfig.distDir = absPath(c.ctx, c.config.DistDir())
Diego Wilson10e564a2020-04-15 17:42:59 +000092 if derefPath, err := filepath.EvalSymlinks(sandboxConfig.distDir); err == nil {
93 sandboxConfig.distDir = absPath(c.ctx, derefPath)
94 }
Diego Wilsona22240b2020-04-02 18:11:28 +000095
Diego Wilsona5d96532020-04-06 22:02:38 +000096 sandboxArgs := []string{
Dan Willemsen63663c62019-01-02 12:24:44 -080097 "-H", "android-build",
98 "-e",
99 "-u", "nobody",
100 "-g", sandboxConfig.group,
Diego Wilsona22240b2020-04-02 18:11:28 +0000101 "-R", "/",
Spandan Das05063612021-06-25 01:39:04 +0000102 // Mount tmp before srcDir
103 // srcDir is /tmp/.* in integration tests, which is a child dir of /tmp
104 // nsjail throws an error if a child dir is mounted before its parent
Diego Wilsona22240b2020-04-02 18:11:28 +0000105 "-B", "/tmp",
Spandan Das2d997042022-11-04 20:58:18 +0000106 c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir,
Diego Wilsona22240b2020-04-02 18:11:28 +0000107 "-B", sandboxConfig.outDir,
Diego Wilsona5d96532020-04-06 22:02:38 +0000108 }
109
110 if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
111 //Mount dist dir as read-write if it already exists
112 sandboxArgs = append(sandboxArgs, "-B",
113 sandboxConfig.distDir)
114 }
115
116 sandboxArgs = append(sandboxArgs,
Dan Willemsen63663c62019-01-02 12:24:44 -0800117 "--disable_clone_newcgroup",
118 "--",
119 "/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`)
Diego Wilsona5d96532020-04-06 22:02:38 +0000120
121 cmd := exec.CommandContext(c.ctx.Context, nsjailPath, sandboxArgs...)
122
Dan Willemsen63663c62019-01-02 12:24:44 -0800123 cmd.Env = c.config.Environment().Environ()
124
125 c.ctx.Verboseln(cmd.Args)
126 data, err := cmd.CombinedOutput()
127 if err == nil && bytes.Contains(data, []byte("Android Success")) {
128 sandboxConfig.working = true
129 return
130 }
131
Dan Willemsen1871d882020-03-02 20:36:04 +0000132 c.ctx.Println("Build sandboxing disabled due to nsjail error.")
Dan Willemsen63663c62019-01-02 12:24:44 -0800133
134 for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
135 c.ctx.Verboseln(line)
136 }
137
138 if err == nil {
139 c.ctx.Verboseln("nsjail exited successfully, but without the correct output")
140 } else if e, ok := err.(*exec.ExitError); ok {
141 c.ctx.Verbosef("nsjail failed with %v", e.ProcessState.String())
142 } else {
143 c.ctx.Verbosef("nsjail failed with %v", err)
144 }
145 })
146
147 return sandboxConfig.working
Dan Willemsen269a8c72017-05-03 17:15:47 -0700148}
149
Taylor Santiago8b0bed72024-09-03 13:30:22 -0700150// Assumes input path is absolute, clean, and if applicable, an evaluated
151// symlink. If path is not a subdirectory of src dir or relative path
152// cannot be determined, return the input untouched.
153func (c *Cmd) relFromSrcDir(path string) string {
154 if !strings.HasPrefix(path, sandboxConfig.srcDir) {
155 return path
Taylor Santiagoca30e082024-07-30 13:48:43 -0700156 }
Dan Willemsen63663c62019-01-02 12:24:44 -0800157
Taylor Santiago8b0bed72024-09-03 13:30:22 -0700158 rel, err := filepath.Rel(sandboxConfig.srcDir, path)
159 if err != nil {
160 return path
161 }
162
163 return rel
164}
165
166func (c *Cmd) dirArg(path string) string {
167 if !c.config.UseABFS() {
168 return path
169 }
170
171 rel := c.relFromSrcDir(path)
172
173 return path + ":" + filepath.Join(abfsSrcDir, rel)
174}
175
176func (c *Cmd) srcDirArg() string {
177 return c.dirArg(sandboxConfig.srcDir)
Taylor Santiagoca30e082024-07-30 13:48:43 -0700178}
179
180func (c *Cmd) outDirArg() string {
Taylor Santiago8b0bed72024-09-03 13:30:22 -0700181 return c.dirArg(sandboxConfig.outDir)
182}
Taylor Santiagoca30e082024-07-30 13:48:43 -0700183
Taylor Santiago8b0bed72024-09-03 13:30:22 -0700184func (c *Cmd) distDirArg() string {
185 return c.dirArg(sandboxConfig.distDir)
Taylor Santiago26118512024-08-05 19:52:41 -0700186}
187
188// When configured to use ABFS, we need to allow the creation of the /src
189// directory. Therefore, we cannot mount the root "/" directory as read-only.
190// Instead, we individually mount the children of "/" as RO.
191func (c *Cmd) readMountArgs() []string {
192 if !c.config.UseABFS() {
193 // For now, just map everything. Make most things readonly.
194 return []string{"-R", "/"}
195 }
196
197 entries, err := os.ReadDir("/")
198 if err != nil {
199 // If we can't read "/", just use the default non-ABFS behavior.
200 return []string{"-R", "/"}
201 }
202
203 args := make([]string, 0, 2*len(entries))
204 for _, ent := range entries {
205 args = append(args, "-R", "/"+ent.Name())
206 }
207
208 return args
Taylor Santiagoca30e082024-07-30 13:48:43 -0700209}
210
Taylor Santiago8b0bed72024-09-03 13:30:22 -0700211func (c *Cmd) workDir() string {
212 if !c.config.UseABFS() {
213 wd, _ := os.Getwd()
214 return wd
215 }
216
217 return abfsSrcDir
218}
219
Dan Willemsen269a8c72017-05-03 17:15:47 -0700220func (c *Cmd) wrapSandbox() {
Taylor Santiago8b0bed72024-09-03 13:30:22 -0700221 wd := c.workDir()
Dan Willemsen63663c62019-01-02 12:24:44 -0800222
Taylor Santiago26118512024-08-05 19:52:41 -0700223 var sandboxArgs []string
224 sandboxArgs = append(sandboxArgs,
Dan Willemsen63663c62019-01-02 12:24:44 -0800225 // The executable to run
226 "-x", c.Path,
227
228 // Set the hostname to something consistent
229 "-H", "android-build",
230
231 // Use the current working dir
232 "--cwd", wd,
233
234 // No time limit
235 "-t", "0",
236
237 // Keep all environment variables, we already filter them out
238 // in soong_ui
239 "-e",
240
Dan Willemsen3a4dbd62019-01-16 23:02:24 -0800241 // Mount /proc read-write, necessary to run a nested nsjail or minijail0
242 "--proc_rw",
243
Dan Willemsen63663c62019-01-02 12:24:44 -0800244 // Use a consistent user & group.
245 // Note that these are mapped back to the real UID/GID when
246 // doing filesystem operations, so they're rather arbitrary.
247 "-u", "nobody",
248 "-g", sandboxConfig.group,
249
250 // Set high values, as nsjail uses low defaults.
251 "--rlimit_as", "soft",
252 "--rlimit_core", "soft",
253 "--rlimit_cpu", "soft",
254 "--rlimit_fsize", "soft",
255 "--rlimit_nofile", "soft",
Taylor Santiago26118512024-08-05 19:52:41 -0700256 )
Dan Willemsen63663c62019-01-02 12:24:44 -0800257
Taylor Santiago26118512024-08-05 19:52:41 -0700258 sandboxArgs = append(sandboxArgs,
Taylor Santiago8b0bed72024-09-03 13:30:22 -0700259 c.readMountArgs()...,
Taylor Santiago26118512024-08-05 19:52:41 -0700260 )
Diego Wilsona22240b2020-04-02 18:11:28 +0000261
Taylor Santiago26118512024-08-05 19:52:41 -0700262 sandboxArgs = append(sandboxArgs,
Dan Willemsen1612e262020-05-01 16:26:56 -0700263 // Mount a writable tmp dir
264 "-B", "/tmp",
265
Spandan Dasa3639e62021-05-25 19:14:02 +0000266 // Mount source
Taylor Santiagoca30e082024-07-30 13:48:43 -0700267 c.config.sandboxConfig.SrcDirMountFlag(), c.srcDirArg(),
Diego Wilsona22240b2020-04-02 18:11:28 +0000268
269 //Mount out dir as read-write
Taylor Santiagoca30e082024-07-30 13:48:43 -0700270 "-B", c.outDirArg(),
Diego Wilsona22240b2020-04-02 18:11:28 +0000271
Dan Willemsen63663c62019-01-02 12:24:44 -0800272 // Disable newcgroup for now, since it may require newer kernels
273 // TODO: try out cgroups
274 "--disable_clone_newcgroup",
275
276 // Only log important warnings / errors
277 "-q",
Taylor Santiago26118512024-08-05 19:52:41 -0700278 )
Taylor Santiago3c16e612024-05-30 14:41:31 -0700279 if c.config.UseABFS() {
280 sandboxArgs = append(sandboxArgs, "-B", "{ABFS_DIR}")
Dan Willemsen63663c62019-01-02 12:24:44 -0800281 }
Dan Willemsen25e6f092019-04-09 10:22:43 -0700282
Spandan Dasa3639e62021-05-25 19:14:02 +0000283 // Mount srcDir RW allowlists as Read-Write
284 if len(c.config.sandboxConfig.SrcDirRWAllowlist()) > 0 && !c.config.sandboxConfig.SrcDirIsRO() {
285 errMsg := `Product source tree has been set as ReadWrite, RW allowlist not necessary.
286 To recover, either
287 1. Unset BUILD_BROKEN_SRC_DIR_IS_WRITABLE #or
288 2. Unset BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST`
289 c.ctx.Fatalln(errMsg)
290 }
291 for _, srcDirChild := range c.config.sandboxConfig.SrcDirRWAllowlist() {
292 sandboxArgs = append(sandboxArgs, "-B", srcDirChild)
293 }
294
Diego Wilsona5d96532020-04-06 22:02:38 +0000295 if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
296 //Mount dist dir as read-write if it already exists
Taylor Santiago8b0bed72024-09-03 13:30:22 -0700297 sandboxArgs = append(sandboxArgs, "-B", c.distDirArg())
Diego Wilsona5d96532020-04-06 22:02:38 +0000298 }
299
Dan Willemsen25e6f092019-04-09 10:22:43 -0700300 if c.Sandbox.AllowBuildBrokenUsesNetwork && c.config.BuildBrokenUsesNetwork() {
301 c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork)
302 c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork())
303 sandboxArgs = append(sandboxArgs, "-N")
Colin Crossaa812d12019-06-19 13:33:24 -0700304 } else if dlv, _ := c.config.Environment().Get("SOONG_DELVE"); dlv != "" {
305 // The debugger is enabled and soong_build will pause until a remote delve process connects, allow
306 // network connections.
307 sandboxArgs = append(sandboxArgs, "-N")
Dan Willemsen25e6f092019-04-09 10:22:43 -0700308 }
309
daniml3ca326ff2021-10-11 14:12:34 +0200310 if ccacheExec := os.Getenv("CCACHE_EXEC"); ccacheExec != "" {
311 bytes, err := exec.Command(ccacheExec, "-k", "cache_dir").Output()
312 if err == nil {
313 sandboxArgs = append(sandboxArgs, "-B", strings.TrimSpace(string(bytes)))
314 }
315 }
316
Dan Willemsen25e6f092019-04-09 10:22:43 -0700317 // Stop nsjail from parsing arguments
318 sandboxArgs = append(sandboxArgs, "--")
319
Dan Willemsen63663c62019-01-02 12:24:44 -0800320 c.Args = append(sandboxArgs, c.Args[1:]...)
321 c.Path = nsjailPath
322
323 env := Environment(c.Env)
324 if _, hasUser := env.Get("USER"); hasUser {
325 env.Set("USER", "nobody")
326 }
327 c.Env = []string(env)
Dan Willemsen269a8c72017-05-03 17:15:47 -0700328}