X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=kconfig_hardened_check%2F__init__.py;h=c484b0dc226f438ebbc141f9bc608968505cef31;hb=0c13022442fe31c1ddcef4b525a71898ba84ed93;hp=3e503490d50f38991f623ef4f497b34ebc7d30c7;hpb=cfb4075cf996cba050abbf56f7c6284c63145042;p=kconfig-hardened-check.git diff --git a/kconfig_hardened_check/__init__.py b/kconfig_hardened_check/__init__.py index 3e50349..c484b0d 100644 --- a/kconfig_hardened_check/__init__.py +++ b/kconfig_hardened_check/__init__.py @@ -15,7 +15,6 @@ # # Mitigations of CPU vulnerabilities: # Аrch-independent: -# mitigations=auto,nosmt (nosmt is slow) # X86: # spec_store_bypass_disable=on # l1tf=full,force @@ -75,14 +74,33 @@ from .__about__ import __version__ SIMPLE_OPTION_TYPES = ('kconfig', 'version', 'cmdline') class OptCheck: - # Constructor without the 'expected' parameter is for option presence checks (any value is OK) - def __init__(self, reason, decision, name, expected=None): - assert(reason and decision and name), \ - 'invalid {} check for "{}"'.format(self.__class__.__name__, name) + def __init__(self, reason, decision, name, expected): + assert(name and name == name.strip() and len(name.split()) == 1), \ + 'invalid name "{}" for {}'.format(name, self.__class__.__name__) self.name = name - self.expected = expected + + assert(decision and decision == decision.strip() and len(decision.split()) == 1), \ + 'invalid decision "{}" for "{}" check'.format(decision, name) self.decision = decision + + assert(reason and reason == reason.strip() and len(reason.split()) == 1), \ + 'invalid reason "{}" for "{}" check'.format(reason, name) self.reason = reason + + assert(expected and expected == expected.strip()), \ + 'invalid expected value "{}" for "{}" check (1)'.format(expected, name) + val_len = len(expected.split()) + if val_len == 3: + assert(expected == 'is not set' or expected == 'is not off'), \ + 'invalid expected value "{}" for "{}" check (2)'.format(expected, name) + elif val_len == 2: + assert(expected == 'is present'), \ + 'invalid expected value "{}" for "{}" check (3)'.format(expected, name) + else: + assert(val_len == 1), \ + 'invalid expected value "{}" for "{}" check (4)'.format(expected, name) + self.expected = expected + self.state = None self.result = None @@ -91,31 +109,37 @@ class OptCheck: return None def check(self): - # handle the option presence check - if self.expected is None: + # handle the 'is present' check + if self.expected == 'is present': if self.state is None: - self.result = 'FAIL: not present' + self.result = 'FAIL: is not present' else: self.result = 'OK: is present' return + # handle the 'is not off' option check + if self.expected == 'is not off': + if self.state == 'off': + self.result = 'FAIL: is off' + elif self.state is None: + self.result = 'FAIL: is off, not found' + else: + self.result = 'OK: is not off, "' + self.state + '"' + return + # handle the option value check if self.expected == self.state: self.result = 'OK' elif self.state is None: if self.expected == 'is not set': - self.result = 'OK: not found' + self.result = 'OK: is not found' else: - self.result = 'FAIL: not found' + self.result = 'FAIL: is not found' else: self.result = 'FAIL: "' + self.state + '"' def table_print(self, _mode, with_results): - if self.expected is None: - expected = '' - else: - expected = self.expected - print('{:<40}|{:^7}|{:^12}|{:^10}|{:^18}'.format(self.name, self.type, expected, self.decision, self.reason), end='') + print('{:<40}|{:^7}|{:^12}|{:^10}|{:^18}'.format(self.name, self.type, self.expected, self.decision, self.reason), end='') if with_results: print('| {}'.format(self.result), end='') @@ -144,6 +168,8 @@ class CmdlineCheck(OptCheck): class VersionCheck: def __init__(self, ver_expected): + assert(ver_expected and isinstance(ver_expected, tuple) and len(ver_expected) == 2), \ + 'invalid version "{}" for VersionCheck'.format(ver_expected) self.ver_expected = ver_expected self.ver = () self.result = None @@ -228,11 +254,13 @@ class OR(ComplexOptCheck): # Add more info for additional checks: if i != 0: if opt.result == 'OK': - self.result = 'OK: {} "{}"'.format(opt.name, opt.expected) - elif opt.result == 'OK: not found': - self.result = 'OK: {} not found'.format(opt.name) + self.result = 'OK: {} is "{}"'.format(opt.name, opt.expected) + elif opt.result == 'OK: is not found': + self.result = 'OK: {} is not found'.format(opt.name) elif opt.result == 'OK: is present': self.result = 'OK: {} is present'.format(opt.name) + elif opt.result.startswith('OK: is not off'): + self.result = 'OK: {} is not off'.format(opt.name) else: # VersionCheck provides enough info assert(opt.result.startswith('OK: version')), \ @@ -257,10 +285,14 @@ class AND(ComplexOptCheck): # This FAIL is caused by additional checks, # and not by the main option that this AND-check is about. # Describe the reason of the FAIL. - if opt.result.startswith('FAIL: \"') or opt.result == 'FAIL: not found': - self.result = 'FAIL: {} not "{}"'.format(opt.name, opt.expected) - elif opt.result == 'FAIL: not present': - self.result = 'FAIL: {} not present'.format(opt.name) + if opt.result.startswith('FAIL: \"') or opt.result == 'FAIL: is not found': + self.result = 'FAIL: {} is not "{}"'.format(opt.name, opt.expected) + elif opt.result == 'FAIL: is not present': + self.result = 'FAIL: {} is not present'.format(opt.name) + elif opt.result == 'FAIL: is off': + self.result = 'FAIL: {} is off'.format(opt.name) + elif opt.result == 'FAIL: is off, not found': + self.result = 'FAIL: {} is off, not found'.format(opt.name) else: # VersionCheck provides enough info self.result = opt.result @@ -387,6 +419,7 @@ def add_kconfig_checks(l, arch): l += [KconfigCheck('self_protection', 'defconfig', 'ARM64_PAN', 'y')] l += [KconfigCheck('self_protection', 'defconfig', 'ARM64_EPAN', 'y')] l += [KconfigCheck('self_protection', 'defconfig', 'UNMAP_KERNEL_AT_EL0', 'y')] + l += [KconfigCheck('self_protection', 'defconfig', 'ARM64_E0PD', 'y')] l += [OR(KconfigCheck('self_protection', 'defconfig', 'HARDEN_EL2_VECTORS', 'y'), AND(KconfigCheck('self_protection', 'defconfig', 'RANDOMIZE_BASE', 'y'), VersionCheck((5, 9))))] # HARDEN_EL2_VECTORS was included in RANDOMIZE_BASE in v5.9 @@ -563,7 +596,7 @@ def add_kconfig_checks(l, arch): l += [OR(KconfigCheck('cut_attack_surface', 'kspp', 'IO_STRICT_DEVMEM', 'y'), devmem_not_set)] # refers to LOCKDOWN l += [AND(KconfigCheck('cut_attack_surface', 'kspp', 'LDISC_AUTOLOAD', 'is not set'), - KconfigCheck('cut_attack_surface', 'kspp', 'LDISC_AUTOLOAD'))] # option presence check + KconfigCheck('cut_attack_surface', 'kspp', 'LDISC_AUTOLOAD', 'is present'))] if arch == 'ARM': l += [OR(KconfigCheck('cut_attack_surface', 'kspp', 'STRICT_DEVMEM', 'y'), devmem_not_set)] # refers to LOCKDOWN @@ -699,6 +732,10 @@ def add_cmdline_checks(l, arch): l += [CmdlineCheck('self_protection', 'defconfig', 'nopti', 'is not set')] l += [CmdlineCheck('self_protection', 'defconfig', 'nospectre_v1', 'is not set')] l += [CmdlineCheck('self_protection', 'defconfig', 'nospectre_v2', 'is not set')] + l += [OR(CmdlineCheck('self_protection', 'defconfig', 'mitigations', 'is not off'), + CmdlineCheck('self_protection', 'defconfig', 'mitigations', 'is not set'))] + l += [OR(CmdlineCheck('self_protection', 'defconfig', 'spectre_v2', 'is not off'), + CmdlineCheck('self_protection', 'defconfig', 'spectre_v2', 'is not set'))] if arch == 'ARM64': l += [OR(CmdlineCheck('self_protection', 'defconfig', 'rodata', 'full'), AND(KconfigCheck('self_protection', 'defconfig', 'RODATA_FULL_DEFAULT_ENABLED', 'y'), @@ -708,6 +745,7 @@ def add_cmdline_checks(l, arch): CmdlineCheck('self_protection', 'defconfig', 'rodata', 'is not set'))] # 'self_protection', 'kspp' + l += [CmdlineCheck('self_protection', 'kspp', 'nosmt', 'is present')] l += [OR(CmdlineCheck('self_protection', 'kspp', 'init_on_alloc', '1'), AND(KconfigCheck('self_protection', 'kspp', 'INIT_ON_ALLOC_DEFAULT_ON', 'y'), CmdlineCheck('self_protection', 'kspp', 'init_on_alloc', 'is not set')))] @@ -717,9 +755,9 @@ def add_cmdline_checks(l, arch): AND(CmdlineCheck('self_protection', 'kspp', 'page_poison', '1'), KconfigCheck('self_protection', 'kspp', 'PAGE_POISONING_ZERO', 'y'), CmdlineCheck('self_protection', 'kspp', 'slub_debug', 'P')))] - l += [OR(CmdlineCheck('self_protection', 'kspp', 'slab_nomerge'), + l += [OR(CmdlineCheck('self_protection', 'kspp', 'slab_nomerge', 'is present'), AND(KconfigCheck('self_protection', 'clipos', 'SLAB_MERGE_DEFAULT', 'is not set'), - CmdlineCheck('self_protection', 'kspp', 'slab_merge', 'is not set')))] # option presence check + CmdlineCheck('self_protection', 'kspp', 'slab_merge', 'is not set')))] l += [OR(CmdlineCheck('self_protection', 'kspp', 'iommu.strict', '1'), AND(KconfigCheck('self_protection', 'kspp', 'IOMMU_DEFAULT_DMA_STRICT', 'y'), CmdlineCheck('self_protection', 'kspp', 'iommu.strict', 'is not set')))] @@ -744,9 +782,6 @@ def add_cmdline_checks(l, arch): # 'self_protection', 'clipos' l += [CmdlineCheck('self_protection', 'clipos', 'page_alloc.shuffle', '1')] - if arch in ('X86_64', 'X86_32'): - l += [AND(CmdlineCheck('self_protection', 'clipos', 'spectre_v2', 'on'), - CmdlineCheck('self_protection', 'defconfig', 'nospectre_v2', 'is not set'))] # 'cut_attack_surface', 'kspp' if arch == 'X86_64': @@ -912,6 +947,9 @@ def normalize_cmdline_options(option, value): if option == 'debugfs': # See debugfs_kernel() in fs/debugfs/inode.c return value + if option == 'mitigations': + # See mitigations_parse_cmdline() in linux/kernel/cpu.c + return value # Implement a limited part of the kstrtobool() logic if value in ('1', 'on', 'On', 'ON', 'y', 'Y', 'yes', 'Yes', 'YES'):