1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
#!/usr/bin/env python3
import argparse
import os
import struct
import sys
import array
from os.path import join
from fcntl import ioctl
from enum import Enum
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('--reset', action='store_true')
group.add_argument('--check', action='store_true')
args = parser.parse_args()
# MSRC001_1020: Load-Store Configuration
MSR = 0xc0011020
# Disable SpecLockMap
BIT = 1 << 54
EFIVARS_PATH = '/sys/firmware/efi/efivars/'
SecureBoot = Enum("SecureBoot", "enabled disabled checkfailed")
def is_secure_boot_enabled():
if not os.path.isdir(EFIVARS_PATH):
return SecureBoot.checkfailed
files = [f for f in os.listdir(EFIVARS_PATH) if f.startswith("SecureBoot-")]
var_path = join(EFIVARS_PATH, files[0])
attr = []
data = []
try:
if os.path.exists(var_path):
with open(var_path, 'rb') as fd:
buffer = fd.read()
attr = buffer[:4]
data = buffer[4:]
if int(data[0]) == 1:
return SecureBoot.enabled
else:
return SecureBoot.disabled
except:
return SecureBoot.checkfailed
if not os.path.exists('/dev/cpu/0/msr'):
ret = os.system('modprobe msr')
if ret:
sys.exit(ret)
def read_msr(cpu):
try:
msr = os.open('/dev/cpu/{}/msr'.format(cpu), os.O_RDONLY)
except PermissionError as e:
sys.stderr.write(str(e) + '\n')
print("Permission denied opening MSR for reading.")
print("Try running as root / with sudo.")
sys.exit(1)
try:
os.lseek(msr, MSR, os.SEEK_SET)
(val,) = struct.unpack('<q', os.read(msr, 8))
return val
except PermissionError as e:
sys.stderr.write(str(e) + '\n')
print("Permission denied on reading from MSR.")
sys.exit(1)
except OSError as e:
sys.stderr.write(str(e) + '\n')
print("General error on reading from MSR.")
sys.exit(1)
finally:
os.close(msr)
cpus = [cpu for cpu in os.listdir('/dev/cpu') if cpu.isdigit()]
if not args.check:
for cpu in cpus:
val = read_msr(cpu)
if args.reset:
if val & BIT == 0:
continue
val &= ~BIT
else:
if val & BIT:
continue
val |= BIT
try:
msr = os.open('/dev/cpu/{}/msr'.format(cpu), os.O_WRONLY)
except PermissionError:
sys.stderr.write(str(e) + '\n')
print("Permission denied opening MSR for writing.")
print("Try running as root / with sudo.")
sys.exit(1)
try:
os.lseek(msr, MSR, os.SEEK_SET)
os.write(msr, struct.pack('<q', val))
except PermissionError as e:
check = is_secure_boot_enabled()
sys.stderr.write(str(e) + '\n')
print("Permission denied writing to MSR.")
if check == SecureBoot.enabled:
print("Secure Boot is enabled, which causes this error. Try disabling Secure Boot.")
elif check == SecureBoot.disabled:
print("Secure Boot is disabled so that's not the problem.")
print("You may want to use another approach, please see https://github.com/rr-debugger/rr/wiki/Zen.")
sys.exit(1)
except OSError as e:
sys.stderr.write(str(e) + '\n')
print("General error on writing to MSR.")
sys.exit(1)
finally:
os.close(msr)
ssb_status = 'unknown'
if not args.reset:
import ctypes
lib = ctypes.CDLL(None)
prctl = lib.prctl
prctl.restype = ctypes.c_int
prctl.argtypes = (ctypes.c_int, ctypes.c_ulong, ctypes.c_ulong, ctypes.c_ulong, ctypes.c_ulong)
PR_GET_SPECULATION_CTRL = 52
PR_SET_SPECULATION_CTRL = 53
PR_SPEC_STORE_BYPASS = 0
PR_SPEC_PRCTL = 1 << 0
PR_SPEC_DISABLE = 1 << 2
# When the kernel does per-process SSB mitigation via prctl or seccomp, it touches the same
# MSR that we changed, but does so based on a value of the MSR it got at boot time, so it
# effectively will reset the bit we just set if it wasn't already set at boot time.
# This doesn't happen when the SSB mitigation is either entirely on or off.
# This is specific to Zen and Zen+, because Zen 2 doesn't require the kernel to change the MSR.
# Check whether the kernel does per-process SSB mitigation, and if it does, enable it for this
# process.
ssb_mode = prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, 0, 0, 0)
if ssb_mode >= 0 and ssb_mode & PR_SPEC_PRCTL:
mitigated = (prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_DISABLE, 0, 0) == 0)
if not mitigated:
print('Failed to enable SSB mitigation')
else:
ssb_status = 'mitigated'
else:
ssb_status = 'immutable'
msrs = [read_msr(cpu) & BIT for cpu in cpus]
if all(msr for msr in msrs):
if ssb_status in ('mitigated', 'immutable') or args.check:
print('Zen workaround in place')
else:
print('Zen workaround maybe in place.')
elif args.reset or args.check:
if all(not msr for msr in msrs):
print('Zen workaround disabled')
elif args.reset:
print('Zen workaround somehow not entirely disabled?')
else:
print('Zen workaround not entirely enabled?')
else:
print('Zen workaround does not stick. Please see https://github.com/rr-debugger/rr/wiki/Zen')
|