123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- // Copyright 2017 Google Inc. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package build
- import (
- "bytes"
- "os"
- "os/exec"
- "os/user"
- "path/filepath"
- "strings"
- "sync"
- )
- type Sandbox struct {
- Enabled bool
- DisableWhenUsingGoma bool
- AllowBuildBrokenUsesNetwork bool
- }
- var (
- noSandbox = Sandbox{}
- basicSandbox = Sandbox{
- Enabled: true,
- }
- dumpvarsSandbox = basicSandbox
- katiSandbox = basicSandbox
- soongSandbox = basicSandbox
- ninjaSandbox = Sandbox{
- Enabled: true,
- DisableWhenUsingGoma: true,
- AllowBuildBrokenUsesNetwork: true,
- }
- )
- const nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail"
- var sandboxConfig struct {
- once sync.Once
- working bool
- group string
- srcDir string
- outDir string
- distDir string
- }
- func (c *Cmd) sandboxSupported() bool {
- if !c.Sandbox.Enabled {
- return false
- }
- // Goma is incompatible with PID namespaces and Mount namespaces. b/122767582
- if c.Sandbox.DisableWhenUsingGoma && c.config.UseGoma() {
- return false
- }
- sandboxConfig.once.Do(func() {
- sandboxConfig.group = "nogroup"
- if _, err := user.LookupGroup(sandboxConfig.group); err != nil {
- sandboxConfig.group = "nobody"
- }
- // These directories will be bind mounted
- // so we need full non-symlink paths
- sandboxConfig.srcDir = absPath(c.ctx, ".")
- if derefPath, err := filepath.EvalSymlinks(sandboxConfig.srcDir); err == nil {
- sandboxConfig.srcDir = absPath(c.ctx, derefPath)
- }
- sandboxConfig.outDir = absPath(c.ctx, c.config.OutDir())
- if derefPath, err := filepath.EvalSymlinks(sandboxConfig.outDir); err == nil {
- sandboxConfig.outDir = absPath(c.ctx, derefPath)
- }
- sandboxConfig.distDir = absPath(c.ctx, c.config.DistDir())
- if derefPath, err := filepath.EvalSymlinks(sandboxConfig.distDir); err == nil {
- sandboxConfig.distDir = absPath(c.ctx, derefPath)
- }
- sandboxArgs := []string{
- "-H", "android-build",
- "-e",
- "-u", "nobody",
- "-g", sandboxConfig.group,
- "-R", "/",
- // Mount tmp before srcDir
- // srcDir is /tmp/.* in integration tests, which is a child dir of /tmp
- // nsjail throws an error if a child dir is mounted before its parent
- "-B", "/tmp",
- c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir,
- "-B", sandboxConfig.outDir,
- }
- if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
- //Mount dist dir as read-write if it already exists
- sandboxArgs = append(sandboxArgs, "-B",
- sandboxConfig.distDir)
- }
- sandboxArgs = append(sandboxArgs,
- "--disable_clone_newcgroup",
- "--",
- "/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`)
- cmd := exec.CommandContext(c.ctx.Context, nsjailPath, sandboxArgs...)
- cmd.Env = c.config.Environment().Environ()
- c.ctx.Verboseln(cmd.Args)
- data, err := cmd.CombinedOutput()
- if err == nil && bytes.Contains(data, []byte("Android Success")) {
- sandboxConfig.working = true
- return
- }
- c.ctx.Println("Build sandboxing disabled due to nsjail error.")
- for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
- c.ctx.Verboseln(line)
- }
- if err == nil {
- c.ctx.Verboseln("nsjail exited successfully, but without the correct output")
- } else if e, ok := err.(*exec.ExitError); ok {
- c.ctx.Verbosef("nsjail failed with %v", e.ProcessState.String())
- } else {
- c.ctx.Verbosef("nsjail failed with %v", err)
- }
- })
- return sandboxConfig.working
- }
- func (c *Cmd) wrapSandbox() {
- wd, _ := os.Getwd()
- sandboxArgs := []string{
- // The executable to run
- "-x", c.Path,
- // Set the hostname to something consistent
- "-H", "android-build",
- // Use the current working dir
- "--cwd", wd,
- // No time limit
- "-t", "0",
- // Keep all environment variables, we already filter them out
- // in soong_ui
- "-e",
- // Mount /proc read-write, necessary to run a nested nsjail or minijail0
- "--proc_rw",
- // Use a consistent user & group.
- // Note that these are mapped back to the real UID/GID when
- // doing filesystem operations, so they're rather arbitrary.
- "-u", "nobody",
- "-g", sandboxConfig.group,
- // Set high values, as nsjail uses low defaults.
- "--rlimit_as", "soft",
- "--rlimit_core", "soft",
- "--rlimit_cpu", "soft",
- "--rlimit_fsize", "soft",
- "--rlimit_nofile", "soft",
- // For now, just map everything. Make most things readonly.
- "-R", "/",
- // Mount a writable tmp dir
- "-B", "/tmp",
- // Mount source
- c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir,
- //Mount out dir as read-write
- "-B", sandboxConfig.outDir,
- // Disable newcgroup for now, since it may require newer kernels
- // TODO: try out cgroups
- "--disable_clone_newcgroup",
- // Only log important warnings / errors
- "-q",
- }
- // Mount srcDir RW allowlists as Read-Write
- if len(c.config.sandboxConfig.SrcDirRWAllowlist()) > 0 && !c.config.sandboxConfig.SrcDirIsRO() {
- errMsg := `Product source tree has been set as ReadWrite, RW allowlist not necessary.
- To recover, either
- 1. Unset BUILD_BROKEN_SRC_DIR_IS_WRITABLE #or
- 2. Unset BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST`
- c.ctx.Fatalln(errMsg)
- }
- for _, srcDirChild := range c.config.sandboxConfig.SrcDirRWAllowlist() {
- sandboxArgs = append(sandboxArgs, "-B", srcDirChild)
- }
- if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
- //Mount dist dir as read-write if it already exists
- sandboxArgs = append(sandboxArgs, "-B", sandboxConfig.distDir)
- }
- if c.Sandbox.AllowBuildBrokenUsesNetwork && c.config.BuildBrokenUsesNetwork() {
- c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork)
- c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork())
- sandboxArgs = append(sandboxArgs, "-N")
- } else if dlv, _ := c.config.Environment().Get("SOONG_DELVE"); dlv != "" {
- // The debugger is enabled and soong_build will pause until a remote delve process connects, allow
- // network connections.
- sandboxArgs = append(sandboxArgs, "-N")
- }
- // Stop nsjail from parsing arguments
- sandboxArgs = append(sandboxArgs, "--")
- c.Args = append(sandboxArgs, c.Args[1:]...)
- c.Path = nsjailPath
- env := Environment(c.Env)
- if _, hasUser := env.Get("USER"); hasUser {
- env.Set("USER", "nobody")
- }
- c.Env = []string(env)
- }
|