diff --git a/SOURCES/0002-Support-modprobe-style-names-in-kpatch-load.patch b/SOURCES/0002-Support-modprobe-style-names-in-kpatch-load.patch new file mode 100644 index 0000000..cb6373a --- /dev/null +++ b/SOURCES/0002-Support-modprobe-style-names-in-kpatch-load.patch @@ -0,0 +1,65 @@ +From 1d63ce84932efda559d6d745930e1b05f446a334 Mon Sep 17 00:00:00 2001 +From: Seth Jennings +Date: Tue, 26 Aug 2014 15:47:07 -0500 +Subject: [PATCH 2/5] Support modprobe style names in kpatch load +To: rhkernel-list@redhat.com + +When the argument is a .ko file, it should be considered a path (i.e. +don't even look for it in the installed DB). When the argument is a +module name, it should be considered a loaded or installed module (and +then in the case of kpatch load we have to do a reverse translation of +all installed modules to see if any of them match). + +Signed-off-by: Seth Jennings +--- + kpatch/kpatch | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/kpatch/kpatch b/kpatch/kpatch +index 074beeb..1d33641 100755 +--- a/kpatch/kpatch ++++ b/kpatch/kpatch +@@ -73,9 +73,29 @@ __find_module () { + return 1 + } + ++mod_name () { ++ MODNAME="$(basename $1)" ++ MODNAME="${MODNAME%.ko}" ++ MODNAME="${MODNAME//-/_}" ++} ++ + find_module () { + arg="$1" +- __find_module "${arg}" ++ if [[ "$arg" =~ \.ko ]]; then ++ __find_module "$arg" || return 1 ++ mod_name "$MODULE" ++ return ++ else ++ for i in $INSTALLDIR/$(uname -r)/*; do ++ mod_name "$i" ++ if [[ $MODNAME == $arg ]]; then ++ MODULE="$i" ++ return ++ fi ++ done ++ fi ++ ++ return 1 + } + + find_core_module() { +@@ -268,7 +288,8 @@ case "$1" in + [[ -e "$kdir" ]] || continue + for module in $kdir/*; do + [[ -e "$module" ]] || continue +- echo "$(basename $module) ($(basename $kdir))" ++ mod_name "$module" ++ echo "$MODNAME ($(basename $kdir))" + done + done + ;; +-- +1.9.3 + diff --git a/SOURCES/0003-Support-modprobe-format-names-in-uninstall.patch b/SOURCES/0003-Support-modprobe-format-names-in-uninstall.patch new file mode 100644 index 0000000..5918551 --- /dev/null +++ b/SOURCES/0003-Support-modprobe-format-names-in-uninstall.patch @@ -0,0 +1,45 @@ +From 58134fd4490ebe57840762afef5d095a9a4a9771 Mon Sep 17 00:00:00 2001 +From: Seth Jennings +Date: Wed, 27 Aug 2014 14:30:39 -0500 +Subject: [PATCH 3/5] Support modprobe format names in uninstall +To: rhkernel-list@redhat.com + +Signed-off-by: Seth Jennings +--- + kpatch/kpatch | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/kpatch/kpatch b/kpatch/kpatch +index 1d33641..1a1b1ac 100755 +--- a/kpatch/kpatch ++++ b/kpatch/kpatch +@@ -264,10 +264,24 @@ case "$1" in + shift + done + +- [[ ! -e $INSTALLDIR/$KVER/"$PATCH" ]] && die "$PATCH is not installed for kernel $KVER" ++ MODULE=$INSTALLDIR/$KVER/"$PATCH" ++ if [[ ! -f "$MODULE" ]]; then ++ mod_name "$PATCH" ++ PATCHNAME=$MODNAME ++ for i in $INSTALLDIR/$KVER/*; do ++ mod_name "$i" ++ if [[ $MODNAME == $PATCHNAME ]]; then ++ MODULE="$i" ++ break ++ fi ++ done ++ fi ++ ++ [[ ! -e $MODULE ]] && die "$PATCH is not installed for kernel $KVER" ++ + + echo "uninstalling $PATCH ($KVER)" +- rm -f $INSTALLDIR/$KVER/"$PATCH" || die "failed to uninstall module $PATCH" ++ rm -f $MODULE || die "failed to uninstall module $PATCH" + if lsinitrd -k $KVER &> /dev/null; then + echo "rebuilding $KVER initramfs" + dracut -f --kver $KVER || die "dracut failed" +-- +1.9.3 + diff --git a/SOURCES/0003-re-enable-patch-modules-with-checksum-matching.patch b/SOURCES/0003-re-enable-patch-modules-with-checksum-matching.patch new file mode 100644 index 0000000..1a3d64b --- /dev/null +++ b/SOURCES/0003-re-enable-patch-modules-with-checksum-matching.patch @@ -0,0 +1,269 @@ +From b0058988c7e57627ae64ace40377de6da4fa14b9 Mon Sep 17 00:00:00 2001 +From: Jessica Yu +Date: Thu, 4 Sep 2014 07:38:17 -0700 +Subject: [PATCH 3/3] re-enable patch modules with checksum matching +To: rhkernel-list@redhat.com + +In order to safely re-enable patch modules, add a special +.kpatch.checksum section containing an md5sum of a patch module's +contents. The contents of this section are exported to sysfs via +patch_init and double checked when kpatch load finds that a module of +the same name is already loaded. + +Conflicts: + kpatch-build/kpatch-build +--- + kmod/core/core.c | 19 ++++++++++++------ + kmod/patch/kpatch-patch-hook.c | 45 ++++++++++++++++++++++++++++-------------- + kmod/patch/kpatch.lds | 1 + + kpatch-build/kpatch-build | 5 ++++- + kpatch/kpatch | 32 ++++++++++++++++++++++++++++++ + 5 files changed, 80 insertions(+), 22 deletions(-) + +diff --git a/kmod/core/core.c b/kmod/core/core.c +index 8c1bb37..bb624a9 100644 +--- a/kmod/core/core.c ++++ b/kmod/core/core.c +@@ -811,12 +811,16 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace) + + down(&kpatch_mutex); + +- kpmod->enabled = false; ++ if (kpmod->enabled) { ++ ret = -EINVAL; ++ goto err_up; ++ } ++ + list_add_tail(&kpmod->list, &kpmod_list); + + if (!try_module_get(kpmod->mod)) { + ret = -ENODEV; +- goto err_up; ++ goto err_list; + } + + list_for_each_entry(object, &kpmod->objects, list) { +@@ -932,8 +936,9 @@ err_unlink: + if (kpatch_object_linked(object)) + kpatch_unlink_object(object); + module_put(kpmod->mod); +-err_up: ++err_list: + list_del(&kpmod->list); ++err_up: + up(&kpatch_mutex); + return ret; + } +@@ -945,11 +950,13 @@ int kpatch_unregister(struct kpatch_module *kpmod) + struct kpatch_func *func; + int ret, force = 0; + +- if (!kpmod->enabled) +- return -EINVAL; +- + down(&kpatch_mutex); + ++ if (!kpmod->enabled) { ++ ret = -EINVAL; ++ goto out; ++ } ++ + do_for_each_linked_func(kpmod, func) { + func->op = KPATCH_OP_UNPATCH; + if (func->force) +diff --git a/kmod/patch/kpatch-patch-hook.c b/kmod/patch/kpatch-patch-hook.c +index ea68878..1370223 100644 +--- a/kmod/patch/kpatch-patch-hook.c ++++ b/kmod/patch/kpatch-patch-hook.c +@@ -35,6 +35,7 @@ extern struct kpatch_patch_dynrela __kpatch_dynrelas[], __kpatch_dynrelas_end[]; + extern struct kpatch_patch_hook __kpatch_hooks_load[], __kpatch_hooks_load_end[]; + extern struct kpatch_patch_hook __kpatch_hooks_unload[], __kpatch_hooks_unload_end[]; + extern unsigned long __kpatch_force_funcs[], __kpatch_force_funcs_end[]; ++extern char __kpatch_checksum[]; + + static struct kpatch_module kpmod; + static struct kobject *patch_kobj; +@@ -61,29 +62,43 @@ static ssize_t patch_enabled_store(struct kobject *kobj, + int ret; + unsigned long val; + +- /* only disabling is supported */ +- if (!kpmod.enabled) +- return -EINVAL; +- + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + val = !!val; + +- /* only disabling is supported */ + if (val) +- return -EINVAL; ++ ret = kpatch_register(&kpmod, replace); ++ else ++ ret = kpatch_unregister(&kpmod); + +- ret = kpatch_unregister(&kpmod); + if (ret) + return ret; + + return count; + } + ++static ssize_t checksum_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%s\n", __kpatch_checksum); ++} ++ + static struct kobj_attribute patch_enabled_attr = + __ATTR(enabled, 0644, patch_enabled_show, patch_enabled_store); ++static struct kobj_attribute checksum_attr = ++ __ATTR(checksum, 0444, checksum_show, NULL); ++ ++static struct attribute *kpatch_attrs[] = { ++ &patch_enabled_attr.attr, ++ &checksum_attr.attr, ++ NULL, ++}; ++ ++static struct attribute_group kpatch_attr_group = { ++ .attrs = kpatch_attrs, ++}; + + static ssize_t func_old_addr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +@@ -354,14 +369,10 @@ static int __init patch_init(void) + if (!patch_kobj) + return -ENOMEM; + +- ret = sysfs_create_file(patch_kobj, &patch_enabled_attr.attr); +- if (ret) +- goto err_patch; +- + functions_kobj = kobject_create_and_add("functions", patch_kobj); + if (!functions_kobj) { + ret = -ENOMEM; +- goto err_sysfs; ++ goto err_patch; + } + + kpmod.mod = THIS_MODULE; +@@ -383,13 +394,17 @@ static int __init patch_init(void) + if (ret) + goto err_objects; + ++ ret = sysfs_create_group(patch_kobj, &kpatch_attr_group); ++ if (ret) ++ goto err_sysfs; ++ + return 0; + ++err_sysfs: ++ kpatch_unregister(&kpmod); + err_objects: + patch_free_objects(); + kobject_put(functions_kobj); +-err_sysfs: +- sysfs_remove_file(patch_kobj, &patch_enabled_attr.attr); + err_patch: + kobject_put(patch_kobj); + return ret; +@@ -401,7 +416,7 @@ static void __exit patch_exit(void) + + patch_free_objects(); + kobject_put(functions_kobj); +- sysfs_remove_file(patch_kobj, &patch_enabled_attr.attr); ++ sysfs_remove_group(patch_kobj, &kpatch_attr_group); + kobject_put(patch_kobj); + } + +diff --git a/kmod/patch/kpatch.lds b/kmod/patch/kpatch.lds +index 105e5e6..49ae8e7 100644 +--- a/kmod/patch/kpatch.lds ++++ b/kmod/patch/kpatch.lds +@@ -2,6 +2,7 @@ __kpatch_funcs = ADDR(.kpatch.funcs); + __kpatch_funcs_end = ADDR(.kpatch.funcs) + SIZEOF(.kpatch.funcs); + __kpatch_dynrelas = ADDR(.kpatch.dynrelas); + __kpatch_dynrelas_end = ADDR(.kpatch.dynrelas) + SIZEOF(.kpatch.dynrelas); ++__kpatch_checksum = ADDR(.kpatch.checksum); + SECTIONS + { + .kpatch.hooks.load : { +diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build +index 91bf5c2..38466f4 100755 +--- a/kpatch-build/kpatch-build ++++ b/kpatch-build/kpatch-build +@@ -343,7 +343,7 @@ else + url="http://us.archive.ubuntu.com/ubuntu/pool/main/l/linux" + extension="bz2" + sublevel="SUBLEVEL = 0" +- taroptions="xvjf" ++ troptions="xvjf" + + elif [[ $DISTRO = debian ]]; then + +@@ -478,6 +478,9 @@ cd "$SRCDIR" + make prepare >> "$LOGFILE" 2>&1 || die + cd "$TEMPDIR/output" + ld -r -o ../patch/output.o $FILES >> "$LOGFILE" 2>&1 || die ++md5sum ../patch/output.o | awk '{printf "%s\0", $1}' > checksum.tmp || die ++objcopy --add-section .kpatch.checksum=checksum.tmp --set-section-flags .kpatch.checksum=alloc,load,contents,readonly ../patch/output.o || die ++rm -f checksum.tmp + cd "$TEMPDIR/patch" + KPATCH_BUILD="$SRCDIR" KPATCH_NAME="$PATCHNAME" KBUILD_EXTRA_SYMBOLS="$SYMVERSFILE" make "O=$OBJDIR" >> "$LOGFILE" 2>&1 || die + +diff --git a/kpatch/kpatch b/kpatch/kpatch +index 0b932a6..0e36dda 100755 +--- a/kpatch/kpatch ++++ b/kpatch/kpatch +@@ -121,6 +121,21 @@ core_module_loaded () { + grep -q "T kpatch_register" /proc/kallsyms + } + ++get_module_name () { ++ echo $(readelf -p .gnu.linkonce.this_module $1 | grep '\[.*\]' | awk '{print $3}') ++} ++ ++verify_module_checksum () { ++ modname=$(get_module_name $1) ++ [[ -z $modname ]] && return 1 ++ ++ checksum=$(readelf -p .kpatch.checksum $1 | grep '\[.*\]' | awk '{print $3}') ++ [[ -z $checksum ]] && return 1 ++ ++ sysfs_checksum=$(cat /sys/kernel/kpatch/patches/${modname}/checksum) ++ [[ $checksum == $sysfs_checksum ]] || return 1 ++} ++ + load_module () { + if ! core_module_loaded; then + if modprobe -q kpatch; then +@@ -131,6 +146,23 @@ load_module () { + insmod "$COREMOD" || die "failed to load core module" + fi + fi ++ ++ modname=$(get_module_name $1) ++ moddir=/sys/kernel/kpatch/patches/$modname ++ if [[ -d $moddir ]] ; then ++ if [[ $(cat "${moddir}/enabled") -eq 0 ]]; then ++ if verify_module_checksum $1; then # same checksum ++ echo "module already loaded, re-enabling" ++ echo 1 > ${moddir}/enabled || die "failed to re-enable module $modname" ++ return ++ else ++ die "error: cannot re-enable patch module $modname, cannot verify checksum match" ++ fi ++ else ++ die "error: module named $modname already loaded and enabled" ++ fi ++ fi ++ + echo "loading patch module: $1" + insmod "$1" "$2" + } +-- +1.9.3 + diff --git a/SPECS/kpatch.spec b/SPECS/kpatch.spec index 130cad7..0b9c93a 100644 --- a/SPECS/kpatch.spec +++ b/SPECS/kpatch.spec @@ -1,15 +1,18 @@ Name: kpatch Version: 0.1.10 -Release: 1%{?dist} +Release: 3%{?dist} Summary: Dynamic kernel patch manager Group: System Environment/Kernel License: GPLv2 URL: https://github.com/dynup/kpatch Source0: https://github.com/dynup/kpatch/archive/v%{version}.tar.gz +Patch0: 0002-Support-modprobe-style-names-in-kpatch-load.patch +Patch1: 0003-Support-modprobe-format-names-in-uninstall.patch +Patch2: 0003-re-enable-patch-modules-with-checksum-matching.patch #BuildRequires: -Requires: bash kmod dracut +Requires: bash kmod dracut binutils BuildArch: noarch @@ -22,6 +25,9 @@ patch the kernel without rebooting. %prep %setup -q +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 %build @@ -43,6 +49,12 @@ rm -f %{buildroot}/usr/share/man/man1/kpatch-build.1.gz %changelog +* Tue Sep 16 2014 Seth Jennings 0.1.10-3 +- support re-enabling forced modules + +* Thu Sep 11 2014 Seth Jennings 0.1.10-2 +- support modprobe format names + * Thu Jul 31 2014 Josh Poimboeuf 0.1.10-1 - update to kpatch 0.1.10