blob: f98a9c8d3f53490f01eedb25117c543d34a50374 [file] [log] [blame]
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging, os
from autotest_lib.client.bin import test, utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros import kernel_config
class kernel_ConfigVerify(test.test):
"""Examine a kernel build CONFIG list to make sure various things are
present, missing, built as modules, etc.
"""
version = 1
IS_BUILTIN = [
# Sanity checks; should be present in builds as builtins.
'INET',
'MMU',
'MODULES',
'PRINTK',
'SECURITY',
# Security; adds stack buffer overflow protections.
'CC_STACKPROTECTOR',
# Security; enables the SECCOMP application API.
'SECCOMP',
# Security; blocks direct physical memory access.
'STRICT_DEVMEM',
# Security; provides some protections against SYN flooding.
'SYN_COOKIES',
# Security; make sure PID_NS, NET_NS, and USER_NS are enabled for
# chrome's layer 1 sandbox.
'PID_NS',
'NET_NS',
'USER_NS',
# Security; perform additional validation of credentials.
'DEBUG_CREDENTIALS',
# Security; make sure the Chrome OS LSM is in use.
'SECURITY_CHROMIUMOS',
]
IS_MODULE = [
# Sanity checks; should be present in builds as modules.
'BLK_DEV_SR',
'BT',
'TUN',
# Useful modules for users that should not be removed
'USB_SERIAL_OTI6858',
]
IS_ENABLED = [
# Either module or enabled, depending on platform.
'VIDEO_V4L2',
]
IS_MISSING = [
# Sanity checks.
'M386', # Never going to optimize to this CPU.
'CHARLIE_THE_UNICORN', # Config not in real kernel config var list.
# Dangerous; allows direct physical memory writing.
'ACPI_CUSTOM_METHOD',
# Dangerous; disables brk ASLR.
'COMPAT_BRK',
# Dangerous; disables VDSO ASLR.
'COMPAT_VDSO',
# Dangerous; allows direct kernel memory writing.
'DEVKMEM',
# Dangerous; allows replacement of running kernel.
'KEXEC',
# Dangerous; allows replacement of running kernel.
'HIBERNATION',
# Assists heap memory attacks; best to keep interface disabled.
'INET_DIAG',
]
IS_EXCLUSIVE = [
# Security; no surprise binary formats.
{
'regex': 'BINFMT_',
'builtin': [
'BINFMT_ELF',
],
'module': [
],
'missing': [
# Sanity checks; one disabled, one does not exist.
'BINFMT_AOUT',
'BINFMT_IMPOSSIBLE',
],
},
# Security; no surprise filesystem formats.
{
'regex': '.*_FS$',
'builtin': [
'DEBUG_FS',
'ECRYPT_FS',
'EXT4_FS',
'EXT4_USE_FOR_EXT23',
'PROC_FS',
'SCSI_PROC_FS',
],
'module': [
'FAT_FS',
'FUSE_FS',
'HFSPLUS_FS',
'ISO9660_FS',
'UDF_FS',
'VFAT_FS',
],
'missing': [
# Sanity checks; one disabled, one does not exist.
'EXT2_FS',
'EXT3_FS',
'XFS_FS',
'IMPOSSIBLE_FS',
],
},
# Security; no surprise partition formats.
# MAC is for external drive formatted on Macintosh.
{
'regex': '.*_PARTITION$',
'builtin': [
'EFI_PARTITION',
'MAC_PARTITION',
'MSDOS_PARTITION',
],
'module': [
],
'missing': [
# Sanity checks; one disabled, one does not exist.
'LDM_PARTITION',
'IMPOSSIBLE_PARTITION',
],
},
]
def is_arm_family(self, arch):
return arch in ['armv7l', 'aarch64']
def is_x86_family(self, arch):
return arch in ['i386', 'x86_64']
def run_once(self):
# Cache the architecture to avoid redundant execs to "uname".
arch = utils.get_arch()
userspace_arch = utils.get_arch_userspace()
# Report the full uname for anyone reading logs.
logging.info('Running %s kernel, %s userspace: %s',
arch, userspace_arch,
utils.system_output('uname -a'))
# Load the list of kernel config variables.
config = kernel_config.KernelConfig()
config.initialize()
# Adjust for kernel-version-specific changes
kernel_ver = os.uname()[2]
if utils.compare_versions(kernel_ver, "3.10") >= 0:
for entry in self.IS_EXCLUSIVE:
if entry['regex'] == 'BINFMT_':
entry['builtin'].append('BINFMT_SCRIPT')
if utils.compare_versions(kernel_ver, "3.14") >= 0:
self.IS_MODULE.append('TEST_ASYNC_DRIVER_PROBE')
for entry in self.IS_EXCLUSIVE:
if entry['regex'] == 'BINFMT_':
entry['builtin'].append('BINFMT_MISC')
if utils.compare_versions(kernel_ver, "3.18") >= 0:
for entry in self.IS_EXCLUSIVE:
if entry['regex'] == '.*_FS$':
entry['builtin'].append('SND_PROC_FS')
# Run the static checks.
map(config.has_builtin, self.IS_BUILTIN)
map(config.has_module, self.IS_MODULE)
map(config.is_enabled, self.IS_ENABLED)
map(config.is_missing, self.IS_MISSING)
map(config.is_exclusive, self.IS_EXCLUSIVE)
# Run the dynamic checks.
# Security; NULL-address hole should be as large as possible.
# Upstream kernel recommends 64k, which should be large enough to
# catch nearly all dereferenced structures.
wanted = '65536'
if self.is_arm_family(arch):
# ... except on ARM where it shouldn't be larger than 32k due
# to historical ELF load location.
wanted = '32768'
config.has_value('DEFAULT_MMAP_MIN_ADDR', [wanted])
# Security; make sure NX page table bits are usable.
if self.is_x86_family(arch):
if arch == "i386":
config.has_builtin('X86_PAE')
else:
config.has_builtin('X86_64')
# Security; marks data segments as RO/NX, text as RO.
if (arch == 'armv7l' and
utils.compare_versions(kernel_ver, "3.8") < 0):
config.is_missing('DEBUG_RODATA')
config.is_missing('DEBUG_SET_MODULE_RONX')
else:
config.has_builtin('DEBUG_RODATA')
config.has_builtin('DEBUG_SET_MODULE_RONX')
if arch == 'aarch64':
config.has_builtin('DEBUG_ALIGN_RODATA')
# NaCl; allow mprotect+PROT_EXEC on noexec mapped files.
config.has_value('MMAP_NOEXEC_TAINT', ['0'])
# Kernel: make sure port 0xED is the one used for I/O delay
if self.is_x86_family(arch):
config.has_builtin('IO_DELAY_0XED')
needed = config.get('CONFIG_IO_DELAY_TYPE_0XED', None)
config.has_value('DEFAULT_IO_DELAY_TYPE', [needed])
# Raise a failure if anything unexpected was seen.
if len(config.failures()):
raise error.TestFail((", ".join(config.failures())))