Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 193ac2eb authored by Evgenii Stepanov's avatar Evgenii Stepanov
Browse files

Support "memtag_heap" sanitizer.

Memtag_heap adds an ELF note that enables MTE heap tagging in
bionic/scudo. Ignored on non-executables. With diagnostic
(diag:{memtag_heap:true}) enables the SYNC mode, otherwise - ASYNC mode.

Memtag_heap defaults to set (with diag) on cc_test targets, unset
otherwise. Ignored on non MTE-compatible hardware.

Bug: b/135772972
Test: soong tests

Change-Id: I88fd0f159e609e17bd13487749980a1ba02cb91c
parent 016370b1
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -420,6 +420,7 @@ type VendorProperties struct {
type ModuleContextIntf interface {
	static() bool
	staticBinary() bool
	testBinary() bool
	header() bool
	binary() bool
	object() bool
@@ -1261,6 +1262,10 @@ func (ctx *moduleContextImpl) staticBinary() bool {
	return ctx.mod.staticBinary()
}

func (ctx *moduleContextImpl) testBinary() bool {
	return ctx.mod.testBinary()
}

func (ctx *moduleContextImpl) header() bool {
	return ctx.mod.Header()
}
@@ -2961,6 +2966,15 @@ func (c *Module) staticBinary() bool {
	return false
}

func (c *Module) testBinary() bool {
	if test, ok := c.linker.(interface {
		testBinary() bool
	}); ok {
		return test.testBinary()
	}
	return false
}

// Header returns true if the module is a header-only variant. (See cc/library.go header()).
func (c *Module) Header() bool {
	if h, ok := c.linker.(interface {
+102 −0
Original line number Diff line number Diff line
@@ -4461,3 +4461,105 @@ func TestAidlFlagsPassedToTheAidlCompiler(t *testing.T) {
		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
	}
}

func checkHasImplicitDep(t *testing.T, m android.TestingModule, name string) {
	implicits := m.Rule("ld").Implicits
	for _, lib := range implicits {
		if strings.Contains(lib.Rel(), name) {
			return
		}
	}

	t.Errorf("%q is not found in implicit deps of module %q", name, m.Module().(*Module).Name())
}

func checkDoesNotHaveImplicitDep(t *testing.T, m android.TestingModule, name string) {
	implicits := m.Rule("ld").Implicits
	for _, lib := range implicits {
		if strings.Contains(lib.Rel(), name) {
			t.Errorf("%q is found in implicit deps of module %q", name, m.Module().(*Module).Name())
		}
	}
}

func TestSanitizeMemtagHeap(t *testing.T) {
	ctx := testCc(t, `
		cc_library_static {
			name: "libstatic",
			sanitize: { memtag_heap: true },
		}

		cc_library_shared {
			name: "libshared",
			sanitize: { memtag_heap: true },
		}

		cc_library {
			name: "libboth",
			sanitize: { memtag_heap: true },
		}

		cc_binary {
			name: "binary",
			shared_libs: [ "libshared" ],
			static_libs: [ "libstatic" ],
		}

		cc_binary {
			name: "binary_true",
			sanitize: { memtag_heap: true },
		}

		cc_binary {
			name: "binary_true_sync",
			sanitize: { memtag_heap: true, diag: { memtag_heap: true }, },
		}

		cc_binary {
			name: "binary_false",
			sanitize: { memtag_heap: false },
		}

		cc_test {
			name: "test",
			gtest: false,
		}

		cc_test {
			name: "test_true",
			gtest: false,
			sanitize: { memtag_heap: true },
		}

		cc_test {
			name: "test_false",
			gtest: false,
			sanitize: { memtag_heap: false },
		}

		cc_test {
			name: "test_true_async",
			gtest: false,
			sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
		}

		`)

	variant := "android_arm64_armv8-a"
	note_async := "note_memtag_heap_async"
	note_sync := "note_memtag_heap_sync"
	note_any := "note_memtag_"

	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("libshared", "android_arm64_armv8-a_shared"), note_any)
	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("libboth", "android_arm64_armv8-a_shared"), note_any)

	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("binary", variant), note_any)
	checkHasImplicitDep(t, ctx.ModuleForTests("binary_true", variant), note_async)
	checkHasImplicitDep(t, ctx.ModuleForTests("binary_true_sync", variant), note_sync)
	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("binary_false", variant), note_any)

	checkHasImplicitDep(t, ctx.ModuleForTests("test", variant), note_sync)
	checkHasImplicitDep(t, ctx.ModuleForTests("test_true", variant), note_async)
	checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("test_false", variant), note_any)
	checkHasImplicitDep(t, ctx.ModuleForTests("test_true_async", variant), note_async)
}
+41 −1
Original line number Diff line number Diff line
@@ -89,6 +89,7 @@ const (
	cfi
	scs
	Fuzzer
	memtag_heap
)

// Name of the sanitizer variation for this sanitizer type
@@ -106,6 +107,8 @@ func (t SanitizerType) variationName() string {
		return "cfi"
	case scs:
		return "scs"
	case memtag_heap:
		return "memtag_heap"
	case Fuzzer:
		return "fuzzer"
	default:
@@ -120,6 +123,8 @@ func (t SanitizerType) name() string {
		return "address"
	case hwasan:
		return "hwaddress"
	case memtag_heap:
		return "memtag_heap"
	case tsan:
		return "thread"
	case intOverflow:
@@ -179,6 +184,7 @@ type SanitizeUserProps struct {
	Integer_overflow *bool    `android:"arch_variant"`
	Scudo            *bool    `android:"arch_variant"`
	Scs              *bool    `android:"arch_variant"`
	Memtag_heap      *bool    `android:"arch_variant"`

	// A modifier for ASAN and HWASAN for write only instrumentation
	Writeonly *bool `android:"arch_variant"`
@@ -190,6 +196,7 @@ type SanitizeUserProps struct {
		Undefined        *bool    `android:"arch_variant"`
		Cfi              *bool    `android:"arch_variant"`
		Integer_overflow *bool    `android:"arch_variant"`
		Memtag_heap      *bool    `android:"arch_variant"`
		Misc_undefined   []string `android:"arch_variant"`
		No_recover       []string `android:"arch_variant"`
	} `android:"arch_variant"`
@@ -330,6 +337,9 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) {
			}
			s.Writeonly = boolPtr(true)
		}
		if found, globalSanitizers = removeFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil {
			s.Memtag_heap = boolPtr(true)
		}

		if len(globalSanitizers) > 0 {
			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
@@ -351,6 +361,12 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) {
		}
	}

	// cc_test targets default to SYNC MemTag.
	if ctx.testBinary() && s.Memtag_heap == nil {
		s.Memtag_heap = boolPtr(true)
		s.Diag.Memtag_heap = boolPtr(true)
	}

	// Enable CFI for all components in the include paths (for Aarch64 only)
	if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
		s.Cfi = boolPtr(true)
@@ -381,6 +397,11 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) {
		s.Scs = nil
	}

	// memtag_heap is only implemented on AArch64.
	if ctx.Arch().ArchType != android.Arm64 {
		s.Memtag_heap = nil
	}

	// Also disable CFI if ASAN is enabled.
	if Bool(s.Address) || Bool(s.Hwaddress) {
		s.Cfi = boolPtr(false)
@@ -435,7 +456,7 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) {

	if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
		Bool(s.Fuzzer) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs)) {
		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap)) {
		sanitize.Properties.SanitizerEnabled = true
	}

@@ -717,6 +738,8 @@ func (sanitize *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
		return sanitize.Properties.Sanitize.Cfi
	case scs:
		return sanitize.Properties.Sanitize.Scs
	case memtag_heap:
		return sanitize.Properties.Sanitize.Memtag_heap
	case Fuzzer:
		return sanitize.Properties.Sanitize.Fuzzer
	default:
@@ -731,6 +754,7 @@ func (sanitize *sanitize) isUnsanitizedVariant() bool {
		!sanitize.isSanitizerEnabled(tsan) &&
		!sanitize.isSanitizerEnabled(cfi) &&
		!sanitize.isSanitizerEnabled(scs) &&
		!sanitize.isSanitizerEnabled(memtag_heap) &&
		!sanitize.isSanitizerEnabled(Fuzzer)
}

@@ -756,6 +780,8 @@ func (sanitize *sanitize) SetSanitizer(t SanitizerType, b bool) {
		sanitize.Properties.Sanitize.Cfi = boolPtr(b)
	case scs:
		sanitize.Properties.Sanitize.Scs = boolPtr(b)
	case memtag_heap:
		sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b)
	case Fuzzer:
		sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
	default:
@@ -1032,6 +1058,20 @@ func sanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) {
			sanitizers = append(sanitizers, "shadow-call-stack")
		}

		if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.binary() {
			noteDep := "note_memtag_heap_async"
			if Bool(c.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
				noteDep = "note_memtag_heap_sync"
			}
			depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true}
			variations := append(mctx.Target().Variations(),
				blueprint.Variation{Mutator: "link", Variation: "static"})
			if c.Device() {
				variations = append(variations, c.ImageVariation())
			}
			mctx.AddFarVariationDependencies(variations, depTag, noteDep)
		}

		if Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
			sanitizers = append(sanitizers, "fuzzer-no-link")
		}
+4 −0
Original line number Diff line number Diff line
@@ -236,6 +236,10 @@ func (test *testDecorator) gtest() bool {
	return BoolDefault(test.Properties.Gtest, true)
}

func (test *testDecorator) testBinary() bool {
	return true
}

func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
	if !test.gtest() {
		return flags
+8 −0
Original line number Diff line number Diff line
@@ -445,6 +445,14 @@ func GatherRequiredDepsForTest(oses ...android.OsType) string {
			stl: "none",
			system_shared_libs: [],
		}

		cc_library_static {
			name: "note_memtag_heap_async",
		}

		cc_library_static {
			name: "note_memtag_heap_sync",
		}
	`

	supportLinuxBionic := false