Create Make flags to set source tree as ReadOnly in soong builds
The following two Make vars control RO/RW access to the source tree
1. BUILD_BROKEN_SRC_DIR_IS_WRITABLE
2. BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST
By default, (1) will be truthy.
- this ensures that this CL is a non breaking change across all products
- different products can opt in to set is as "false"
Bug: 174726238
Test: from build/soong dir, ran go test ./ui/build
Change-Id: I4d55ac74f02b2a73194d31506a9010162620b25a
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index d17b464..37940ba 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -60,6 +60,7 @@
"path.go",
"proc_sync.go",
"rbe.go",
+ "sandbox_config.go",
"signal.go",
"soong.go",
"test_build.go",
@@ -86,5 +87,8 @@
"config_linux.go",
"sandbox_linux.go",
],
+ testSrcs: [
+ "sandbox_linux_test.go",
+ ],
},
}
diff --git a/ui/build/config.go b/ui/build/config.go
index 1d1f71f..f9f638c 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -57,6 +57,7 @@
katiSuffix string
targetDevice string
targetDeviceDir string
+ sandboxConfig *SandboxConfig
// Autodetected
totalRAM uint64
@@ -120,7 +121,8 @@
func NewConfig(ctx Context, args ...string) Config {
ret := &configImpl{
- environ: OsEnvironment(),
+ environ: OsEnvironment(),
+ sandboxConfig: &SandboxConfig{},
}
// Default matching ninja
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 54aeda0..83c8865 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -225,6 +225,10 @@
// Extra environment variables to be exported to ninja
"BUILD_BROKEN_NINJA_USES_ENV_VARS",
+ // Used to restrict write access to source tree
+ "BUILD_BROKEN_SRC_DIR_IS_WRITABLE",
+ "BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST",
+
// Not used, but useful to be in the soong.log
"BOARD_VNDK_VERSION",
@@ -280,6 +284,8 @@
config.SetNinjaArgs(strings.Fields(makeVars["NINJA_GOALS"]))
config.SetTargetDevice(makeVars["TARGET_DEVICE"])
config.SetTargetDeviceDir(makeVars["TARGET_DEVICE_DIR"])
+ config.sandboxConfig.SetSrcDirIsRO(makeVars["BUILD_BROKEN_SRC_DIR_IS_WRITABLE"] == "false")
+ config.sandboxConfig.SetSrcDirRWAllowlist(strings.Fields(makeVars["BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST"]))
config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true")
config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true")
diff --git a/ui/build/sandbox_config.go b/ui/build/sandbox_config.go
new file mode 100644
index 0000000..1b46459
--- /dev/null
+++ b/ui/build/sandbox_config.go
@@ -0,0 +1,36 @@
+// Copyright 2021 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
+
+type SandboxConfig struct {
+ srcDirIsRO bool
+ srcDirRWAllowlist []string
+}
+
+func (sc *SandboxConfig) SetSrcDirIsRO(ro bool) {
+ sc.srcDirIsRO = ro
+}
+
+func (sc *SandboxConfig) SrcDirIsRO() bool {
+ return sc.srcDirIsRO
+}
+
+func (sc *SandboxConfig) SetSrcDirRWAllowlist(allowlist []string) {
+ sc.srcDirRWAllowlist = allowlist
+}
+
+func (sc *SandboxConfig) SrcDirRWAllowlist() []string {
+ return sc.srcDirRWAllowlist
+}
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index dab0e75..b0a6748 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -145,6 +145,13 @@
func (c *Cmd) wrapSandbox() {
wd, _ := os.Getwd()
+ var srcDirMountFlag string
+ if c.config.sandboxConfig.SrcDirIsRO() {
+ srcDirMountFlag = "-R"
+ } else {
+ srcDirMountFlag = "-B" //Read-Write
+ }
+
sandboxArgs := []string{
// The executable to run
"-x", c.Path,
@@ -184,8 +191,8 @@
// Mount a writable tmp dir
"-B", "/tmp",
- // Mount source are read-write
- "-B", sandboxConfig.srcDir,
+ // Mount source
+ srcDirMountFlag, sandboxConfig.srcDir,
//Mount out dir as read-write
"-B", sandboxConfig.outDir,
@@ -198,6 +205,18 @@
"-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)
diff --git a/ui/build/sandbox_linux_test.go b/ui/build/sandbox_linux_test.go
new file mode 100644
index 0000000..7bfd750
--- /dev/null
+++ b/ui/build/sandbox_linux_test.go
@@ -0,0 +1,104 @@
+// Copyright 2021 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 (
+ "os"
+ "testing"
+)
+
+func TestMain(m *testing.M) {
+ // set src dir of sandbox
+ sandboxConfig.srcDir = "/my/src/dir"
+ os.Exit(m.Run())
+}
+
+func TestMountFlagsSrcDir(t *testing.T) {
+ testCases := []struct {
+ srcDirIsRO bool
+ expectedSrcDirFlag string
+ }{
+ {
+ srcDirIsRO: false,
+ expectedSrcDirFlag: "-B",
+ },
+ {
+ srcDirIsRO: true,
+ expectedSrcDirFlag: "-R",
+ },
+ }
+ for _, testCase := range testCases {
+ c := testCmd()
+ c.config.sandboxConfig.SetSrcDirIsRO(testCase.srcDirIsRO)
+ c.wrapSandbox()
+ if !isExpectedMountFlag(c.Args, sandboxConfig.srcDir, testCase.expectedSrcDirFlag) {
+ t.Error("Mount flag of srcDir is not correct")
+ }
+ }
+}
+
+func TestMountFlagsSrcDirRWAllowlist(t *testing.T) {
+ testCases := []struct {
+ srcDirRWAllowlist []string
+ }{
+ {
+ srcDirRWAllowlist: []string{},
+ },
+ {
+ srcDirRWAllowlist: []string{"my/path"},
+ },
+ {
+ srcDirRWAllowlist: []string{"my/path1", "my/path2"},
+ },
+ }
+ for _, testCase := range testCases {
+ c := testCmd()
+ c.config.sandboxConfig.SetSrcDirIsRO(true)
+ c.config.sandboxConfig.SetSrcDirRWAllowlist(testCase.srcDirRWAllowlist)
+ c.wrapSandbox()
+ for _, allowlistPath := range testCase.srcDirRWAllowlist {
+ if !isExpectedMountFlag(c.Args, allowlistPath, "-B") {
+ t.Error("Mount flag of srcDirRWAllowlist is not correct, expect -B")
+ }
+ }
+ }
+}
+
+// utils for setting up test
+func testConfig() Config {
+ // create a minimal testConfig
+ env := Environment([]string{})
+ sandboxConfig := SandboxConfig{}
+ return Config{&configImpl{environ: &env,
+ sandboxConfig: &sandboxConfig}}
+}
+
+func testCmd() *Cmd {
+ return Command(testContext(), testConfig(), "sandbox_test", "path/to/nsjail")
+}
+
+func isExpectedMountFlag(cmdArgs []string, dirName string, expectedFlag string) bool {
+ indexOfSrcDir := index(cmdArgs, dirName)
+ return cmdArgs[indexOfSrcDir-1] == expectedFlag
+}
+
+func index(arr []string, target string) int {
+ for idx, element := range arr {
+ if element == target {
+ return idx
+ }
+ }
+ panic("element could not be located in input array")
+}