blob: 703ce758ad544ca0d1a104ba114bfc31bfbe9f2a [file] [log] [blame] [edit]
# 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(2) 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',
# We don't need to provide access to *all* symbols in /proc/kallsyms.
'KALLSYMS_ALL',
# bpf(2) syscall can be used to generate code patterns in kernel memory.
'BPF_SYSCALL',
# This callback can be subverted to point to arbitrary programs. We
# require firmware to be in the rootfs at normal locations which lets
# the kernel locate things itself.
'FW_LOADER_USER_HELPER',
'FW_LOADER_USER_HELPER_FALLBACK',
]
MISSING_OK = [
# Due to a bug (crbug.com/782034), modifying this file together with
# kernel changes might cause failures in the CQ. In order to avoid that,
# this list contains modules that are okay that they are missing.
]
IS_EXCLUSIVE = [
# Security; no surprise binary formats.
{
'regex': 'BINFMT_',
'builtin': [
'BINFMT_ELF',
],
'module': [
],
'enabled': [
],
'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',
'PROC_FS',
'SCSI_PROC_FS',
],
'module': [
'FAT_FS',
'FUSE_FS',
'HFSPLUS_FS',
'ISO9660_FS',
'UDF_FS',
'VFAT_FS',
],
'enabled': [
],
'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': [
],
'enabled': [
],
'missing': [
# Sanity checks; one disabled, one does not exist.
'LDM_PARTITION',
'IMPOSSIBLE_PARTITION',
],
},
]
def is_x86_family(self, arch):
"""
Returns true if the architecture is x86 family.
"""
return arch in ['i386', 'x86_64']
def run_once(self):
"""
The actual test.
"""
# 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(missing_ok=self.MISSING_OK)
# Adjust for kernel-version-specific changes
kernel_ver = os.uname()[2]
# For linux-3.10 or newer.
if utils.compare_versions(kernel_ver, "3.10") >= 0:
for entry in self.IS_EXCLUSIVE:
if entry['regex'] == 'BINFMT_':
entry['builtin'].append('BINFMT_SCRIPT')
# For linux-3.14 or newer.
if utils.compare_versions(kernel_ver, "3.14") >= 0:
self.IS_MODULE.append('TEST_ASYNC_DRIVER_PROBE')
self.IS_MISSING.remove('INET_DIAG')
for entry in self.IS_EXCLUSIVE:
if entry['regex'] == 'BINFMT_':
entry['builtin'].append('BINFMT_MISC')
if entry['regex'] == '.*_FS$':
entry['module'].append('NFS_FS')
# For linux-3.18 or newer.
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')
entry['builtin'].append('USB_CONFIGFS_F_FS')
entry['builtin'].append('ESD_FS')
entry['enabled'].append('CONFIGFS_FS')
entry['module'].append('USB_F_FS')
# Like FW_LOADER_USER_HELPER, these may be exploited by userspace.
# We run udev everywhere which uses netlink sockets for event
# propagation rather than executing programs, so don't need this.
self.IS_MISSING.append('UEVENT_HELPER')
self.IS_MISSING.append('UEVENT_HELPER_PATH')
# For kernels older than linux-4.4.
if utils.compare_versions(kernel_ver, "4.4") < 0:
for entry in self.IS_EXCLUSIVE:
if entry['regex'] == '.*_FS$':
entry['builtin'].append('EXT4_USE_FOR_EXT23')
# For linux-4.4 or newer.
if utils.compare_versions(kernel_ver, '4.4') >= 0:
self.IS_BUILTIN.append('STATIC_USERMODEHELPER')
# For linux-4.19 or newer.
if utils.compare_versions(kernel_ver, "4.19") >= 0:
self.IS_MISSING.remove('BPF_SYSCALL')
self.IS_BUILTIN.append('HAVE_EBPF_JIT')
self.IS_BUILTIN.append('BPF_JIT_ALWAYS_ON')
self.IS_BUILTIN.remove('CC_STACKPROTECTOR')
self.IS_BUILTIN.append('STACKPROTECTOR')
# 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. For
# compatibility with ARM binaries (even on x86) this needs to
# be 32k.
wanted = '32768'
config.has_value('DEFAULT_MMAP_MIN_ADDR', [wanted])
# Security; make sure usermode helper is our tool for linux-4.4+.
if utils.compare_versions(kernel_ver, '4.4') >= 0:
wanted = '"/sbin/usermode-helper"'
config.has_value('STATIC_USERMODEHELPER_PATH', [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 utils.compare_versions(kernel_ver, "4.11") < 0:
config.has_builtin('DEBUG_RODATA')
config.has_builtin('DEBUG_SET_MODULE_RONX')
else:
config.has_builtin('STRICT_KERNEL_RWX')
config.has_builtin('STRICT_MODULE_RWX')
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())))