Add more values for the normalization
[kconfig-hardened-check.git] / kconfig_hardened_check / __init__.py
index cb8d54bf96cd166552f9411745f4b61151fb70d7..0367d88094b22ea8e779e2146841ed7dd18270b0 100644 (file)
@@ -311,6 +311,9 @@ def detect_version(fname):
 def add_kconfig_checks(l, arch):
     # Calling the KconfigCheck class constructor:
     #     KconfigCheck(reason, decision, name, expected)
+    #
+    # [!] Don't add CmdlineChecks in add_kconfig_checks() to avoid wrong results
+    #     when the tool doesn't check the cmdline.
 
     modules_not_set = KconfigCheck('cut_attack_surface', 'kspp', 'MODULES', 'is not set')
     devmem_not_set = KconfigCheck('cut_attack_surface', 'kspp', 'DEVMEM', 'is not set') # refers to LOCKDOWN
@@ -647,13 +650,30 @@ def add_kconfig_checks(l, arch):
 def add_cmdline_checks(l, arch):
     # Calling the CmdlineCheck class constructor:
     #     CmdlineCheck(reason, decision, name, expected)
-    # Don't add CmdlineChecks in add_kconfig_checks() to avoid wrong results
-    # when the tool doesn't check the cmdline.
+    #
+    # [!] Don't add CmdlineChecks in add_kconfig_checks() to avoid wrong results
+    #     when the tool doesn't check the cmdline.
+    #
+    # [!] Make sure that values of the options in CmdlineChecks need normalization.
+    #     For more info see normalize_cmdline_options().
+    #
+    # A common pattern for checking the 'param_x' cmdline parameter
+    # that __overrides__ the 'PARAM_X_DEFAULT' kconfig option:
+    #   l += [OR(CmdlineCheck(reason, decision, 'param_x', '1'),
+    #            AND(KconfigCheck(reason, decision, 'PARAM_X_DEFAULT_ON', 'y'),
+    #                CmdlineCheck(reason, decision, 'param_x, 'is not set')))]
+    #
+    # Here we don't check the kconfig options or minimal kernel version
+    # required for the cmdline parameters. That would make the checks
+    # very complex and not give a 100% guarantee anyway.
 
     if arch == 'ARM64':
         l += [OR(CmdlineCheck('self_protection', 'defconfig', 'rodata', 'full'),
                  AND(KconfigCheck('self_protection', 'defconfig', 'RODATA_FULL_DEFAULT_ENABLED', 'y'),
                      CmdlineCheck('self_protection', 'defconfig', 'rodata', 'is not set')))]
+    else:
+        l += [OR(CmdlineCheck('self_protection', 'defconfig', 'rodata', '1'),
+                 CmdlineCheck('self_protection', 'defconfig', 'rodata', 'is not set'))]
 
     l += [OR(CmdlineCheck('self_protection', 'kspp', 'init_on_alloc', '1'),
              AND(KconfigCheck('self_protection', 'kspp', 'INIT_ON_ALLOC_DEFAULT_ON', 'y'),
@@ -673,6 +693,9 @@ def add_cmdline_checks(l, arch):
     l += [OR(CmdlineCheck('self_protection', 'kspp', 'iommu.passthrough', '0'),
              AND(KconfigCheck('self_protection', 'kspp', 'IOMMU_DEFAULT_PASSTHROUGH', 'is not set'),
                  CmdlineCheck('self_protection', 'kspp', 'iommu.passthrough', 'is not set')))]
+    l += [OR(CmdlineCheck('self_protection', 'kspp', 'hardened_usercopy', '1'),
+             AND(KconfigCheck('self_protection', 'kspp', 'HARDENED_USERCOPY', 'y'),
+                 CmdlineCheck('self_protection', 'kspp', 'hardened_usercopy', 'is not set')))]
     l += [OR(CmdlineCheck('self_protection', 'kspp', 'slab_common.usercopy_fallback', '0'),
              AND(KconfigCheck('self_protection', 'kspp', 'HARDENED_USERCOPY_FALLBACK', 'is not set'),
                  CmdlineCheck('self_protection', 'kspp', 'slab_common.usercopy_fallback', 'is not set')))]
@@ -831,6 +854,24 @@ def parse_kconfig_file(parsed_options, fname):
                 parsed_options[option] = value
 
 
+def normalize_cmdline_options(option, value):
+    # Handle special cases
+    if option == 'pti':
+        # Don't normalize the pti value since
+        # the Linux kernel doesn't use kstrtobool() for pti.
+        # See pti_check_boottime_disable() in linux/arch/x86/mm/pti.c
+        return value
+
+    # Implement a limited part of the kstrtobool() logic
+    if value in ('1', 'on', 'On', 'ON', 'y', 'Y', 'yes', 'Yes', 'YES'):
+        return '1'
+    if value in ('0', 'off', 'Off', 'OFF', 'n', 'N', 'no', 'No', 'NO'):
+        return '0'
+
+    # Preserve unique values
+    return value
+
+
 def parse_cmdline_file(parsed_options, fname):
     with open(fname, 'r') as f:
         line = f.readline()
@@ -846,6 +887,7 @@ def parse_cmdline_file(parsed_options, fname):
             else:
                 name = opt
                 value = '' # '' is not None
+            value = normalize_cmdline_options(name, value)
             parsed_options[name] = value