Browse Source

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
Spandan Das 3 years ago
parent
commit
a3639e62cd

+ 4 - 0
ui/build/Android.bp

@@ -60,6 +60,7 @@ bootstrap_go_package {
         "path.go",
         "proc_sync.go",
         "rbe.go",
+        "sandbox_config.go",
         "signal.go",
         "soong.go",
         "test_build.go",
@@ -86,5 +87,8 @@ bootstrap_go_package {
             "config_linux.go",
             "sandbox_linux.go",
         ],
+        testSrcs: [
+            "sandbox_linux_test.go",
+        ],
     },
 }

+ 3 - 1
ui/build/config.go

@@ -57,6 +57,7 @@ type configImpl struct {
 	katiSuffix      string
 	targetDevice    string
 	targetDeviceDir string
+	sandboxConfig   *SandboxConfig
 
 	// Autodetected
 	totalRAM uint64
@@ -120,7 +121,8 @@ func checkTopDir(ctx Context) {
 
 func NewConfig(ctx Context, args ...string) Config {
 	ret := &configImpl{
-		environ: OsEnvironment(),
+		environ:       OsEnvironment(),
+		sandboxConfig: &SandboxConfig{},
 	}
 
 	// Default matching ninja

+ 6 - 0
ui/build/dumpvars.go

@@ -225,6 +225,10 @@ func runMakeProductConfig(ctx Context, config Config) {
 		// 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 @@ func runMakeProductConfig(ctx Context, config Config) {
 	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")

+ 36 - 0
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
+}

+ 21 - 2
ui/build/sandbox_linux.go

@@ -145,6 +145,13 @@ func (c *Cmd) sandboxSupported() bool {
 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 @@ func (c *Cmd) wrapSandbox() {
 		// 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 @@ func (c *Cmd) wrapSandbox() {
 		"-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)

+ 104 - 0
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")
+}