OR needs OptCheck.check() return values
[kconfig-hardened-check.git] / kconfig-hardened-check.py
index a87b28d5374e0a686a9da7bcdeb07e2f5f551e93..d8f844705bcfa7d8290044d6b8225ffd9eb09af2 100755 (executable)
@@ -22,7 +22,6 @@ from collections import OrderedDict
 import re
 
 debug_mode = False  # set it to True to print the unknown options from the config
-error_count = 0
 checklist = []
 
 
@@ -36,24 +35,60 @@ class OptCheck:
         self.result = None
 
     def check(self):
-        global error_count
-
         if self.expected == self.state:
             self.result = 'OK'
         elif self.state is None:
             if self.expected == 'is not set':
                 self.result = 'OK: not found'
             else:
-                error_count += 1
                 self.result = 'FAIL: not found'
         else:
-            error_count += 1
             self.result = 'FAIL: "' + self.state + '"'
 
+        if self.result.startswith('OK'):
+            return True, self.result
+        else:
+            return False, self.result
+
     def __repr__(self):
         return '{} = {}'.format(self.name, self.state)
 
 
+class OR:
+    def __init__(self, *opts):
+        self.opts = opts
+        self.result = None
+
+    @property
+    def name(self):
+        return self.opts[0].name
+
+    @property
+    def expected(self):
+        return self.opts[0].expected
+
+    @property
+    def state(self):
+        return self.opts[0].state
+
+    @property
+    def decision(self):
+        return self.opts[0].decision
+
+    @property
+    def reason(self):
+        return self.opts[0].reason
+
+    def check(self):
+        for opt in self.opts:
+            result, msg = opt.check()
+            if result:
+                self.result = 'OK (CONFIG_{} {})'.format(opt.name, opt.state)
+                return result, self.result
+        self.result = 'FAIL: "{}"'.format(self.opts[0].state)
+        return False, self.result
+
+
 def construct_opt_checks():
     checklist.append(OptCheck('BUG',                     'y', 'ubuntu18', 'self_protection'))
     checklist.append(OptCheck('PAGE_TABLE_ISOLATION',    'y', 'ubuntu18', 'self_protection'))
@@ -117,6 +152,7 @@ def construct_opt_checks():
     checklist.append(OptCheck('ZSMALLOC_STAT',        'is not set', 'ubuntu18', 'cut_attack_surface'))
     checklist.append(OptCheck('PAGE_OWNER',           'is not set', 'ubuntu18', 'cut_attack_surface'))
     checklist.append(OptCheck('DEBUG_KMEMLEAK',       'is not set', 'ubuntu18', 'cut_attack_surface'))
+    checklist.append(OptCheck('BINFMT_AOUT',          'is not set', 'ubuntu18', 'cut_attack_surface'))
 
     checklist.append(OptCheck('IO_STRICT_DEVMEM',     'y', 'kspp', 'cut_attack_surface'))
     checklist.append(OptCheck('LEGACY_VSYSCALL_NONE', 'y', 'kspp', 'cut_attack_surface')) # 'vsyscall=none'
@@ -169,8 +205,6 @@ def print_opt_checks():
 
 
 def print_check_results():
-    global error_count
-
     print('  {:<39}|{:^13}|{:^10}|{:^20}||{:^20}'.format('option name', 'desired val', 'decision', 'reason', 'check result'))
     print('  ===========================================================================================================')
     for opt in checklist:
@@ -184,7 +218,11 @@ def get_option_state(options, name):
 
 def perform_checks(parsed_options):
     for opt in checklist:
-        opt.state = get_option_state(parsed_options, opt.name)
+        if hasattr(opt, 'opts'):
+            for o in opt.opts:
+                o.state = get_option_state(parsed_options, o.name)
+        else:
+            opt.state = get_option_state(parsed_options, opt.name)
         opt.check()
 
 
@@ -242,6 +280,7 @@ if __name__ == '__main__':
 
     if args.config:
         check_config_file(args.config)
+        error_count = len(list(filter(lambda opt: opt.result.startswith('FAIL'), checklist)))
         if error_count == 0:
             print('[+] config check is PASSED')
             sys.exit(0)