kernel_samsung_a53x/scripts/fmp/IntegrityCheckCodeGen.py
2024-06-15 16:02:09 -03:00

239 lines
8.5 KiB
Python
Executable file

#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# -*- coding: utf-8 -*-
import sys
sys.dont_write_bytecode = True
import os
import re
from Utils import Utils
from Utils import log_d, log_i, debug_print
from ELF import ELF
from ELF import STV_DEFAULT, STT_FUNC, STT_OBJECT, STB_GLOBAL
pattern_sec_name_ic_target = [".text", ".rodata"]
pattern_sec_name_not_allowed = [".plt", ".rela", ".debug", "cfi", "ftrace", ".init", ".exit"]
NAME_FILE_SECTION_HANDLED_ALREADY = "section_is_handled_already"
PREFIX_ANCHOR_VAR = "fips140"
PREFIX_NAME_RET_FUNC = "ret_addr"
MIN_SYM_SIZE_BYTES = 4
PREFIX_ELF_OBJECTS = "elf_"
PREFIX_PATCHED_SOURCES = "fipsed_"
class CodeGenerator:
sec_sym_file_dict = {} # {section_name: [ symbol_name, file_where_symbol_is ]}
def analize_elf_patch_source(self, __elf: ELF, source_file: str):
available_target_sec = self.lookup_ci_target_sections(__elf)
targets_sym_list = self.lookup_ci_target_symbols(__elf, available_target_sec) # ret [[sec_idx, sec_name, sym_name] ... ]
self.patch_source_with_addr_ret_function(source_file, targets_sym_list)
for lst in targets_sym_list:
self.sec_sym_file_dict[lst[1]] = [lst[2], source_file]
def is_section_handled_already(self, sc_name: str) -> bool:
return bool(sc_name in self.sec_sym_file_dict)
def is_section_name_match(self, sc_name: str) -> bool:
for __s in pattern_sec_name_ic_target:
if __s in sc_name:
return True
return False
def is_section_name_allowed(self, sc_name: str) -> bool:
for __na in pattern_sec_name_not_allowed:
if __na in sc_name:
return False
return True
def lookup_ci_target_sections(self, __elf: ELF) -> list:
"""
Returns list of sections
"""
elf_sec_list = []
res_sec_list = []
elf_sec_list = __elf.get_sections_all_names()
for __s_name in elf_sec_list:
# check if the section name seems such
# we need to cover with integrity check
if not self.is_section_name_match(__s_name):
continue
# check the section name doesn't contain
# not allowed substrings
if not self.is_section_name_allowed(__s_name):
continue
# check if the section anchor is already
# handled
if self.is_section_handled_already(__s_name):
continue
res_sec_list.append(__s_name)
debug_print(" found section(s) : {}".format(res_sec_list))
return res_sec_list
def lookup_ci_target_symbols(self, __elf: ELF, sec_lst: list) -> list:
"""
Returns list of [ section index, section name, symbol_name ]
"""
sym_list = []
res_list = []
for __sec_name in sec_lst:
__sec_idx = __elf.get_section_idx_by_name(__sec_name)
sym_list = __elf.get_all_sym_names_by_sec_idx(__sec_idx)
for __sym in sym_list:
__size = __elf.get_symbol_size(__sym)
__type = __elf.get_symbol_type(__sym)
__bind = __elf.get_symbol_bind(__sym)
__vis = __elf.get_symbol_vis(__sym)
if (__size > MIN_SYM_SIZE_BYTES
and __vis == STV_DEFAULT
and ((__type == STT_FUNC and __bind == STB_GLOBAL)
or (__type == STT_OBJECT))):
# it is possible to add something here to choose most convenient
# symbol.. but, actually the just 1st one is good enough
res_list.append([__sec_idx, __sec_name, __sym])
break
log_d(" chosen pair : {}".format(res_list))
return res_list
def generate_support_source_file(self, file_name: str, sec_sym_file_dict: dict):
sc_names = []
sym_names = []
header_name = os.path.basename(file_name).replace(".c", ".h")
for k, v in sec_sym_file_dict.items():
sc_names.append(k)
sym_names.append(v[0])
support_src = []
support_src.append("#include \"stdint.h\"")
support_src.append("#include \"{}\"".format(header_name))
support_src.append("")
support_src.append("#pragma clang optimize off")
support_src.append("")
support_src.append("#define FIPS_HMAC_SIZE 32")
support_src.append("#define FIPS_CHUNKS_MAX_AMOUNT 2048")
support_src.append("#define FIPS_SECTIONS_MAX_AMOUNT 48")
support_src.append("#define FIPS_ANCHOR_SYMS_OFFS_MAX_AMOUNT FIPS_SECTIONS_MAX_AMOUNT")
support_src.append("")
for i, j in zip(sc_names, sym_names):
support_src.append("extern void *{}{}(void); // {}".format(PREFIX_NAME_RET_FUNC, i.replace(".", "_"), j))
support_src.append("")
# anchor variables addrs array
support_src.append("void *fips140_get_anchor_addr(uint32_t i)")
support_src.append("{")
support_src.append(" switch(i) {")
for __sc, __i in zip(sc_names, range(len(sc_names))):
support_src.append(" case {}: return (void *){}{}();".format(__i,
PREFIX_NAME_RET_FUNC,
__sc.replace(".", "_")))
support_src.append(" default: return (void*)0;")
support_src.append(" };")
support_src.append("}")
support_src.append("")
# room for anchor symbols build-time in-section offsets. TO BE EMBEDDED
support_src.append("__attribute__ ((section(\".rodata\"), unused))")
support_src.append("uint32_t {}_anchor_offset[FIPS_SECTIONS_MAX_AMOUNT];"
.format(PREFIX_ANCHOR_VAR))
support_src.append("")
# room for variable sections amount. TO BE EMBEDDED
support_src.append("__attribute__ ((section(\".rodata\"), unused))")
support_src.append("uint32_t {}_sec_amount;".format(PREFIX_ANCHOR_VAR))
support_src.append("")
# room for gaps info. TO BE EMBEDDED
support_src.append("__attribute__ ((section(\".rodata\"), unused))")
support_src.append("struct chunks_info {}_chunks_info[FIPS_CHUNKS_MAX_AMOUNT];"
.format(PREFIX_ANCHOR_VAR))
support_src.append("")
# room for variable chunk amount. TO BE EMBEDDED
support_src.append("__attribute__ ((section(\".rodata\"), unused))")
support_src.append("uint32_t {}_chunk_amount;".format(PREFIX_ANCHOR_VAR))
support_src.append("")
# room for HMAC. TO BE EMBEDDED
support_src.append("__attribute__ ((section(\".rodata\"), unused))")
support_src.append("uint8_t {}_builtime_hmac[FIPS_HMAC_SIZE];".format(PREFIX_ANCHOR_VAR))
support_src.append("")
support_src.append("#pragma clang optimize on")
support_src.append("")
Utils().file_wr_data(file_name, support_src)
def generate_support_file(self, source_file: str):
self.generate_support_source_file(source_file, self.sec_sym_file_dict)
def patch_source_with_addr_ret_function(self, file_name: str, sec_sym_lst: list):
src = []
for __i in sec_sym_lst:
section_name = __i[1]
symbol_name = __i[2]
log_i(" Source: {} to be patched: {} {} ".format(os.path.basename(file_name),
section_name, symbol_name))
src.append("")
src.append("/* Here and below auto-generated part */")
src.append("#pragma clang optimize off ")
src.append("void *{}{}(void)".format(PREFIX_NAME_RET_FUNC, section_name.replace(".", "_")))
src.append("{")
src.append(" return (void*)&{};".format(symbol_name))
src.append("}")
src.append("#pragma clang optimize on ")
src.append("")
Utils().file_append_data(file_name, src)
def derive_source_name_from_elf_object(object_name: str):
"""
Derives source name from object file name.
hope we`ll never use cpp file
"""
__lst = re.split("(.*)elf_(.*).o", object_name)
__naked_file_name = __lst[-2]
__path = __lst[-3]
c_file_name = os.path.join(__path, PREFIX_PATCHED_SOURCES + __naked_file_name + ".c")
return c_file_name
def get_sec_sym_list_ordered(ic_support_file: str) -> list:
"""
Determines sections order by parsing ic_support_file
"""
res_sec_sym_list = []
if not os.path.isfile(ic_support_file):
raise RuntimeError(" ERROR: file not found. Ordered Sections/symbols list failed")
with open(ic_support_file, "r") as file:
lines = file.readlines()
for __l in lines:
anchor_var_pattern = "^extern void \\*{}(.*)\\(void\\); // (.*)$".format(PREFIX_NAME_RET_FUNC)
__p = re.compile(anchor_var_pattern, re.MULTILINE)
res = __p.findall(__l)
if len(res) == 1 and len(res[0]) == 2:
res_sec_sym_list.append([res[0][0].replace("_", "."), res[0][1]])
log_i(res_sec_sym_list)
if len(res_sec_sym_list) == 0:
raise RuntimeError(" ERROR: sections <> symbols pairs list is not found")
return res_sec_sym_list
if __name__ == "__main__":
cmd_args = sys.argv
if len(cmd_args) < 3:
log_i("Usage " + sys.argv[0] + " source_root_directory" + " obj_file")
sys.exit(-1)
o_files = cmd_args[3:]
c_support_file = cmd_args[1]
sec_sym_file = cmd_args[2]
c_file_to_generate = c_support_file
cg = CodeGenerator()
for _o_file in o_files:
c_file_to_patch = derive_source_name_from_elf_object(_o_file)
if not os.path.isfile(c_file_to_patch):
raise RuntimeError(" ERROR: Source to be patched {} was not found".format(c_file_to_patch))
elf = ELF(_o_file)
cg.analize_elf_patch_source(elf, c_file_to_patch)
cg.generate_support_file(c_support_file)
sys.exit(0)