kpatch: better support for livepatch modules

Livepatch modules can be supported with minimal changes to the kpatch
script.  Adjust for appropriate sysfs paths, core-patching code (in
kernel for livepatch, kpatch.ko for kpatch), and checksum verification
(only verify the checksum if it exists).

Fixes #479.
This commit is contained in:
Joe Lawrence 2016-12-19 13:28:30 -05:00
parent c27b3206cf
commit b55dfb7dee

View File

@ -27,6 +27,14 @@ INSTALLDIR=/var/lib/kpatch
SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))"
VERSION="0.3.4" VERSION="0.3.4"
# Livepatch is built into the kernel, if it's not present
# we must use kpatch core module.
if [[ -e /sys/kernel/livepatch ]] ; then
SYSFS="/sys/kernel/livepatch"
else
SYSFS="/sys/kernel/kpatch/patches"
fi
usage_cmd() { usage_cmd() {
printf ' %-20s\n %s\n' "$1" "$2" >&2 printf ' %-20s\n %s\n' "$1" "$2" >&2
} }
@ -116,8 +124,8 @@ find_core_module() {
return 1 return 1
} }
core_module_loaded () { core_loaded () {
grep -q "T kpatch_register" /proc/kallsyms grep -q -e "T klp_register_patch" -e "T kpatch_register" /proc/kallsyms
} }
get_module_name () { get_module_name () {
@ -129,16 +137,20 @@ verify_module_checksum () {
[[ -z $modname ]] && return 1 [[ -z $modname ]] && return 1
checksum=$(readelf -p .kpatch.checksum $1 | grep '\[.*\]' | awk '{print $3}') checksum=$(readelf -p .kpatch.checksum $1 | grep '\[.*\]' | awk '{print $3}')
[[ -z $checksum ]] && return 1
sysfs_checksum=$(cat /sys/kernel/kpatch/patches/${modname}/checksum) # Fail checksum match only if both exist and diverge
if [[ ! -z $checksum ]] && [[ -e "$SYSFS/${modname}/checksum" ]] ; then
sysfs_checksum=$(cat $SYSFS/${modname}/checksum)
[[ $checksum == $sysfs_checksum ]] || return 1 [[ $checksum == $sysfs_checksum ]] || return 1
fi
return 0
} }
load_module () { load_module () {
local module="$1" local module="$1"
if ! core_module_loaded; then if ! core_loaded; then
if modprobe -q kpatch; then if modprobe -q kpatch; then
echo "loaded core module" echo "loaded core module"
else else
@ -149,7 +161,7 @@ load_module () {
fi fi
local modname=$(get_module_name $module) local modname=$(get_module_name $module)
local moddir=/sys/kernel/kpatch/patches/$modname local moddir="$SYSFS/$modname"
if [[ -d $moddir ]] ; then if [[ -d $moddir ]] ; then
if [[ $(cat "${moddir}/enabled") -eq 0 ]]; then if [[ $(cat "${moddir}/enabled") -eq 0 ]]; then
if verify_module_checksum $module; then # same checksum if verify_module_checksum $module; then # same checksum
@ -191,7 +203,7 @@ load_module () {
unload_module () { unload_module () {
PATCH="${1//-/_}" PATCH="${1//-/_}"
PATCH="${PATCH%.ko}" PATCH="${PATCH%.ko}"
ENABLED=/sys/kernel/kpatch/patches/"$PATCH"/enabled ENABLED="$SYSFS/$PATCH/enabled"
[[ -e "$ENABLED" ]] || die "patch module $1 is not loaded" [[ -e "$ENABLED" ]] || die "patch module $1 is not loaded"
if [[ $(cat "$ENABLED") -eq 1 ]]; then if [[ $(cat "$ENABLED") -eq 1 ]]; then
echo "disabling patch module: $PATCH" echo "disabling patch module: $PATCH"
@ -233,7 +245,7 @@ case "$1" in
[[ "$#" -ne 2 ]] && usage [[ "$#" -ne 2 ]] && usage
case "$2" in case "$2" in
"--all") "--all")
for module in /sys/kernel/kpatch/patches/*; do for module in $SYSFS/*; do
[[ -e $module ]] || continue [[ -e $module ]] || continue
unload_module $(basename $module) || die "failed to unload module $module" unload_module $(basename $module) || die "failed to unload module $module"
done done
@ -320,7 +332,7 @@ case "$1" in
"list") "list")
[[ "$#" -ne 1 ]] && usage [[ "$#" -ne 1 ]] && usage
echo "Loaded patch modules:" echo "Loaded patch modules:"
for module in /sys/kernel/kpatch/patches/*; do for module in $SYSFS/*; do
if [[ -e $module ]] && [[ $(cat $module/enabled) -eq 1 ]]; then if [[ -e $module ]] && [[ $(cat $module/enabled) -eq 1 ]]; then
echo $(basename "$module") echo $(basename "$module")
fi fi