// 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" "strings" "sync" ) type Sandbox struct { Enabled bool DisableWhenUsingGoma bool } var ( noSandbox = Sandbox{} basicSandbox = Sandbox{ Enabled: true, } dumpvarsSandbox = basicSandbox katiSandbox = basicSandbox soongSandbox = basicSandbox ninjaSandbox = Sandbox{ Enabled: true, DisableWhenUsingGoma: true, } ) const nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail" var sandboxConfig struct { once sync.Once working bool group 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" } cmd := exec.CommandContext(c.ctx.Context, nsjailPath, "-H", "android-build", "-e", "-u", "nobody", "-g", sandboxConfig.group, "-B", "/", "--disable_clone_newcgroup", "--", "/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`) 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. This may become fatal in the future.") c.ctx.Println("Please let us know why nsjail doesn't work in your environment at:") c.ctx.Println(" https://groups.google.com/forum/#!forum/android-building") c.ctx.Println(" https://issuetracker.google.com/issues/new?component=381517") 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. Eventually we should limit this, especially to make most things readonly. "-B", "/", // Disable newcgroup for now, since it may require newer kernels // TODO: try out cgroups "--disable_clone_newcgroup", // Only log important warnings / errors "-q", // Stop parsing arguments "--", } 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) }