diff --git a/build/soong/Android.bp b/build/soong/Android.bp index 0abfdf61e..3f2e2df13 100644 --- a/build/soong/Android.bp +++ b/build/soong/Android.bp @@ -47,5 +47,6 @@ bootstrap_go_package { "service_fuzzer_bindings.go", "validate_bindings.go", ], + testSrcs: ["selinux_test.go"], pluginFor: ["soong_build"], } diff --git a/build/soong/flags.go b/build/soong/flags.go index b1aebac75..c7aeb3217 100644 --- a/build/soong/flags.go +++ b/build/soong/flags.go @@ -15,22 +15,141 @@ package selinux import ( + "maps" + "android/soong/android" + + "github.com/google/blueprint" ) +var ( + flagsDepTag = dependencyTag{name: "flags"} + buildFlagsDepTag = dependencyTag{name: "build_flags"} +) + +func init() { + ctx := android.InitRegistrationContext + ctx.RegisterModuleType("se_flags", flagsFactory) + ctx.RegisterModuleType("se_flags_collector", flagsCollectorFactory) +} + type flagsProperties struct { - // List of flags to be passed to M4 macro. + // List of build time flags for flag-guarding. Flags []string + + // List of se_flags_collector modules to export flags to. + Export_to []string +} + +type flagsModule struct { + android.ModuleBase + properties flagsProperties +} + +type flagsInfo struct { + Flags []string +} + +var flagsProviderKey = blueprint.NewProvider[flagsInfo]() + +// se_flags contains a list of build time flags for sepolicy. Build time flags are defined under +// .scl files (e.g. build/release/build_flags.scl). By importing flags with se_flags modules, +// sepolicy rules can be guarded by `is_flag_enabled` / `is_flag_disabled` macro. +// +// For example, an Android.bp file could have: +// +// se_flags { +// name: "aosp_selinux_flags", +// flags: ["RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT"], +// export_to: ["all_selinux_flags"], +// } +// +// And then one could flag-guard .te file rules: +// +// is_flag_enabled(RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT, ` +// type vfio_handler, domain, coredomain; +// binder_use(vfio_handler) +// ') +// +// or contexts entries: +// +// is_flag_enabled(RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT, ` +// android.system.virtualizationservice_internal.IVfioHandler u:object_r:vfio_handler_service:s0 +// ') +func flagsFactory() android.Module { + module := &flagsModule{} + module.AddProperties(&module.properties) + android.InitAndroidModule(module) + return module +} + +func (f *flagsModule) DepsMutator(ctx android.BottomUpMutatorContext) { + // dep se_flag_collector -> se_flags + for _, export := range f.properties.Export_to { + ctx.AddReverseDependency(ctx.Module(), flagsDepTag, export) + } +} + +func (f *flagsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + android.SetProvider(ctx, flagsProviderKey, flagsInfo{ + Flags: f.properties.Flags, + }) +} + +type buildFlagsInfo struct { + BuildFlags map[string]string +} + +var buildFlagsProviderKey = blueprint.NewProvider[buildFlagsInfo]() + +type flagsCollectorModule struct { + android.ModuleBase + buildFlags map[string]string +} + +// se_flags_collector module collects flags from exported se_flags modules (see export_to property +// of se_flags modules), and then converts them into build-time flags. It will be used to generate +// M4 macros to flag-guard sepolicy. +func flagsCollectorFactory() android.Module { + module := &flagsCollectorModule{} + android.InitAndroidModule(module) + return module +} + +func (f *flagsCollectorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + var flags []string + ctx.VisitDirectDepsWithTag(flagsDepTag, func(m android.Module) { + if dep, ok := android.OtherModuleProvider(ctx, m, flagsProviderKey); ok { + flags = append(flags, dep.Flags...) + } else { + ctx.ModuleErrorf("unknown dependency %q", ctx.OtherModuleName(m)) + } + }) + buildFlags := make(map[string]string) + for _, flag := range android.SortedUniqueStrings(flags) { + if val, ok := ctx.Config().GetBuildFlag(flag); ok { + buildFlags[flag] = val + } + } + android.SetProvider(ctx, buildFlagsProviderKey, buildFlagsInfo{ + BuildFlags: buildFlags, + }) +} + +type flaggableModuleProperties struct { + // List of se_flag_collector modules to be passed to M4 macro. + Build_flags []string } type flaggableModule interface { android.Module flagModuleBase() *flaggableModuleBase + flagDeps(ctx android.BottomUpMutatorContext) getBuildFlags(ctx android.ModuleContext) map[string]string } type flaggableModuleBase struct { - properties flagsProperties + properties flaggableModuleProperties } func initFlaggableModule(m flaggableModule) { @@ -42,13 +161,19 @@ func (f *flaggableModuleBase) flagModuleBase() *flaggableModuleBase { return f } +func (f *flaggableModuleBase) flagDeps(ctx android.BottomUpMutatorContext) { + ctx.AddDependency(ctx.Module(), buildFlagsDepTag, f.properties.Build_flags...) +} + // getBuildFlags returns a map from flag names to flag values. func (f *flaggableModuleBase) getBuildFlags(ctx android.ModuleContext) map[string]string { ret := make(map[string]string) - for _, flag := range android.SortedUniqueStrings(f.properties.Flags) { - if val, ok := ctx.Config().GetBuildFlag(flag); ok { - ret[flag] = val + ctx.VisitDirectDepsWithTag(buildFlagsDepTag, func(m android.Module) { + if dep, ok := android.OtherModuleProvider(ctx, m, buildFlagsProviderKey); ok { + maps.Copy(ret, dep.BuildFlags) + } else { + ctx.PropertyErrorf("build_flags", "unknown dependency %q", ctx.OtherModuleName(m)) } - } + }) return ret } diff --git a/build/soong/policy.go b/build/soong/policy.go index 9d8727550..cbcc57ae6 100644 --- a/build/soong/policy.go +++ b/build/soong/policy.go @@ -129,7 +129,7 @@ func policyConfDefaultFactory() android.Module { c := &policyConfDefaults{} c.AddProperties( &policyConfProperties{}, - &flagsProperties{}, + &flaggableModuleProperties{}, ) android.InitDefaultsModule(c) return c @@ -270,6 +270,10 @@ func (c *policyConf) transformPolicyToConf(ctx android.ModuleContext) android.Ou return conf } +func (c *policyConf) DepsMutator(ctx android.BottomUpMutatorContext) { + c.flagDeps(ctx) +} + func (c *policyConf) GenerateAndroidBuildActions(ctx android.ModuleContext) { if !c.installable() { c.SkipInstall() diff --git a/build/soong/selinux_contexts.go b/build/soong/selinux_contexts.go index 5cc9c709f..1282b9041 100644 --- a/build/soong/selinux_contexts.go +++ b/build/soong/selinux_contexts.go @@ -110,6 +110,8 @@ func (m *selinuxContextsModule) onlyInRecovery() bool { } func (m *selinuxContextsModule) DepsMutator(ctx android.BottomUpMutatorContext) { + m.flagDeps(ctx) + if m.deps != nil { m.deps(ctx) } @@ -182,7 +184,7 @@ func contextsDefaultsFactory() android.Module { m.AddProperties( &selinuxContextsProperties{}, &seappProperties{}, - &flagsProperties{}, + &flaggableModuleProperties{}, ) android.InitDefaultsModule(m) return m diff --git a/build/soong/selinux_test.go b/build/soong/selinux_test.go new file mode 100644 index 000000000..dd980a552 --- /dev/null +++ b/build/soong/selinux_test.go @@ -0,0 +1,96 @@ +// Copyright 2024 The Android Open Source Project +// +// 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 selinux + +import ( + "os" + "reflect" + "testing" + + "android/soong/android" +) + +func TestMain(m *testing.M) { + os.Exit(m.Run()) +} + +var prepareForTest = android.GroupFixturePreparers( + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + buildFlags := make(map[string]string) + buildFlags["RELEASE_FLAGS_BAR"] = "true" + buildFlags["RELEASE_FLAGS_FOO1"] = "false" + // "RELEASE_FLAGS_FOO2" is missing + buildFlags["RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT"] = "true" + variables.BuildFlags = buildFlags + }), + android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + ctx.RegisterModuleType("se_flags", flagsFactory) + ctx.RegisterModuleType("se_flags_collector", flagsCollectorFactory) + }), +) + +func TestFlagCollector(t *testing.T) { + t.Parallel() + + ctx := android.GroupFixturePreparers( + prepareForTest, + android.FixtureAddTextFile("package_bar/Android.bp", ` + se_flags { + name: "se_flags_bar", + flags: ["RELEASE_FLAGS_BAR"], + export_to: ["se_flags_collector"], + } + `), + android.FixtureAddTextFile("package_foo/Android.bp", ` + se_flags { + name: "se_flags_foo", + flags: ["RELEASE_FLAGS_FOO1", "RELEASE_FLAGS_FOO2"], + export_to: ["se_flags_collector"], + } + `), + android.FixtureAddTextFile("system/sepolicy/Android.bp", ` + se_flags { + name: "se_flags", + flags: ["RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT"], + export_to: ["se_flags_collector"], + } + se_flags_collector { + name: "se_flags_collector", + } + `), + ).RunTest(t).TestContext + + collectorModule := ctx.ModuleForTests("se_flags_collector", "").Module() + collectorData, ok := android.OtherModuleProvider(ctx.OtherModuleProviderAdaptor(), collectorModule, buildFlagsProviderKey) + if !ok { + t.Errorf("se_flags_collector must provide buildFlags") + return + } + + actual := flagsToM4Macros(collectorData.BuildFlags) + expected := []string{ + "-D target_flag_RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT=true", + "-D target_flag_RELEASE_FLAGS_BAR=true", + "-D target_flag_RELEASE_FLAGS_FOO1=false", + } + if !reflect.DeepEqual(actual, expected) { + t.Errorf("M4 macros were not exported correctly"+ + "\nactual: %v"+ + "\nexpected: %v", + actual, + expected, + ) + } +} diff --git a/flagging/Android.bp b/flagging/Android.bp index 55e116b41..8f7355a97 100644 --- a/flagging/Android.bp +++ b/flagging/Android.bp @@ -12,24 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. -// This file contains a list of flags for sepolicy. -se_policy_conf_defaults { - name: "se_policy_conf_flags_defaults", - srcs: [":sepolicy_flagging_macros"], +// This module contains a list of build time flags (defined on AOSP) for sepolicy. +// Additional se_flags modules can be added anywhere for additional flags. +se_flags { + name: "aosp_selinux_flags", flags: [ "RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT", "RELEASE_HARDWARE_BLUETOOTH_RANGING_SERVICE", ], + export_to: ["all_selinux_flags"], +} + +// se_flags_collector collects flags from exported se_flags modules and converts it to build flags. +se_flags_collector { + name: "all_selinux_flags", +} + +se_policy_conf_defaults { + name: "se_policy_conf_flags_defaults", + srcs: [":sepolicy_flagging_macros"], + build_flags: ["all_selinux_flags"], } contexts_defaults { name: "contexts_flags_defaults", srcs: [":sepolicy_flagging_macros"], neverallow_files: [":sepolicy_flagging_macros"], // for seapp_contexts - flags: [ - "RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT", - "RELEASE_HARDWARE_BLUETOOTH_RANGING_SERVICE", - ], + build_flags: ["all_selinux_flags"], } filegroup {