From 28a5a1d2759b16d7f3782543645996daccf32eb3 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Wed, 5 Feb 2014 09:58:01 -0600 Subject: [PATCH] add kpatch and kpatch-build scripts Signed-off-by: Seth Jennings --- scripts/kpatch | 179 +++++++++++++++++++++++++++++++++++++++++++ scripts/kpatch-build | 131 +++++++++++++++++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100755 scripts/kpatch create mode 100755 scripts/kpatch-build diff --git a/scripts/kpatch b/scripts/kpatch new file mode 100755 index 0000000..874ffa1 --- /dev/null +++ b/scripts/kpatch @@ -0,0 +1,179 @@ +#!/bin/bash + +# This is the primary kpatch user script that manages loading, unloading +# and displaying information on the hotfixes that are installed on the +# system. + +# Subcommands: +# - enable MODULE +# - disable MODULE +# - load [--all | MODULE] +# - unload [--all | MODULE] +# - list +# - info MODULE + +# TODO: add kernelrelease option to manage releases other than the +# currently running one + +KERNELRELEASE=$(uname -r) +VARDIR=/var/lib/kpatch/$KERNELRELEASE +USRDIR=/usr/lib/kpatch/$KERNELRELEASE + +usage () { + echo "usage:" + echo "kpatch enable MODULE" + echo "kpatch disable MODULE" + echo "kpatch load [--all | MODULE]" + echo "kpatch unload [--all | MODULE]" + echo "kpatch list" + echo "kpatch info MODULE" + exit 1 +} + +# return either VARDIR or USRDIR (or exits if not found) +find_hotfix () { + [ -e $VARDIR/available/$1 ] && DIR=$VARDIR && return + [ -e $USRDIR/available/$1 ] && DIR=$USRDIR && return + echo "Hotfix $2 is not installed" + echo "Use "kpatch list" for available hotfixes" + exit 2 +} + +# takes full path to hotfix module +load_hotfix () { + NAME=$(basename $1) + NAME=${NAME%.*} + insmod $1 + if [ $? -ne 0 ] + then + echo "Hotfix $NAME failed to load" + exit 3 + fi + echo "Hotfix $NAME loaded" +} + +# takes only the module filename +unload_hotfix () { + NAME=$(basename $1) + rmmod ${NAME%.*} + if [ $? -ne 0 ] + then + echo "Hotfix $NAME failed to unload" + exit 3 + fi + echo "Hotfix $NAME unloaded" +} + +unset DIR +[ $# -gt 2 ] || [ $# -lt 1 ] && usage +MODFILE=$2.ko +case $1 in +"enable") + [ $# -ne 2 ] && usage + find_hotfix $MODFILE + if [ -e $DIR/enabled/$MODFILE ] + then + echo "Hotfix $2 is already enabled" + exit 0 + fi + ln -s $DIR/available/$MODFILE $DIR/enabled/$MODFILE + if [ $? -ne 0 ] + then + echo "Failed to enable hotfix $2" + exit 3 + else + echo "Hotfix $2 enabled" + fi + ;; +"disable") + [ $# -ne 2 ] && usage + find_hotfix $MODFILE + if [ ! -e $DIR/enabled/$MODFILE ] + then + echo "Hotfix $2 is already disabled" + exit 0 + fi + rm -f $DIR/enabled/$MODFILE + if [ $? -ne 0 ] + then + echo "Failed to disable hotfix $2" + exit 3 + else + echo "Hotfix $2 disabled" + fi + ;; +"load") + [ $# -ne 2 ] && usage + case $2 in + "--all") + for i in $(ls $VARDIR/enabled) + do + load_hotfix $VARDIR/enabled/$i + done + for i in $(ls $USRDIR/enabled) + do + load_hotfix $USRDIR/enabled/$i + done + ;; + *) + find_hotfix $MODFILE + load_hotfix $DIR/available/$MODFILE + ;; + esac + ;; +"unload") + [ $# -ne 2 ] && usage + case $2 in + "--all") + for i in $(ls $VARDIR/available) + do + unload_hotfix ${i%.*} + done + for i in $(ls $USRDIR/available) + do + unload_hotfix ${i%.*} + done + ;; + *) + find_hotfix $MODFILE + unload_hotfix $2 + ;; + esac + ;; +"list") + [ $# -ne 1 ] && usage + echo "User hotfixes available:" + for i in $(ls $VARDIR/available) + do + echo ${i%.*} + done + echo "" + echo "User hotfixes enabled:" + for i in $(ls $VARDIR/enabled) + do + echo ${i%.*} + done + echo "" + echo "System hotfixes available:" + for i in $(ls $USRDIR/available) + do + echo ${i%.*} + done + echo "" + echo "System hotfixes enabled:" + for i in $(ls $USRDIR/enabled) + do + echo ${i%.*} + done + ;; +"info") + [ $# -ne 2 ] && usage + find_hotfix $MODFILE + echo "Hotfix information for ${2%.*}:" + modinfo $DIR/available/$MODFILE + ;; +*) + echo "subcommand $1 not recognized" + usage + ;; +esac diff --git a/scripts/kpatch-build b/scripts/kpatch-build new file mode 100755 index 0000000..e33ddf3 --- /dev/null +++ b/scripts/kpatch-build @@ -0,0 +1,131 @@ +#/bin/bash + +# kpatch build script +# +# This script takes a patch based on the version of the kernel +# currently running and creates a kernel module that will +# replace modified functions in the kernel such that the +# patched code takes effect. + +# This script: +# - Installs the required yum/rpm tools +# - Downloads the kernel src rpm for the currently running kernel +# - Installs the build dependencies for the src rpm +# - Unpacks and prepares the src rpm for building +# - Builds the base kernel (vmlinux) +# - Builds the patched kernel and monitors changed objects +# - Builds the patched objects with gcc flags -f[function|data]-sections +# - Runs kpatch tools to create and link the patch kernel module + +BASE=$PWD +LOGFILE=$PWD/kpatch-build.log +KPATCHPATH=/usr/local/share/kpatch +ARCHVERSION=$(uname -r) +DISTROVERSION=${ARCHVERSION%*.*} +CPUS=$(grep -c ^processor /proc/cpuinfo) +LOCALVERSION=$(uname -r) +LOCALVERSION=-${LOCALVERSION##*-} + +cleanup() { + rm -Rf kernel-$DISTROVERSION.src.rpm rpmbuild $LOGFILE $TEMPDIR > /dev/null 2>/dev/null +} + +die() { + echo "kpatch encountered an error" + exit 1 +} + +if [ $# -ne 1 ] +then + echo "kpatch patchfile" + exit 2 +fi + +PATCHFILE=$(readlink -f $1) +if [ ! -e $PATCHFILE ] +then + echo "patch file not found" + exit 3 +fi + +cleanup + +echo "Verifying required development tools" +yum install rpmdevtools yum-utils || die + +echo "Downloading kernel source for $ARCHVERSION" +yumdownloader --source kernel-$ARCHVERSION || die + +echo "Verifying build dependencies for kernel package" +yum-builddep kernel-$DISTROVERSION.src.rpm || die + +echo "Unpacking kernel source" +rpmdev-setuptree >> $LOGFILE 2>&1 || die +rpm -Uvh kernel-$DISTROVERSION.src.rpm >> $LOGFILE 2>&1 || die +cd ~/rpmbuild/SPECS || die +rpmbuild -bp --target=$(uname -m) kernel.spec >> $LOGFILE 2>&1 || die +cd ~/rpmbuild/BUILD/kernel-* || die +cd linux-$ARCHVERSION || die +BUILD=$PWD +echo $LOCALVERSION > localversion + +echo "Building the base kernel" +make -j$CPUS vmlinux >> $LOGFILE 2>&1 || die +TEMPDIR=`mktemp -d` || die +cp -R $KPATCHPATH/* $TEMPDIR || die +cp vmlinux $TEMPDIR || die + +echo "Building the patched kernel" +patch -p1 < $PATCHFILE >> $LOGFILE 2>&1 +make -j$CPUS vmlinux > $TEMPDIR/patched_build.log 2>&1 || die + +echo "Detecting changed objects" +grep CC $TEMPDIR/patched_build.log | grep -v init/version.o | awk '{print $2}' >> $TEMPDIR/changed_objs +if [ $? -ne 0 ] +then + echo "No changed objects" + exit 0 +fi + +echo "Rebuilding changed objects" +mkdir $TEMPDIR/patched +for i in $(cat $TEMPDIR/changed_objs) +do + rm -f $i + KCFLAGS="-ffunction-sections -fdata-sections" make $i >> $LOGFILE 2>&1 || die + strip -d $i + cp -f $i $TEMPDIR/patched/ + +done +patch -R -p1 < $PATCHFILE >> $LOGFILE 2>&1 +mkdir $TEMPDIR/base +for i in $(cat $TEMPDIR/changed_objs) +do + rm -f $i + KCFLAGS="-ffunction-sections -fdata-sections" make $i >> $LOGFILE 2>&1 || die + strip -d $i + cp -f $i $TEMPDIR/base/ +done + +echo "Extracting new and modified ELF sections" +cd $TEMPDIR +mkdir output +for i in base/* +do + FILE=`basename $i` + ./tools/create-diff-object base/$FILE patched/$FILE output/$FILE >> $LOGFILE 2>&1 +done + +echo "Building hotfix module" +ld -r -o output.o output/* >> $LOGFILE 2>&1 || die +./tools/add-patches-section output.o vmlinux >> $LOGFILE 2>&1 || die +KPATCH_BUILD=$BUILD make >> $LOGFILE 2>&1 || die +strip -d kpatch-patch.ko >> $LOGFILE 2>&1 || die +./tools/link-vmlinux-syms kpatch-patch.ko vmlinux >> $LOGFILE 2>&1 || die + +echo "Patch generation complete" +cp -f $TEMPDIR/kpatch-patch.ko $BASE +echo "Cleaning up" +cleanup +echo "Done" +