Skip to content

Instantly share code, notes, and snippets.

@Freaky
Last active August 12, 2023 18:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Freaky/2560975d3c94246b86f464b8be75c967 to your computer and use it in GitHub Desktop.
Save Freaky/2560975d3c94246b86f464b8be75c967 to your computer and use it in GitHub Desktop.
FreeBSD Zenbleed mitigation rc script
#!/bin/sh
#
# PROVIDE: zenbleed_workaround
# REQUIRE: root mountcritlocal microcode_update
# BEFORE: SERVERS
# KEYWORD: nojail resume
# Source: https://gist.github.com/Freaky/2560975d3c94246b86f464b8be75c967
#
# Copyright (c) 2023 Thomas Hurst <tom@hur.st>
# License: https://opensource.org/license/mit/
. /etc/rc.subr
name="zenbleed_workaround"
desc="Zenbleed MSR chicken bit workaround"
rcvar="zenbleed_workaround_enable"
extra_commands="resume"
start_precmd="check_zen2"
resume_precmd="${start_precmd}"
stop_precmd="${start_precmd}"
start_cmd="workaround_zenbleed"
resume_cmd="${start_cmd}"
stop_cmd="restore_zenbleed"
load_rc_config "${name}"
: "${zenbleed_workaround_enable:=NO}"
CPUCTL="/usr/sbin/cpucontrol"
check_zen2() {
if ! kldstat -q -m cpuctl; then
if ! kldload cpuctl > /dev/null 2>&1; then
warn "Can't load cpuctl module."
return 1
fi
fi
authenticamd="0x68747541 0x444d4163 0x69746e65"
if ! ${CPUCTL} -i 0x0 /dev/cpuctl0 | grep -q "$authenticamd"; then
warn "Not an AMD processor, ignoring"
return 1
fi
cpuid=$(${CPUCTL} -i 0x1 /dev/cpuctl0 | awk '{ print $4 }')
family=$(( ((cpuid & 0xf00) >> 8) + ((cpuid & 0x0ff00000) >> 20) ))
model=$(( (cpuid & 0xf0) >> 4 | (cpuid & 0x000f0000) >> 12 ))
# Zen 2 is Family 23 Model 49 - 160
if ! { [ $family -eq 23 ] && [ $model -ge 49 ] && [ $model -le 160 ]; } then
warn "Not a Zen 2 processor, ignoring"
return 1
fi
hmicrocode=$(${CPUCTL} -m 0x8b /dev/cpuctl0 | awk '{ print $4 }')
microcode=$((hmicrocode))
# Numbers sourced from Linux patch:
# https://freshbsd.org/linux/linux/commit/0a9266b79cacdd02b888aed1308c308ad6d4ee4e
if [ $model -ge $((0x30)) ] && [ $model -le $((0x3f)) ] && [ $microcode -ge $((0x0830107a)) ] ||
[ $model -ge $((0x60)) ] && [ $model -le $((0x67)) ] && [ $microcode -ge $((0x0860010b)) ] ||
[ $model -ge $((0x68)) ] && [ $model -le $((0x6f)) ] && [ $microcode -ge $((0x08608105)) ] ||
[ $model -ge $((0x70)) ] && [ $model -le $((0x7f)) ] && [ $microcode -ge $((0x08701032)) ] ||
[ $model -ge $((0xa0)) ] && [ $model -le $((0xaf)) ] && [ $microcode -ge $((0x08a00008)) ]; then
warn "Known-good microcode detected: ${hmicrocode}, ignoring"
return 1
fi
}
check_date() {
# Start whinging mid-December
if [ "$(/bin/date +%s)" -ge 1702684800 ]; then
warn "Zenbleed should be resolved by updated AMD microcode, please check."
warn "Updated microcode is provided by sysutils/devcpu-data"
fi
}
workaround_zenbleed() {
check_date
check_startmsgs && echo "Applying Zenbleed MSR workaround"
for D in /dev/cpuctl*
do
${CPUCTL} -m "0xc0011029|=0x200" "$D"
done
}
restore_zenbleed() {
check_startmsgs && echo "Removing Zenbleed MSR workaround"
for D in /dev/cpuctl*
do
${CPUCTL} -m "0xc0011029&=~0x200" "$D"
done
}
run_rc_command "$1"
@monwarez
Copy link

monwarez commented Aug 2, 2023

Hello nice works, I have noticed that after resume the chicken bit is reset.
So it could be great to have the resume clause in this script. Here a slight modification:

#
# PROVIDE: zenbleed_workaround
# KEYWORD: nojail resume

I think that a jail should not have access to this kind of power, thus nojail.

name="zenbleed_workaround"
desc="Zenbleed MSR chicken bit workaround"
rcvar="zenbleed_workaround_enable"

extra_commands="resume"

Here we add the resume extra commands that will simply restart zenbleed_workaround

start_precmd="check_zen2"
stop_precmd="$start_precmd"
start_cmd="workaround_zenbleed"
stop_cmd="restore_zenbleed"
resume_cmd="resume_zenbleed"

And then

resume_zenbleed() {
	run_rc_command restart
}

Here the simple diff

3a4
> # KEYWORD: nojail resume
10a12
> extra_commands="resume"
14a17
> resume_cmd="resume_zenbleed"
52a56,59
> }
>
> resume_zenbleed() {
> 	run_rc_command restart

@Freaky
Copy link
Author

Freaky commented Aug 2, 2023

Thanks. I added a resume command (just pointing at start_cmd, no need to run restart or indirect it through a function I can see), and made it slightly less chatty.

@grahamperrin
Copy link

@Freaky
Copy link
Author

Freaky commented Aug 3, 2023

Proof of concept BSD port: https://git.hardenedbsd.org/shawn.webb/zenbleed (be sure to co the shawn.webb/bsd/main branch)

@monwarez
Copy link

monwarez commented Aug 3, 2023

Thanks. I added a resume command (just pointing at start_cmd, no need to run restart or indirect it through a function I can see), and made it slightly less chatty.

Did you check that it will still check for the correct cpu ?
For me it seems like it will only check at start, not after resume.
So you can have a case where it will not initially start (like on an intel cpu), but after resume it will set the bit.
But maybe I don't really know how resume works, maybe it check if it succeed before resuming it.

@Freaky
Copy link
Author

Freaky commented Aug 3, 2023

Good point. I added a resume_precmd.

@Freaky
Copy link
Author

Freaky commented Aug 12, 2023

I added known-good microcode checks (just EPYC "Rome" CPUs right now I believe), and a warning that appears if you use the script after mid December.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment