File Index
Files and directories
Sign in
FILE

its_ret_alignment.py

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/x86/bugs/its_ret_alignment.py PYTHON
  1#!/usr/bin/env python3
  2# SPDX-License-Identifier: GPL-2.0
  3#
  4# Copyright (c) 2025 Intel Corporation
  5#
  6# Test for indirect target selection (ITS) mitigation.
  7#
  8# Tests if the RETs are correctly patched by evaluating the
  9# vmlinux .return_sites in /proc/kcore.
 10#
 11# Install dependencies
 12# add-apt-repository ppa:michel-slm/kernel-utils
 13# apt update
 14# apt install -y python3-drgn python3-pyelftools python3-capstone
 15#
 16# Run on target machine
 17# mkdir -p /usr/lib/debug/lib/modules/$(uname -r)
 18# cp $VMLINUX /usr/lib/debug/lib/modules/$(uname -r)/vmlinux
 19#
 20# Usage: ./its_ret_alignment.py
 21
 22import os, sys, argparse
 23from pathlib import Path
 24
 25this_dir = os.path.dirname(os.path.realpath(__file__))
 26sys.path.insert(0, this_dir + '/../../kselftest')
 27import ksft
 28import common as c
 29
 30bug = "indirect_target_selection"
 31mitigation = c.get_sysfs(bug)
 32if not mitigation or "Aligned branch/return thunks" not in mitigation:
 33    ksft.test_result_skip("Skipping its_ret_alignment.py: Aligned branch/return thunks not enabled")
 34    ksft.finished()
 35
 36c.check_dependencies_or_skip(['drgn', 'elftools', 'capstone'], script_name="its_ret_alignment.py")
 37
 38from elftools.elf.elffile import ELFFile
 39from drgn.helpers.common.memory import identify_address
 40
 41cap = c.init_capstone()
 42
 43if len(os.sys.argv) > 1:
 44    arg_vmlinux = os.sys.argv[1]
 45    if not os.path.exists(arg_vmlinux):
 46        ksft.test_result_fail(f"its_ret_alignment.py: vmlinux not found at user-supplied path: {arg_vmlinux}")
 47        ksft.exit_fail()
 48    os.makedirs(f"/usr/lib/debug/lib/modules/{os.uname().release}", exist_ok=True)
 49    os.system(f'cp {arg_vmlinux} /usr/lib/debug/lib/modules/$(uname -r)/vmlinux')
 50
 51vmlinux = f"/usr/lib/debug/lib/modules/{os.uname().release}/vmlinux"
 52if not os.path.exists(vmlinux):
 53    ksft.test_result_fail(f"its_ret_alignment.py: vmlinux not found at {vmlinux}")
 54    ksft.exit_fail()
 55
 56ksft.print_msg(f"Using vmlinux: {vmlinux}")
 57
 58rethunks_start_vmlinux, rethunks_sec_offset, size = c.get_section_info(vmlinux, '.return_sites')
 59ksft.print_msg(f"vmlinux: Section .return_sites (0x{rethunks_start_vmlinux:x}) found at 0x{rethunks_sec_offset:x} with size 0x{size:x}")
 60
 61sites_offset = c.get_patch_sites(vmlinux, rethunks_sec_offset, size)
 62total_rethunk_tests = len(sites_offset)
 63ksft.print_msg(f"Found {total_rethunk_tests} rethunk sites")
 64
 65prog = c.get_runtime_kernel()
 66rethunks_start_kcore = prog.symbol('__return_sites').address
 67ksft.print_msg(f'kcore: __rethunk_sites: 0x{rethunks_start_kcore:x}')
 68
 69its_return_thunk = prog.symbol('its_return_thunk').address
 70ksft.print_msg(f'kcore: its_return_thunk: 0x{its_return_thunk:x}')
 71
 72tests_passed = 0
 73tests_failed = 0
 74tests_unknown = 0
 75tests_skipped = 0
 76
 77with open(vmlinux, 'rb') as f:
 78    elffile = ELFFile(f)
 79    text_section = elffile.get_section_by_name('.text')
 80
 81    for i in range(len(sites_offset)):
 82        site = rethunks_start_kcore + sites_offset[i]
 83        vmlinux_site = rethunks_start_vmlinux + sites_offset[i]
 84        try:
 85            passed = unknown = failed = skipped = False
 86
 87            symbol = identify_address(prog, site)
 88            vmlinux_insn = c.get_instruction_from_vmlinux(elffile, text_section, text_section['sh_addr'], vmlinux_site)
 89            kcore_insn = list(cap.disasm(prog.read(site, 16), site))[0]
 90
 91            insn_end = site + kcore_insn.size - 1
 92
 93            safe_site = insn_end & 0x20
 94            site_status = "" if safe_site else "(unsafe)"
 95
 96            ksft.print_msg(f"\nSite {i}: {symbol} <0x{site:x}> {site_status}")
 97            ksft.print_msg(f"\tvmlinux: 0x{vmlinux_insn.address:x}:\t{vmlinux_insn.mnemonic}\t{vmlinux_insn.op_str}")
 98            ksft.print_msg(f"\tkcore:   0x{kcore_insn.address:x}:\t{kcore_insn.mnemonic}\t{kcore_insn.op_str}")
 99
100            if safe_site:
101                tests_passed += 1
102                passed = True
103                ksft.print_msg(f"\tPASSED: At safe address")
104                continue
105
106            if "jmp" in kcore_insn.mnemonic:
107                passed = True
108            elif "ret" not in kcore_insn.mnemonic:
109                skipped = True
110
111            if passed:
112                ksft.print_msg(f"\tPASSED: Found {kcore_insn.mnemonic} {kcore_insn.op_str}")
113                tests_passed += 1
114            elif skipped:
115                ksft.print_msg(f"\tSKIPPED: Found '{kcore_insn.mnemonic}'")
116                tests_skipped += 1
117            elif unknown:
118                ksft.print_msg(f"UNKNOWN: An unknown instruction: {kcore_insn}")
119                tests_unknown += 1
120            else:
121                ksft.print_msg(f'\t************* FAILED *************')
122                ksft.print_msg(f"\tFound {kcore_insn.mnemonic} {kcore_insn.op_str}")
123                ksft.print_msg(f'\t**********************************')
124                tests_failed += 1
125        except Exception as e:
126            ksft.print_msg(f"UNKNOWN: An unexpected error occurred: {e}")
127            tests_unknown += 1
128
129ksft.print_msg(f"\n\nSummary:")
130ksft.print_msg(f"PASSED: \t{tests_passed} \t/ {total_rethunk_tests}")
131ksft.print_msg(f"FAILED: \t{tests_failed} \t/ {total_rethunk_tests}")
132ksft.print_msg(f"SKIPPED: \t{tests_skipped} \t/ {total_rethunk_tests}")
133ksft.print_msg(f"UNKNOWN: \t{tests_unknown} \t/ {total_rethunk_tests}")
134
135if tests_failed == 0:
136    ksft.test_result_pass("All ITS return thunk sites passed.")
137else:
138    ksft.test_result_fail(f"{tests_failed} failed sites need ITS return thunks.")
139ksft.finished()
~/ Read-only file preview.