164 lines
4.7 KiB
Python
164 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
#
|
|
# Copyright (c) 2025 Intel Corporation
|
|
#
|
|
# This contains kselftest framework adapted common functions for testing
|
|
# mitigation for x86 bugs.
|
|
|
|
import os, sys, re, shutil
|
|
|
|
sys.path.insert(0, '../../kselftest')
|
|
import ksft
|
|
|
|
def read_file(path):
|
|
if not os.path.exists(path):
|
|
return None
|
|
with open(path, 'r') as file:
|
|
return file.read().strip()
|
|
|
|
def cpuinfo_has(arg):
|
|
cpuinfo = read_file('/proc/cpuinfo')
|
|
if arg in cpuinfo:
|
|
return True
|
|
return False
|
|
|
|
def cmdline_has(arg):
|
|
cmdline = read_file('/proc/cmdline')
|
|
if arg in cmdline:
|
|
return True
|
|
return False
|
|
|
|
def cmdline_has_either(args):
|
|
cmdline = read_file('/proc/cmdline')
|
|
for arg in args:
|
|
if arg in cmdline:
|
|
return True
|
|
return False
|
|
|
|
def cmdline_has_none(args):
|
|
return not cmdline_has_either(args)
|
|
|
|
def cmdline_has_all(args):
|
|
cmdline = read_file('/proc/cmdline')
|
|
for arg in args:
|
|
if arg not in cmdline:
|
|
return False
|
|
return True
|
|
|
|
def get_sysfs(bug):
|
|
return read_file("/sys/devices/system/cpu/vulnerabilities/" + bug)
|
|
|
|
def sysfs_has(bug, mitigation):
|
|
status = get_sysfs(bug)
|
|
if mitigation in status:
|
|
return True
|
|
return False
|
|
|
|
def sysfs_has_either(bugs, mitigations):
|
|
for bug in bugs:
|
|
for mitigation in mitigations:
|
|
if sysfs_has(bug, mitigation):
|
|
return True
|
|
return False
|
|
|
|
def sysfs_has_none(bugs, mitigations):
|
|
return not sysfs_has_either(bugs, mitigations)
|
|
|
|
def sysfs_has_all(bugs, mitigations):
|
|
for bug in bugs:
|
|
for mitigation in mitigations:
|
|
if not sysfs_has(bug, mitigation):
|
|
return False
|
|
return True
|
|
|
|
def bug_check_pass(bug, found):
|
|
ksft.print_msg(f"\nFound: {found}")
|
|
# ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}")
|
|
ksft.test_result_pass(f'{bug}: {found}')
|
|
|
|
def bug_check_fail(bug, found, expected):
|
|
ksft.print_msg(f'\nFound:\t {found}')
|
|
ksft.print_msg(f'Expected:\t {expected}')
|
|
ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}")
|
|
ksft.test_result_fail(f'{bug}: {found}')
|
|
|
|
def bug_status_unknown(bug, found):
|
|
ksft.print_msg(f'\nUnknown status: {found}')
|
|
ksft.print_msg(f"\ncmdline: {read_file('/proc/cmdline')}")
|
|
ksft.test_result_fail(f'{bug}: {found}')
|
|
|
|
def basic_checks_sufficient(bug, mitigation):
|
|
if not mitigation:
|
|
bug_status_unknown(bug, "None")
|
|
return True
|
|
elif mitigation == "Not affected":
|
|
ksft.test_result_pass(bug)
|
|
return True
|
|
elif mitigation == "Vulnerable":
|
|
if cmdline_has_either([f'{bug}=off', 'mitigations=off']):
|
|
bug_check_pass(bug, mitigation)
|
|
return True
|
|
return False
|
|
|
|
def get_section_info(vmlinux, section_name):
|
|
from elftools.elf.elffile import ELFFile
|
|
with open(vmlinux, 'rb') as f:
|
|
elffile = ELFFile(f)
|
|
section = elffile.get_section_by_name(section_name)
|
|
if section is None:
|
|
ksft.print_msg("Available sections in vmlinux:")
|
|
for sec in elffile.iter_sections():
|
|
ksft.print_msg(sec.name)
|
|
raise ValueError(f"Section {section_name} not found in {vmlinux}")
|
|
return section['sh_addr'], section['sh_offset'], section['sh_size']
|
|
|
|
def get_patch_sites(vmlinux, offset, size):
|
|
import struct
|
|
output = []
|
|
with open(vmlinux, 'rb') as f:
|
|
f.seek(offset)
|
|
i = 0
|
|
while i < size:
|
|
data = f.read(4) # s32
|
|
if not data:
|
|
break
|
|
sym_offset = struct.unpack('<i', data)[0] + i
|
|
i += 4
|
|
output.append(sym_offset)
|
|
return output
|
|
|
|
def get_instruction_from_vmlinux(elffile, section, virtual_address, target_address):
|
|
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
|
|
section_start = section['sh_addr']
|
|
section_end = section_start + section['sh_size']
|
|
|
|
if not (section_start <= target_address < section_end):
|
|
return None
|
|
|
|
offset = target_address - section_start
|
|
code = section.data()[offset:offset + 16]
|
|
|
|
cap = init_capstone()
|
|
for instruction in cap.disasm(code, target_address):
|
|
if instruction.address == target_address:
|
|
return instruction
|
|
return None
|
|
|
|
def init_capstone():
|
|
from capstone import Cs, CS_ARCH_X86, CS_MODE_64, CS_OPT_SYNTAX_ATT
|
|
cap = Cs(CS_ARCH_X86, CS_MODE_64)
|
|
cap.syntax = CS_OPT_SYNTAX_ATT
|
|
return cap
|
|
|
|
def get_runtime_kernel():
|
|
import drgn
|
|
return drgn.program_from_kernel()
|
|
|
|
def check_dependencies_or_skip(modules, script_name="unknown test"):
|
|
for mod in modules:
|
|
try:
|
|
__import__(mod)
|
|
except ImportError:
|
|
ksft.test_result_skip(f"Skipping {script_name}: missing module '{mod}'")
|
|
ksft.finished()
|