X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=kconfig_hardened_check%2F__init__.py;h=2b5fe1e68d7bd5b2fa6d58b95e7c26be90a6ac1d;hb=6268a3101ffcd0fae4604374d33dd1363f60f6ce;hp=02d02818d893fc082309f141ec2a6e1ea3374739;hpb=79beb2daa162429dfc71ccff8be88ce6ab7295e6;p=kconfig-hardened-check.git diff --git a/kconfig_hardened_check/__init__.py b/kconfig_hardened_check/__init__.py index 02d0281..2b5fe1e 100644 --- a/kconfig_hardened_check/__init__.py +++ b/kconfig_hardened_check/__init__.py @@ -29,9 +29,9 @@ # pti=on # spec_store_bypass_disable=on # l1tf=full,force +# l1d_flush=on (a part of the l1tf option) # mds=full,nosmt # tsx=off -# l1d_flush=on # ARM64: # kpti=on # ssbd=force-on @@ -81,6 +81,7 @@ import re import json from .__about__ import __version__ +TYPES_OF_CHECKS = ('kconfig', 'version') class OptCheck: def __init__(self, reason, decision, name, expected): @@ -121,6 +122,12 @@ class KconfigCheck(OptCheck): def type(self): return 'kconfig' + def json_dump(self, with_results): + dump = [self.name, self.type, self.expected, self.decision, self.reason] + if with_results: + dump.append(self.result) + return dump + class VersionCheck: def __init__(self, ver_expected): @@ -158,7 +165,7 @@ class PresenceCheck: if self.type == 'kconfig': self.name = 'CONFIG_' + name else: - self.name = name + sys.exit('[!] ERROR: unsupported type "{}" for {}'.format(type, self.__class__.__name__)) self.state = None self.result = None @@ -192,7 +199,7 @@ class ComplexOptCheck: @property def type(self): - return self.opts[0].type + return 'complex' @property def expected(self): @@ -220,6 +227,12 @@ class ComplexOptCheck: if with_results: print('| {}'.format(self.result), end='') + def json_dump(self, with_results): + dump = self.opts[0].json_dump(False) + if with_results: + dump.append(self.result) + return dump + class OR(ComplexOptCheck): # self.opts[0] is the option that this OR-check is about. @@ -594,6 +607,7 @@ def add_kconfig_checks(l, arch): # 'cut_attack_surface', 'my' l += [OR(KconfigCheck('cut_attack_surface', 'my', 'TRIM_UNUSED_KSYMS', 'y'), modules_not_set)] + l += [KconfigCheck('cut_attack_surface', 'my', 'BPF_UNPRIV_DEFAULT_OFF', 'y')] # see kernel.unprivileged_bpf_disabled l += [KconfigCheck('cut_attack_surface', 'my', 'MMIOTRACE', 'is not set')] # refers to LOCKDOWN (permissive) l += [KconfigCheck('cut_attack_surface', 'my', 'LIVEPATCH', 'is not set')] l += [KconfigCheck('cut_attack_surface', 'my', 'IP_DCCP', 'is not set')] @@ -623,16 +637,16 @@ def print_unknown_options(checklist, parsed_options): known_options = [] for o1 in checklist: - if not hasattr(o1, 'opts'): + if o1.type != 'complex': known_options.append(o1.name) continue for o2 in o1.opts: - if not hasattr(o2, 'opts'): + if o2.type != 'complex': if hasattr(o2, 'name'): known_options.append(o2.name) continue for o3 in o2.opts: - if hasattr(o3, 'opts'): + if o3.type == 'complex': sys.exit('[!] ERROR: unexpected ComplexOptCheck inside {}'.format(o2.name)) if hasattr(o3, 'name'): known_options.append(o3.name) @@ -644,13 +658,10 @@ def print_unknown_options(checklist, parsed_options): def print_checklist(mode, checklist, with_results): if mode == 'json': - opts = [] + output = [] for o in checklist: - opt = [o.name, o.type, o.expected, o.decision, o.reason] - if with_results: - opt.append(o.result) - opts.append(opt) - print(json.dumps(opts)) + output.append(o.json_dump(with_results)) + print(json.dumps(output)) return # table header @@ -693,27 +704,38 @@ def print_checklist(mode, checklist, with_results): print('[+] Config check is finished: \'OK\' - {}{} / \'FAIL\' - {}{}'.format(ok_count, ok_suppressed, fail_count, fail_suppressed)) -def populate_opt_with_data(opt, parsed_options, kernel_version): - if hasattr(opt, 'opts'): - # prepare ComplexOptCheck +def populate_simple_opt_with_data(opt, data, data_type): + if opt.type == 'complex': + sys.exit('[!] ERROR: unexpected ComplexOptCheck {}: {}'.format(opt.name, vars(opt))) + if data_type not in TYPES_OF_CHECKS: + sys.exit('[!] ERROR: invalid data type "{}"'.format(data_type)) + + if data_type != opt.type: + return + + if data_type == 'kconfig': + opt.state = data.get(opt.name, None) + elif data_type == 'version': + opt.ver = data + + +def populate_opt_with_data(opt, data, data_type): + if opt.type == 'complex': for o in opt.opts: - if hasattr(o, 'opts'): - # Recursion for nested ComplexOptChecks - populate_opt_with_data(o, parsed_options, kernel_version) - if hasattr(o, 'state'): - o.state = parsed_options.get(o.name, None) - if hasattr(o, 'ver'): - o.ver = kernel_version + if o.type == 'complex': + # Recursion for nested ComplexOptCheck objects + populate_opt_with_data(o, data, data_type) + else: + populate_simple_opt_with_data(o, data, data_type) else: - # prepare simple check, opt.state is mandatory - if not hasattr(opt, 'state'): - sys.exit('[!] ERROR: bad simple check {}'.format(vars(opt))) - opt.state = parsed_options.get(opt.name, None) + if opt.type != 'kconfig': + sys.exit('[!] ERROR: bad type "{}" for a simple check {}'.format(opt.type, opt.name)) + populate_simple_opt_with_data(opt, data, data_type) -def populate_with_data(checklist, parsed_options, kernel_version): +def populate_with_data(checklist, data, data_type): for opt in checklist: - populate_opt_with_data(opt, parsed_options, kernel_version) + populate_opt_with_data(opt, data, data_type) def perform_checks(checklist): @@ -796,7 +818,8 @@ def main(): # populate the checklist with the parsed kconfig data parsed_kconfig_options = OrderedDict() parse_kconfig_file(parsed_kconfig_options, args.config) - populate_with_data(config_checklist, parsed_kconfig_options, kernel_version) + populate_with_data(config_checklist, parsed_kconfig_options, 'kconfig') + populate_with_data(config_checklist, kernel_version, 'version') # now everything is ready for performing the checks perform_checks(config_checklist)