1// Copyright 2021 The Android Open Source Project 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 15package selinux 16 17import ( 18 "sort" 19 20 "android/soong/android" 21) 22 23func init() { 24 ctx := android.InitRegistrationContext 25 ctx.RegisterModuleType("se_freeze_test", freezeTestFactory) 26} 27 28// se_freeze_test compares the plat sepolicy with the prebuilt sepolicy. Additional directories can 29// be specified via Makefile variables: SEPOLICY_FREEZE_TEST_EXTRA_DIRS and 30// SEPOLICY_FREEZE_TEST_EXTRA_PREBUILT_DIRS. 31func freezeTestFactory() android.Module { 32 f := &freezeTestModule{} 33 f.AddProperties(&f.properties) 34 android.InitAndroidArchModule(f, android.DeviceSupported, android.MultilibCommon) 35 return f 36} 37 38type freezeTestProperties struct { 39 // Frozen SEPolicy version to compare 40 Board_api_level *string 41 42 // Path to the base platform public policy cil 43 Current_cil *string `android:"path"` 44 45 // Path to the prebuilt cil of given board API level 46 Prebuilt_cil *string `android:"path"` 47} 48 49type freezeTestModule struct { 50 android.ModuleBase 51 52 properties freezeTestProperties 53 54 freezeTestTimestamp android.ModuleOutPath 55} 56 57func (f *freezeTestModule) shouldCompareExtraDirs(ctx android.EarlyModuleContext) bool { 58 val, _ := ctx.Config().GetBuildFlag("RELEASE_BOARD_API_LEVEL_FROZEN") 59 return val == "true" 60} 61 62func (f *freezeTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 63 if ctx.ModuleName() != "se_freeze_test" || ctx.ModuleDir() != "system/sepolicy" { 64 // two freeze test modules don't make sense. 65 ctx.ModuleErrorf("There can only be 1 se_freeze_test module named se_freeze_test in system/sepolicy") 66 } 67 68 f.freezeTestTimestamp = android.PathForModuleOut(ctx, "freeze_test") 69 70 // Freeze test 1: compare ToT sepolicy and prebuilt sepolicy 71 currentCil := android.PathForModuleSrc(ctx, String(f.properties.Current_cil)) 72 prebuiltCil := android.PathForModuleSrc(ctx, String(f.properties.Prebuilt_cil)) 73 if ctx.Failed() { 74 return 75 } 76 77 rule := android.NewRuleBuilder(pctx, ctx) 78 rule.Command().BuiltTool("sepolicy_freeze_test"). 79 FlagWithInput("-c ", currentCil). 80 FlagWithInput("-p ", prebuiltCil) 81 82 // Freeze test 2: compare extra directories 83 // We don't know the exact structure of extra directories, so just directly compare them 84 extraDirs := ctx.DeviceConfig().SepolicyFreezeTestExtraDirs() 85 extraPrebuiltDirs := ctx.DeviceConfig().SepolicyFreezeTestExtraPrebuiltDirs() 86 87 var implicits []string 88 if f.shouldCompareExtraDirs(ctx) { 89 if len(extraDirs) != len(extraPrebuiltDirs) { 90 ctx.ModuleErrorf("SEPOLICY_FREEZE_TEST_EXTRA_DIRS and SEPOLICY_FREEZE_TEST_EXTRA_PREBUILT_DIRS must have the same number of directories.") 91 return 92 } 93 94 for _, dir := range append(extraDirs, extraPrebuiltDirs...) { 95 glob, err := ctx.GlobWithDeps(dir+"/**/*", []string{"bug_map"} /* exclude */) 96 if err != nil { 97 ctx.ModuleErrorf("failed to glob sepolicy dir %q: %s", dir, err.Error()) 98 return 99 } 100 implicits = append(implicits, glob...) 101 } 102 sort.Strings(implicits) 103 104 for idx, _ := range extraDirs { 105 rule.Command().Text("diff"). 106 Flag("-r"). 107 Flag("-q"). 108 FlagWithArg("-x ", "bug_map"). // exclude 109 Text(extraDirs[idx]). 110 Text(extraPrebuiltDirs[idx]) 111 } 112 } else { 113 if len(extraDirs) > 0 || len(extraPrebuiltDirs) > 0 { 114 ctx.ModuleErrorf("SEPOLICY_FREEZE_TEST_EXTRA_DIRS or SEPOLICY_FREEZE_TEST_EXTRA_PREBUILT_DIRS cannot be set before system/sepolicy freezes.") 115 return 116 } 117 } 118 119 rule.Command().Text("touch"). 120 Output(f.freezeTestTimestamp). 121 Implicits(android.PathsForSource(ctx, implicits)) 122 123 rule.Build("sepolicy_freeze_test", "sepolicy_freeze_test") 124} 125 126func (f *freezeTestModule) AndroidMkEntries() []android.AndroidMkEntries { 127 return []android.AndroidMkEntries{android.AndroidMkEntries{ 128 Class: "FAKE", 129 // OutputFile is needed, even though BUILD_PHONY_PACKAGE doesn't use it. 130 // Without OutputFile this module won't be exported to Makefile. 131 OutputFile: android.OptionalPathForPath(f.freezeTestTimestamp), 132 Include: "$(BUILD_PHONY_PACKAGE)", 133 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 134 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 135 entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", f.freezeTestTimestamp.String()) 136 }, 137 }, 138 }} 139} 140