mm/kasan: Kasan global support setting alignment length

1. Similar to asan, supports single byte out of bounds detection
2. Fix the script to address the issue of not supporting the big end

Signed-off-by: wangmingrong1 <wangmingrong1@xiaomi.com>
This commit is contained in:
wangmingrong1 2024-09-20 10:53:23 +08:00 committed by Xiang Xiao
parent 0d8b6de03a
commit 469418f3c9
7 changed files with 73 additions and 34 deletions

View File

@ -196,7 +196,7 @@ define LINK_ALLSYMS_KASAN
$(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c) $(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c)
$(Q) $(call DELFILE, allsyms.tmp)) $(Q) $(call DELFILE, allsyms.tmp))
$(if $(CONFIG_MM_KASAN_GLOBAL), $(if $(CONFIG_MM_KASAN_GLOBAL),
$(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp $(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp -a $(CONFIG_MM_KASAN_GLOBAL_ALIGN)
$(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c) $(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c)
$(Q) $(call DELFILE, kasan_globals.tmp)) $(Q) $(call DELFILE, kasan_globals.tmp))
$(Q) $(LD) $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \ $(Q) $(LD) $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \

View File

@ -171,7 +171,7 @@ define LINK_ALLSYMS_KASAN
$(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c) $(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c)
$(Q) $(call DELFILE, allsyms.tmp)) $(Q) $(call DELFILE, allsyms.tmp))
$(if $(CONFIG_MM_KASAN_GLOBAL), $(if $(CONFIG_MM_KASAN_GLOBAL),
$(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp $(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp -a $(CONFIG_MM_KASAN_GLOBAL_ALIGN)
$(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c) $(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c)
$(Q) $(call DELFILE, kasan_globals.tmp)) $(Q) $(call DELFILE, kasan_globals.tmp))
$(Q) $(LD) --entry=__start $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \ $(Q) $(LD) --entry=__start $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \

View File

@ -173,7 +173,7 @@ define LINK_ALLSYMS_KASAN
$(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c) $(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c)
$(Q) $(call DELFILE, allsyms.tmp)) $(Q) $(call DELFILE, allsyms.tmp))
$(if $(CONFIG_MM_KASAN_GLOBAL), $(if $(CONFIG_MM_KASAN_GLOBAL),
$(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp $(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp -a $(CONFIG_MM_KASAN_GLOBAL_ALIGN)
$(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c) $(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c)
$(Q) $(call DELFILE, kasan_globals.tmp)) $(Q) $(call DELFILE, kasan_globals.tmp))
$(Q) $(LD) $(LDENTRY) $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \ $(Q) $(LD) $(LDENTRY) $(LDFLAGS) $(LIBPATHS) $(EXTRA_LIBPATHS) \

View File

@ -399,7 +399,7 @@ define LINK_ALLSYMS_KASAN
$(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c) $(Q) $(call COMPILE, allsyms.tmp, allsyms$(OBJEXT), -x c)
$(Q) $(call DELFILE, allsyms.tmp)) $(Q) $(call DELFILE, allsyms.tmp))
$(if $(CONFIG_MM_KASAN_GLOBAL), $(if $(CONFIG_MM_KASAN_GLOBAL),
$(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp $(Q) $(TOPDIR)/tools/kasan_global.py -e $(NUTTX) -o kasan_globals.tmp -a $(CONFIG_MM_KASAN_GLOBAL_ALIGN)
$(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c) $(Q) $(call COMPILE, kasan_globals.tmp, kasan_globals$(OBJEXT) -fno-sanitize=kernel-address, -x c)
$(Q) $(call DELFILE, kasan_globals.tmp)) $(Q) $(call DELFILE, kasan_globals.tmp))
$(if $(CONFIG_HAVE_CXX),\ $(if $(CONFIG_HAVE_CXX),\

View File

@ -341,6 +341,14 @@ config MM_KASAN_GLOBAL
KEEP ( *(. data. rel. local.. LASAN0)) KEEP ( *(. data. rel. local.. LASAN0))
}", used to extract data generated by the compiler }", used to extract data generated by the compiler
if MM_KASAN_GLOBAL
config MM_KASAN_GLOBAL_ALIGN
int "KASan global alignment"
default 32
endif # MM_KASAN_GLOBAL
config MM_KASAN_DISABLE_READ_PANIC config MM_KASAN_DISABLE_READ_PANIC
bool "Disable panic on kasan read error" bool "Disable panic on kasan read error"
default n default n

View File

@ -40,8 +40,6 @@
#define KASAN_GLOBAL_LAST_WORD_MASK(end) \ #define KASAN_GLOBAL_LAST_WORD_MASK(end) \
(UINTPTR_MAX >> (-(end) & (KASAN_BITS_PER_WORD - 1))) (UINTPTR_MAX >> (-(end) & (KASAN_BITS_PER_WORD - 1)))
#define KASAN_GLOBAL_SHADOW_SCALE (32)
/**************************************************************************** /****************************************************************************
* Private Types * Private Types
****************************************************************************/ ****************************************************************************/
@ -77,7 +75,7 @@ kasan_global_mem_to_shadow(FAR const void *ptr, size_t size,
{ {
DEBUGASSERT(addr + size <= g_global_region[i]->end); DEBUGASSERT(addr + size <= g_global_region[i]->end);
addr -= g_global_region[i]->begin; addr -= g_global_region[i]->begin;
addr /= KASAN_GLOBAL_SHADOW_SCALE; addr /= CONFIG_MM_KASAN_GLOBAL_ALIGN;
*bit = addr % KASAN_BITS_PER_WORD; *bit = addr % KASAN_BITS_PER_WORD;
return (FAR uintptr_t *) return (FAR uintptr_t *)
&g_global_region[i]->shadow[addr / KASAN_BITS_PER_WORD]; &g_global_region[i]->shadow[addr / KASAN_BITS_PER_WORD];
@ -101,15 +99,15 @@ kasan_global_is_poisoned(FAR const void *addr, size_t size)
return false; return false;
} }
if (size <= KASAN_GLOBAL_SHADOW_SCALE) if (size <= CONFIG_MM_KASAN_GLOBAL_ALIGN)
{ {
return ((*p >> bit) & 1); return ((*p >> bit) & 1);
} }
nbit = KASAN_BITS_PER_WORD - bit % KASAN_BITS_PER_WORD; nbit = KASAN_BITS_PER_WORD - bit % KASAN_BITS_PER_WORD;
mask = KASAN_GLOBAL_FIRST_WORD_MASK(bit); mask = KASAN_GLOBAL_FIRST_WORD_MASK(bit);
size = ALIGN_UP(size, KASAN_GLOBAL_SHADOW_SCALE); size = ALIGN_UP(size, CONFIG_MM_KASAN_GLOBAL_ALIGN);
size /= KASAN_GLOBAL_SHADOW_SCALE; size /= CONFIG_MM_KASAN_GLOBAL_ALIGN;
while (size >= nbit) while (size >= nbit)
{ {

View File

@ -29,9 +29,6 @@ from elftools.elf.elffile import ELFFile
debug = False debug = False
# N-byte aligned shadow area 1 bit
KASAN_GLOBAL_ALIGN = 32
# The maximum gap that two data segments can tolerate # The maximum gap that two data segments can tolerate
KASAN_MAX_DATA_GAP = 1 << 16 KASAN_MAX_DATA_GAP = 1 << 16
@ -68,10 +65,11 @@ KASAN_GLOBAL_STRUCT_64 = Struct(
# Global configuration information # Global configuration information
class Config: class Config:
def __init__(self, outpath, elf, ldscript): def __init__(self, outpath, elf, ldscript, align):
self.outpath = outpath self.outpath = outpath
self.elf = elf self.elf = elf
self.ldscript = ldscript self.ldscript = ldscript
self.align = align
if self.elf is None or os.path.exists(self.elf) is False: if self.elf is None or os.path.exists(self.elf) is False:
self.elf = None self.elf = None
@ -97,43 +95,73 @@ class Config:
class KASanRegion: class KASanRegion:
def __init__(self, start, end) -> None: def __init__(self, start, end, align, endian, bitwides) -> None:
self.start = start self.start = start
self.end = end self.end = end
self.size = int((end - start) // KASAN_GLOBAL_ALIGN // 8) + 1 self.align = align
self.shadow = bytearray(b"\x00" * self.size) self.endian = endian
self.bitwides = bitwides
self.size = int((end - start) // self.align // self.bitwides) + 1
self.__shadow = [0] * self.size
def mark_bit(self, index, nbits): def shadow(self) -> bytearray:
self.shadow[index] |= 1 << nbits ret = bytearray()
for i in self.__shadow:
for j in range(self.bitwides // 8):
if self.endian == "little":
ret.append((i >> (j * 8)) & 0xFF)
else:
ret.append((i >> ((self.bitwides // 8 - j - 1) * 8)) & 0xFF)
return ret
def mark_bit(self, index, nbit, nbits):
for i in range(nbits):
self.__shadow[index] |= 1 << nbit
nbit += 1
if nbit == self.bitwides:
index += 1
nbit = 0
def poison(self, dict): def poison(self, dict):
dict_size = dict["size"] dict_size = dict["size"]
if dict_size % 32: if dict_size % self.align:
dict_size = int((dict_size + 31) // 32) * 32 dict_size = int((dict_size + self.align - 1) // self.align) * self.align
distance = (dict["beg"] + dict_size - self.start) // self.align
# Index of the marked shadow area
index = int(distance // self.bitwides)
# The X-th bit of the specific byte marked
nbit = distance % self.bitwides
# Number of digits to be marked
nbits = (dict["size_with_redzone"] - dict_size) // self.align
distance = (dict["beg"] + dict_size - self.start) // KASAN_GLOBAL_ALIGN
index = int(distance // 8)
nbits = distance % 8
if debug: if debug:
print( print(
"regin: %08x addr: %08x size: %d bits: %d || poison index: %d nbits: %d" "regin: %08x addr: %08x size: %d bits: %d || poison index: %d nbit: %d nbits: %d"
% ( % (
self.start, self.start,
dict["beg"], dict["beg"],
dict["size"], dict["size"],
int(dict["size_with_redzone"] // KASAN_GLOBAL_ALIGN), int(dict["size_with_redzone"] // self.align),
index, index,
nbit,
nbits, nbits,
) )
) )
# Using 32bytes: with 1bit alignment, # Using 32bytes: with 1bit alignment,
# only one bit of inaccessible area exists for each pair of global variables. # only one bit of inaccessible area exists for each pair of global variables.
self.mark_bit(index, nbits) self.mark_bit(index, nbit, nbits)
class KASanInfo: class KASanInfo:
def __init__(self) -> None: def __init__(self, align, endian, bitwides) -> None:
self.align = align
self.endian = endian
self.bitwides = bitwides
# Record the starting position of the merged data block # Record the starting position of the merged data block
self.data_sections = [] self.data_sections = []
# Record the kasan region corresponding to each data block # Record the kasan region corresponding to each data block
@ -158,13 +186,15 @@ class KASanInfo:
end = i[1] end = i[1]
if debug: if debug:
print("KAsan Shadow Block: %08x ---- %08x" % (start, end)) print("KAsan Shadow Block: %08x ---- %08x" % (start, end))
self.regions.append(KASanRegion(start, end)) self.regions.append(
KASanRegion(start, end, self.align, self.endian, self.bitwides)
)
def mark_shadow(self, dict): def mark_shadow(self, dict):
for i in self.regions: for i in self.regions:
start = i.start start = i.start
end = i.end end = i.end
if start <= dict["beg"] and dict["beg"] <= end: if start <= dict["beg"] and dict["beg"] + dict["size_with_redzone"] <= end:
i.poison(dict) i.poison(dict)
break break
@ -227,10 +257,11 @@ def create_kasan_file(config: Config, region_list=[]):
% (long_to_bytestring(config.bitwides, config.endian, region.end)) % (long_to_bytestring(config.bitwides, config.endian, region.end))
) )
for j in range(len(region.shadow)): shadow = region.shadow()
for j in range(len(shadow)):
if j % 8 == 0: if j % 8 == 0:
file.write("\n") file.write("\n")
file.write("0x%02x, " % (region.shadow[j])) file.write("0x%02x, " % (shadow[j]))
file.write("\n};") file.write("\n};")
@ -253,6 +284,7 @@ def parse_args() -> Config:
global debug global debug
parser = argparse.ArgumentParser(description="Build kasan global variable region") parser = argparse.ArgumentParser(description="Build kasan global variable region")
parser.add_argument("-o", "--outpath", help="outpath") parser.add_argument("-o", "--outpath", help="outpath")
parser.add_argument("-a", "--align", default=32, type=int, help="alignment")
parser.add_argument("-d", "--ldscript", help="ld script path(Only support sim)") parser.add_argument("-d", "--ldscript", help="ld script path(Only support sim)")
parser.add_argument("-e", "--elffile", help="elffile") parser.add_argument("-e", "--elffile", help="elffile")
parser.add_argument( parser.add_argument(
@ -264,10 +296,11 @@ def parse_args() -> Config:
args = parser.parse_args() args = parser.parse_args()
debug = args.debug debug = args.debug
return Config(args.outpath, args.elffile, args.ldscript) return Config(args.outpath, args.elffile, args.ldscript, args.align)
def main(): def main():
config = parse_args() config = parse_args()
if config.elf is None: if config.elf is None:
handle_error(config) handle_error(config)
@ -298,7 +331,7 @@ def main():
dict_list = sorted(dict_list, key=lambda item: item["beg"]) dict_list = sorted(dict_list, key=lambda item: item["beg"])
# Merge all global variables to obtain several segments of the distribution range # Merge all global variables to obtain several segments of the distribution range
kasan = KASanInfo() kasan = KASanInfo(config.align, config.endian, config.bitwides)
for i in dict_list: for i in dict_list:
kasan.merge_ranges(i) kasan.merge_ranges(i)