Add "hwaddress" sanitizer.
This is a new sanitizer similar to ASan, but with a few differences
from the build system perspective:
* Only runs on AArch64.
* Supports static binaries.
* Bionic itself will be built with HWASan.
* Does not have any "if a library is sanitized than the binary must
be, too" requirements unlike ASan. Even better, individual static
libraries can be freely sanitized or not. We propagate "nosanitize"
from binaries to static libraries anyway, because otherwise there
is no good way to disable hwasan on a binary as a whole.
Same a CFI, we export a list of sanitized static libraries to make.
In fact, we export separate lists for vendor and regular libraries,
because it is possible for one to be sanitized without the other
(i.e. there can be mylib.hwasan.vendor w/o mylib.hwasan or vice
versa).
Bug: 112438058, 112709969
Test: manual, part of a bigger patch set
Change-Id: Ie4fdeb522ac03cf9684526882e84dfee3807b6a7
diff --git a/cc/cc.go b/cc/cc.go
index bde8041..7f65640 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -46,6 +46,9 @@
ctx.TopDown("asan_deps", sanitizerDepsMutator(asan))
ctx.BottomUp("asan", sanitizerMutator(asan)).Parallel()
+ ctx.TopDown("hwasan_deps", sanitizerDepsMutator(hwasan))
+ ctx.BottomUp("hwasan", sanitizerMutator(hwasan)).Parallel()
+
ctx.TopDown("cfi_deps", sanitizerDepsMutator(cfi))
ctx.BottomUp("cfi", sanitizerMutator(cfi)).Parallel()
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index a360a17..39b5df4 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -226,6 +226,14 @@
return SanitizerRuntimeLibrary(t, "asan")
}
+func HWAddressSanitizerRuntimeLibrary(t Toolchain) string {
+ return SanitizerRuntimeLibrary(t, "hwasan")
+}
+
+func HWAddressSanitizerStaticLibrary(t Toolchain) string {
+ return SanitizerRuntimeLibrary(t, "hwasan_static")
+}
+
func UndefinedBehaviorSanitizerRuntimeLibrary(t Toolchain) string {
return SanitizerRuntimeLibrary(t, "ubsan_standalone")
}
diff --git a/cc/makevars.go b/cc/makevars.go
index 88d4639..072821c 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -300,6 +300,8 @@
if target.Os.Class == android.Device {
ctx.Strict(secondPrefix+"ADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.AddressSanitizerRuntimeLibrary(toolchain), ".so"))
+ ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerRuntimeLibrary(toolchain), ".so"))
+ ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_STATIC_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerStaticLibrary(toolchain), ".a"))
ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
ctx.Strict(secondPrefix+"UBSAN_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), ".a"))
ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 1037181..a522a60 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -36,12 +36,15 @@
asanLdflags = []string{"-Wl,-u,__asan_preinit"}
asanLibs = []string{"libasan"}
+ hwasanCflags = []string{"-mllvm", "-hwasan-with-ifunc=0", "-fno-omit-frame-pointer", "-Wno-frame-larger-than="}
+
cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blacklist.txt"}
cfiLdflags = []string{"-flto", "-fsanitize-cfi-cross-dso", "-fsanitize=cfi",
"-Wl,-plugin-opt,O1"}
- cfiExportsMapPath = "build/soong/cc/config/cfi_exports.map"
- cfiStaticLibsMutex sync.Mutex
+ cfiExportsMapPath = "build/soong/cc/config/cfi_exports.map"
+ cfiStaticLibsMutex sync.Mutex
+ hwasanStaticLibsMutex sync.Mutex
intOverflowCflags = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blacklist.txt"}
minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer", "-fno-sanitize-recover=integer"}
@@ -59,6 +62,7 @@
const (
asan sanitizerType = iota + 1
+ hwasan
tsan
intOverflow
cfi
@@ -68,6 +72,8 @@
switch t {
case asan:
return "asan"
+ case hwasan:
+ return "hwasan"
case tsan:
return "tsan"
case intOverflow:
@@ -85,8 +91,9 @@
Never *bool `android:"arch_variant"`
// main sanitizers
- Address *bool `android:"arch_variant"`
- Thread *bool `android:"arch_variant"`
+ Address *bool `android:"arch_variant"`
+ Thread *bool `android:"arch_variant"`
+ Hwaddress *bool `android:"arch_variant"`
// local sanitizers
Undefined *bool `android:"arch_variant"`
@@ -131,6 +138,7 @@
func init() {
android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
+ android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
}
func (sanitize *sanitize) props() []interface{} {
@@ -216,6 +224,10 @@
s.Scudo = boolPtr(true)
}
+ if found, globalSanitizers = removeFromList("hwaddress", globalSanitizers); found && s.Hwaddress == nil {
+ s.Hwaddress = boolPtr(true)
+ }
+
if len(globalSanitizers) > 0 {
ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
}
@@ -256,8 +268,13 @@
s.Diag.Cfi = nil
}
+ // HWASan requires AArch64 hardware feature (top-byte-ignore).
+ if ctx.Arch().ArchType != android.Arm64 {
+ s.Hwaddress = nil
+ }
+
// Also disable CFI if ASAN is enabled.
- if Bool(s.Address) {
+ if Bool(s.Address) || Bool(s.Hwaddress) {
s.Cfi = nil
s.Diag.Cfi = nil
}
@@ -278,6 +295,11 @@
s.Diag.Cfi = nil
}
+ // HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
+ if ctx.inRecovery() {
+ s.Hwaddress = nil
+ }
+
if ctx.staticBinary() {
s.Address = nil
s.Coverage = nil
@@ -297,15 +319,20 @@
if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
Bool(s.Coverage) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
- Bool(s.Scudo)) {
+ Bool(s.Scudo) || Bool(s.Hwaddress)) {
sanitize.Properties.SanitizerEnabled = true
}
// Disable Scudo if ASan or TSan is enabled.
- if Bool(s.Address) || Bool(s.Thread) {
+ if Bool(s.Address) || Bool(s.Thread) || Bool(s.Hwaddress) {
s.Scudo = nil
}
+ if Bool(s.Hwaddress) {
+ s.Address = nil
+ s.Thread = nil
+ }
+
if Bool(s.Coverage) {
if !Bool(s.Address) {
ctx.ModuleErrorf(`Use of "coverage" also requires "address"`)
@@ -407,6 +434,11 @@
diagSanitizers = append(diagSanitizers, "address")
}
+ if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ flags.CFlags = append(flags.CFlags, hwasanCflags...)
+ sanitizers = append(sanitizers, "hwaddress")
+ }
+
if Bool(sanitize.Properties.Sanitize.Thread) {
sanitizers = append(sanitizers, "thread")
}
@@ -494,6 +526,8 @@
runtimeLibrary := ""
if Bool(sanitize.Properties.Sanitize.Address) {
runtimeLibrary = config.AddressSanitizerRuntimeLibrary(ctx.toolchain())
+ } else if Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ runtimeLibrary = config.HWAddressSanitizerRuntimeLibrary(ctx.toolchain())
} else if Bool(sanitize.Properties.Sanitize.Thread) {
runtimeLibrary = config.ThreadSanitizerRuntimeLibrary(ctx.toolchain())
} else if Bool(sanitize.Properties.Sanitize.Scudo) {
@@ -542,6 +576,9 @@
if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Cfi) {
ret.SubName += ".cfi"
}
+ if ret.Class == "STATIC_LIBRARIES" && Bool(sanitize.Properties.Sanitize.Hwaddress) {
+ ret.SubName += ".hwasan"
+ }
}
func (sanitize *sanitize) inSanitizerDir() bool {
@@ -552,6 +589,8 @@
switch t {
case asan:
return sanitize.Properties.Sanitize.Address
+ case hwasan:
+ return sanitize.Properties.Sanitize.Hwaddress
case tsan:
return sanitize.Properties.Sanitize.Thread
case intOverflow:
@@ -565,12 +604,14 @@
func (sanitize *sanitize) isUnsanitizedVariant() bool {
return !sanitize.isSanitizerEnabled(asan) &&
+ !sanitize.isSanitizerEnabled(hwasan) &&
!sanitize.isSanitizerEnabled(tsan) &&
!sanitize.isSanitizerEnabled(cfi)
}
func (sanitize *sanitize) isVariantOnProductionDevice() bool {
return !sanitize.isSanitizerEnabled(asan) &&
+ !sanitize.isSanitizerEnabled(hwasan) &&
!sanitize.isSanitizerEnabled(tsan)
}
@@ -581,6 +622,8 @@
if !b {
sanitize.Properties.Sanitize.Coverage = nil
}
+ case hwasan:
+ sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
case tsan:
sanitize.Properties.Sanitize.Thread = boolPtr(b)
case intOverflow:
@@ -625,7 +668,7 @@
return ok && t.library || t == reuseObjTag
}
-// Propagate asan requirements down from binaries
+// Propagate sanitizer requirements down from binaries
func sanitizerDepsMutator(t sanitizerType) func(android.TopDownMutatorContext) {
return func(mctx android.TopDownMutatorContext) {
if c, ok := mctx.Module().(*Module); ok && c.sanitize.isSanitizerEnabled(t) {
@@ -636,7 +679,11 @@
if d, ok := child.(*Module); ok && d.sanitize != nil &&
!Bool(d.sanitize.Properties.Sanitize.Never) &&
!d.sanitize.isSanitizerExplicitlyDisabled(t) {
- if (t == cfi && d.static()) || t != cfi {
+ if t == cfi || t == hwasan {
+ if d.static() {
+ d.sanitize.Properties.SanitizeDep = true
+ }
+ } else {
d.sanitize.Properties.SanitizeDep = true
}
}
@@ -728,6 +775,34 @@
modules[1].(*Module).Properties.PreventInstall = true
modules[1].(*Module).Properties.HideFromMake = true
}
+ } else if t == hwasan {
+ if mctx.Device() {
+ // CFI and HWASAN are currently mutually exclusive so disable
+ // CFI if this is an HWASAN variant.
+ modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
+ }
+
+ if c.static() {
+ if c.useVndk() {
+ hwasanVendorStaticLibs := hwasanVendorStaticLibs(mctx.Config())
+ hwasanStaticLibsMutex.Lock()
+ *hwasanVendorStaticLibs = append(*hwasanVendorStaticLibs, c.Name())
+ hwasanStaticLibsMutex.Unlock()
+ } else {
+ hwasanStaticLibs := hwasanStaticLibs(mctx.Config())
+ hwasanStaticLibsMutex.Lock()
+ *hwasanStaticLibs = append(*hwasanStaticLibs, c.Name())
+ hwasanStaticLibsMutex.Unlock()
+ }
+ } else {
+ if isSanitizerEnabled {
+ modules[0].(*Module).Properties.PreventInstall = true
+ modules[0].(*Module).Properties.HideFromMake = true
+ } else {
+ modules[1].(*Module).Properties.PreventInstall = true
+ modules[1].(*Module).Properties.HideFromMake = true
+ }
+ }
}
}
c.sanitize.Properties.SanitizeDep = false
@@ -741,8 +816,21 @@
}).(*[]string)
}
+func hwasanStaticLibs(config android.Config) *[]string {
+ return config.Once("hwasanStaticLibs", func() interface{} {
+ return &[]string{}
+ }).(*[]string)
+}
+
+func hwasanVendorStaticLibs(config android.Config) *[]string {
+ return config.Once("hwasanVendorStaticLibs", func() interface{} {
+ return &[]string{}
+ }).(*[]string)
+}
+
func enableMinimalRuntime(sanitize *sanitize) bool {
if !Bool(sanitize.Properties.Sanitize.Address) &&
+ !Bool(sanitize.Properties.Sanitize.Hwaddress) &&
!Bool(sanitize.Properties.Sanitize.Scudo) &&
(Bool(sanitize.Properties.Sanitize.Integer_overflow) ||
len(sanitize.Properties.Sanitize.Misc_undefined) > 0) &&
@@ -759,3 +847,13 @@
sort.Strings(*cfiStaticLibs)
ctx.Strict("SOONG_CFI_STATIC_LIBRARIES", strings.Join(*cfiStaticLibs, " "))
}
+
+func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
+ hwasanStaticLibs := hwasanStaticLibs(ctx.Config())
+ sort.Strings(*hwasanStaticLibs)
+ ctx.Strict("SOONG_HWASAN_STATIC_LIBRARIES", strings.Join(*hwasanStaticLibs, " "))
+
+ hwasanVendorStaticLibs := hwasanVendorStaticLibs(ctx.Config())
+ sort.Strings(*hwasanVendorStaticLibs)
+ ctx.Strict("SOONG_HWASAN_VENDOR_STATIC_LIBRARIES", strings.Join(*hwasanVendorStaticLibs, " "))
+}