sandbox_linux.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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. package build
  15. import (
  16. "bytes"
  17. "os"
  18. "os/exec"
  19. "os/user"
  20. "path/filepath"
  21. "strings"
  22. "sync"
  23. )
  24. type Sandbox struct {
  25. Enabled bool
  26. DisableWhenUsingGoma bool
  27. AllowBuildBrokenUsesNetwork bool
  28. }
  29. var (
  30. noSandbox = Sandbox{}
  31. basicSandbox = Sandbox{
  32. Enabled: true,
  33. }
  34. dumpvarsSandbox = basicSandbox
  35. katiSandbox = basicSandbox
  36. soongSandbox = basicSandbox
  37. ninjaSandbox = Sandbox{
  38. Enabled: true,
  39. DisableWhenUsingGoma: true,
  40. AllowBuildBrokenUsesNetwork: true,
  41. }
  42. )
  43. const nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail"
  44. var sandboxConfig struct {
  45. once sync.Once
  46. working bool
  47. group string
  48. srcDir string
  49. outDir string
  50. distDir string
  51. }
  52. func (c *Cmd) sandboxSupported() bool {
  53. if !c.Sandbox.Enabled {
  54. return false
  55. }
  56. // Goma is incompatible with PID namespaces and Mount namespaces. b/122767582
  57. if c.Sandbox.DisableWhenUsingGoma && c.config.UseGoma() {
  58. return false
  59. }
  60. sandboxConfig.once.Do(func() {
  61. sandboxConfig.group = "nogroup"
  62. if _, err := user.LookupGroup(sandboxConfig.group); err != nil {
  63. sandboxConfig.group = "nobody"
  64. }
  65. // These directories will be bind mounted
  66. // so we need full non-symlink paths
  67. sandboxConfig.srcDir = absPath(c.ctx, ".")
  68. if derefPath, err := filepath.EvalSymlinks(sandboxConfig.srcDir); err == nil {
  69. sandboxConfig.srcDir = absPath(c.ctx, derefPath)
  70. }
  71. sandboxConfig.outDir = absPath(c.ctx, c.config.OutDir())
  72. if derefPath, err := filepath.EvalSymlinks(sandboxConfig.outDir); err == nil {
  73. sandboxConfig.outDir = absPath(c.ctx, derefPath)
  74. }
  75. sandboxConfig.distDir = absPath(c.ctx, c.config.DistDir())
  76. if derefPath, err := filepath.EvalSymlinks(sandboxConfig.distDir); err == nil {
  77. sandboxConfig.distDir = absPath(c.ctx, derefPath)
  78. }
  79. sandboxArgs := []string{
  80. "-H", "android-build",
  81. "-e",
  82. "-u", "nobody",
  83. "-g", sandboxConfig.group,
  84. "-R", "/",
  85. // Mount tmp before srcDir
  86. // srcDir is /tmp/.* in integration tests, which is a child dir of /tmp
  87. // nsjail throws an error if a child dir is mounted before its parent
  88. "-B", "/tmp",
  89. c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir,
  90. "-B", sandboxConfig.outDir,
  91. }
  92. if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
  93. //Mount dist dir as read-write if it already exists
  94. sandboxArgs = append(sandboxArgs, "-B",
  95. sandboxConfig.distDir)
  96. }
  97. sandboxArgs = append(sandboxArgs,
  98. "--disable_clone_newcgroup",
  99. "--",
  100. "/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`)
  101. cmd := exec.CommandContext(c.ctx.Context, nsjailPath, sandboxArgs...)
  102. cmd.Env = c.config.Environment().Environ()
  103. c.ctx.Verboseln(cmd.Args)
  104. data, err := cmd.CombinedOutput()
  105. if err == nil && bytes.Contains(data, []byte("Android Success")) {
  106. sandboxConfig.working = true
  107. return
  108. }
  109. c.ctx.Println("Build sandboxing disabled due to nsjail error.")
  110. for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
  111. c.ctx.Verboseln(line)
  112. }
  113. if err == nil {
  114. c.ctx.Verboseln("nsjail exited successfully, but without the correct output")
  115. } else if e, ok := err.(*exec.ExitError); ok {
  116. c.ctx.Verbosef("nsjail failed with %v", e.ProcessState.String())
  117. } else {
  118. c.ctx.Verbosef("nsjail failed with %v", err)
  119. }
  120. })
  121. return sandboxConfig.working
  122. }
  123. func (c *Cmd) wrapSandbox() {
  124. wd, _ := os.Getwd()
  125. sandboxArgs := []string{
  126. // The executable to run
  127. "-x", c.Path,
  128. // Set the hostname to something consistent
  129. "-H", "android-build",
  130. // Use the current working dir
  131. "--cwd", wd,
  132. // No time limit
  133. "-t", "0",
  134. // Keep all environment variables, we already filter them out
  135. // in soong_ui
  136. "-e",
  137. // Mount /proc read-write, necessary to run a nested nsjail or minijail0
  138. "--proc_rw",
  139. // Use a consistent user & group.
  140. // Note that these are mapped back to the real UID/GID when
  141. // doing filesystem operations, so they're rather arbitrary.
  142. "-u", "nobody",
  143. "-g", sandboxConfig.group,
  144. // Set high values, as nsjail uses low defaults.
  145. "--rlimit_as", "soft",
  146. "--rlimit_core", "soft",
  147. "--rlimit_cpu", "soft",
  148. "--rlimit_fsize", "soft",
  149. "--rlimit_nofile", "soft",
  150. // For now, just map everything. Make most things readonly.
  151. "-R", "/",
  152. // Mount a writable tmp dir
  153. "-B", "/tmp",
  154. // Mount source
  155. c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir,
  156. //Mount out dir as read-write
  157. "-B", sandboxConfig.outDir,
  158. // Disable newcgroup for now, since it may require newer kernels
  159. // TODO: try out cgroups
  160. "--disable_clone_newcgroup",
  161. // Only log important warnings / errors
  162. "-q",
  163. }
  164. // Mount srcDir RW allowlists as Read-Write
  165. if len(c.config.sandboxConfig.SrcDirRWAllowlist()) > 0 && !c.config.sandboxConfig.SrcDirIsRO() {
  166. errMsg := `Product source tree has been set as ReadWrite, RW allowlist not necessary.
  167. To recover, either
  168. 1. Unset BUILD_BROKEN_SRC_DIR_IS_WRITABLE #or
  169. 2. Unset BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST`
  170. c.ctx.Fatalln(errMsg)
  171. }
  172. for _, srcDirChild := range c.config.sandboxConfig.SrcDirRWAllowlist() {
  173. sandboxArgs = append(sandboxArgs, "-B", srcDirChild)
  174. }
  175. if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
  176. //Mount dist dir as read-write if it already exists
  177. sandboxArgs = append(sandboxArgs, "-B", sandboxConfig.distDir)
  178. }
  179. if c.Sandbox.AllowBuildBrokenUsesNetwork && c.config.BuildBrokenUsesNetwork() {
  180. c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork)
  181. c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork())
  182. sandboxArgs = append(sandboxArgs, "-N")
  183. } else if dlv, _ := c.config.Environment().Get("SOONG_DELVE"); dlv != "" {
  184. // The debugger is enabled and soong_build will pause until a remote delve process connects, allow
  185. // network connections.
  186. sandboxArgs = append(sandboxArgs, "-N")
  187. }
  188. // Stop nsjail from parsing arguments
  189. sandboxArgs = append(sandboxArgs, "--")
  190. c.Args = append(sandboxArgs, c.Args[1:]...)
  191. c.Path = nsjailPath
  192. env := Environment(c.Env)
  193. if _, hasUser := env.Get("USER"); hasUser {
  194. env.Set("USER", "nobody")
  195. }
  196. c.Env = []string(env)
  197. }