From 6b68c43d4590df2ec8faa840b2f7a8e0983f089c Mon Sep 17 00:00:00 2001 From: Roman Rashchupkin Date: Wed, 14 Feb 2018 17:52:53 +0300 Subject: [PATCH 1/2] Create kpatch only for specified binaries --- scripts/pkgbuild | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/pkgbuild b/scripts/pkgbuild index f91af57..ff2dd78 100755 --- a/scripts/pkgbuild +++ b/scripts/pkgbuild @@ -340,9 +340,13 @@ kp_build() { kp_gen_kpatch() { echo " generating kpatches" - pushd $HOME/root.patched + if test -n $KP_PATCHED_BINARIES; then + targets=$KP_PATCHED_BINARIES + else + pushd $HOME/root.patched targets=$(find . -perm /0111 -type f) - popd + popd + fi rm -rf $HOME/${KP_PROJECT_PATCH%.*} mkdir $HOME/${KP_PROJECT_PATCH%.*} From 21459c884ee77c5f7df1bea4a1ed844d7e2c0b88 Mon Sep 17 00:00:00 2001 From: Roman Rashchupkin Date: Tue, 13 Feb 2018 11:32:05 +0300 Subject: [PATCH 2/2] Store in patch the env offset for coroutine search --- scripts/pkgbuild | 13 ++++- src/kpatch_coro.c | 123 ++++++++++++++++++++++++++++++---------------- src/kpatch_file.h | 3 +- src/kpatch_make.c | 16 ++++-- 4 files changed, 107 insertions(+), 48 deletions(-) diff --git a/scripts/pkgbuild b/scripts/pkgbuild index ff2dd78..6c8fae0 100755 --- a/scripts/pkgbuild +++ b/scripts/pkgbuild @@ -369,6 +369,11 @@ kp_gen_kpatch() { eu-unstrip "$HOME/root.original/$t" "$debug" + local coroutine_arg="" + if test -n "$COROUTINE_ENV_OFFSET"; then + coroutine_arg="-c $COROUTINE_ENV_OFFSET" + fi + $KPATCH_PATH/kpatch_strip --strip $patched $patched.kpstripped cp $patched.kpstripped $patched.relfixup $KPATCH_PATH/kpatch_strip --rel-fixup $debug $patched.relfixup @@ -376,7 +381,8 @@ kp_gen_kpatch() { /usr/bin/strip --strip-unneeded $patched.stripped cp $patched.stripped $patched.undolink $KPATCH_PATH/kpatch_strip --undo-link $debug $patched.undolink - $KPATCH_PATH/kpatch_make -b "$buildid" $patched.undolink -o $patched.kpatch + $KPATCH_PATH/kpatch_make -b "$buildid" $coroutine_arg \ + $patched.undolink -o $patched.kpatch cp $patched.kpatch $HOME/${KP_PROJECT_PATCH%.*}/$buildid.kpatch no_patches=0 done @@ -451,6 +457,10 @@ kp_patch_test() { exit 1 } +kp_calc_coroutine_env_offset() { + unset COROUTINE_ENV_OFFSET +} + main() { echo "Starting at "`date +%T`"..." @@ -473,6 +483,7 @@ main() { kp_build_hook kp_build kp_sanity_check + kp_calc_coroutine_env_offset kp_gen_kpatch kp_pack_patch elif test "$ACTION" == "test"; then diff --git a/src/kpatch_coro.c b/src/kpatch_coro.c index 45d4a0b..5cfb303 100644 --- a/src/kpatch_coro.c +++ b/src/kpatch_coro.c @@ -5,6 +5,7 @@ #include #include +#include #include @@ -115,8 +116,6 @@ kpatch_coro_free(struct kpatch_coro *c) #define STACK_OFFSET_COROUTINE_UCONTEXT (7 * sizeof(long)) #define STACK_OFFSET_COROUTINE (8 * sizeof(long)) -#define UCONTEXT_OFFSET_JMPBUF 0x38 - #define UCONTEXT_OFFSET_UC_STACK_SS_SP offsetof(ucontext_t, uc_stack.ss_sp) #define UCONTEXT_OFFSET_UC_STACK_SS_SIZE offsetof(ucontext_t, uc_stack.ss_size) @@ -235,48 +234,29 @@ int is_centos7_qemu(struct kpatch_process *proc) return 1; }; - -static int qemu_centos7_find_coroutines(struct kpatch_process *proc) +static int get_patch_coroutine_env_offset(struct kpatch_process *proc) { - struct object_file *oheap, *tcmalloc; - struct process_mem_iter *iter; - struct kpatch_coro *coro; - struct vm_area heap; - unsigned long __start_context, ptr_guard, cur; - int ret; - - if (!is_test_target(proc, "fail_coro") && !is_centos7_qemu(proc)) - return CORO_SEARCH_NEXT; - - kpdebug("Looking for coroutines in QEMU %d...\n", proc->pid); - oheap = kpatch_process_get_obj_by_regex(proc, "^\\[heap\\]$"); - tcmalloc = kpatch_process_get_obj_by_regex(proc, "^libtcmalloc.*\\.so\\.4.*"); - if (!oheap) { - kpdebug("FAIL. Can't find [heap](%p)\n", oheap); - return -1; - } + struct object_file *o; + list_for_each_entry(o, &proc->objs, list) { + if (!o->name || strcmp(proc->comm, o->name)) + continue; - /* NOTE(pboldin) We accurately craft stack for test so we - * don't need tcmalloc installed and used - */ - if (!tcmalloc && !is_test_target(proc, "fail_coro")) { - kpdebug("FAIL. Can't find tcmalloc lib. Full [heap] scan is not " - "implemented yet"); - return -1; - } - heap = list_first_entry(&oheap->vma, struct obj_vm_area, list)->inmem; + if (o->kpfile.patch) + return o->kpfile.patch->coroutine_env_offset; - ret = locate_start_context_symbol(proc, &__start_context); - if (ret < 0) { - kpdebug("FAIL. Can't locate_start_context_symbol\n"); - return CORO_SEARCH_NEXT; + if (o->skpfile && o->skpfile->patch) + return o->skpfile->patch->coroutine_env_offset; } + return 0; +} - ret = get_ptr_guard(proc, &ptr_guard); - if (ret < 0) { - kpdebug("FAIL. Can't get_ptr_guard\n"); - return -1; - } +static int qemu_centos7_find_coroutines_vma(struct kpatch_process *proc, struct vm_area vma, + unsigned long __start_context, unsigned long ptr_guard, int patch_coroutine_env_offset) +{ + int ret; + struct process_mem_iter *iter; + unsigned long cur; + struct kpatch_coro *coro; iter = kpatch_process_mem_iter_init(proc); if (iter == NULL) { @@ -284,11 +264,10 @@ static int qemu_centos7_find_coroutines(struct kpatch_process *proc) return -1; } - for (cur = heap.start; cur < heap.end; cur += PAGE_SIZE) { + for (cur = vma.start; cur < vma.end; cur += PAGE_SIZE) { unsigned long val, val2; unsigned long ptr = cur + PAGE_SIZE; unsigned long *regs; - val = PEEK_ULONG(ptr - STACK_OFFSET_START_CONTEXT); if (val != __start_context) continue; @@ -308,7 +287,7 @@ static int qemu_centos7_find_coroutines(struct kpatch_process *proc) break; } ret = kpatch_process_mem_read(proc, - val + UCONTEXT_OFFSET_JMPBUF, + val + patch_coroutine_env_offset, &coro->env, sizeof(coro->env)); if (ret < 0) { @@ -321,8 +300,66 @@ static int qemu_centos7_find_coroutines(struct kpatch_process *proc) regs[JB_RSP] = PTR_DEMANGLE(regs[JB_RSP], ptr_guard); regs[JB_RIP] = PTR_DEMANGLE(regs[JB_RIP], ptr_guard); } - kpatch_process_mem_iter_free(iter); + return ret; +} + +static int qemu_centos7_find_coroutines(struct kpatch_process *proc) +{ + struct object_file *oanonymous, *oheap, *tcmalloc; + struct obj_vm_area *ovma; + struct vm_area heap; + unsigned long __start_context, ptr_guard; + int patch_coroutine_env_offset ; + int ret; + + if (!is_test_target(proc, "fail_coro") && !is_centos7_qemu(proc)) + return CORO_SEARCH_NEXT; + + kpdebug("Looking for coroutines in QEMU %d...\n", proc->pid); + tcmalloc = kpatch_process_get_obj_by_regex(proc, "^libtcmalloc.*\\.so\\.4.*"); + + /* NOTE(pboldin) We accurately craft stack for test so we + * don't need tcmalloc installed and used + */ + if (!tcmalloc && !is_test_target(proc, "fail_coro")) { + kpdebug("FAIL. Can't find tcmalloc lib. Full [heap] scan is not " + "implemented yet"); + return -1; + } + + ret = get_ptr_guard(proc, &ptr_guard); + if (ret < 0) { + kpdebug("FAIL. Can't get_ptr_guard\n"); + return -1; + } + + ret = locate_start_context_symbol(proc, &__start_context); + if (ret < 0) { + kpdebug("FAIL. Can't locate_start_context_symbol\n"); + return CORO_SEARCH_NEXT; + } + + patch_coroutine_env_offset = get_patch_coroutine_env_offset(proc); + if (patch_coroutine_env_offset <= 0) { + kpdebug("FAIL. No patch_coroutine_env_offset\n"); + return patch_coroutine_env_offset; + } + + oheap = kpatch_process_get_obj_by_regex(proc, "^\\[heap\\]$"); + if (!oheap) { + kpdebug("FAIL. Can't find [heap](%p)\n", oheap); + return -1; + } + heap = list_first_entry(&oheap->vma, struct obj_vm_area, list)->inmem; + qemu_centos7_find_coroutines_vma(proc, heap, __start_context, ptr_guard, patch_coroutine_env_offset); + + oanonymous = kpatch_process_get_obj_by_regex(proc, "\\[anonymous\\]$"); + list_for_each_entry(ovma, &oanonymous->vma, list) { + if (!(ovma->inmem.prot & (PROT_READ|PROT_WRITE))) + continue; + qemu_centos7_find_coroutines_vma(proc, ovma->inmem, __start_context, ptr_guard, patch_coroutine_env_offset); + } return ret; } diff --git a/src/kpatch_file.h b/src/kpatch_file.h index 0b314e3..0d16277 100644 --- a/src/kpatch_file.h +++ b/src/kpatch_file.h @@ -152,7 +152,8 @@ struct kpatch_file { }; char srcversion[25]; /* srcversion of module or zeros */ - char pad2[231]; + uint32_t coroutine_env_offset; + char pad2[227]; /* relocations */ /* content */ diff --git a/src/kpatch_make.c b/src/kpatch_make.c index c584b06..e698781 100644 --- a/src/kpatch_make.c +++ b/src/kpatch_make.c @@ -26,7 +26,7 @@ static void xerror(const char *fmt, ...) exit(1); } -int make_file(int fdo, void *buf1, off_t size, const char *buildid) +int make_file(int fdo, void *buf1, off_t size, const char *buildid, int coroutine_env_offset) { int res; struct kpatch_file khdr; @@ -44,6 +44,8 @@ int make_file(int fdo, void *buf1, off_t size, const char *buildid) size = ALIGN(size, 16); khdr.total_size = khdr.kpatch_offset + size; + khdr.coroutine_env_offset = coroutine_env_offset; + res = write(fdo, &khdr, sizeof(khdr)); res += write(fdo, buf1, size); @@ -57,6 +59,7 @@ static void usage(void) { printf("Usage: kpatch_make [-d] -n [-v ] -e [-o ] [input2]\n"); printf(" -b buildid = target buildid for patch\n"); + printf(" -c offset = env offset in coroutine\n"); printf(" -d debug (verbose)\n"); printf("\n"); printf(" result is printed to output and is the following:\n"); @@ -72,8 +75,9 @@ int main(int argc, char **argv) void *buf; struct stat st; char *buildid = NULL, *outputname = NULL; + int coroutine_env_offset = -1; - while ((opt = getopt(argc, argv, "db:o:v:s:")) != -1) { + while ((opt = getopt(argc, argv, "db:o:v:s:c:")) != -1) { switch (opt) { case 'd': verbose = 1; @@ -84,6 +88,12 @@ int main(int argc, char **argv) case 'o': outputname = strdup(optarg); break; + case 'c': + if (sscanf(optarg, "%d", &coroutine_env_offset) != 1) { + printf("Error: can't parse jmpbuf offset for coroutines.\n"); + usage(); + } + break; default: /* '?' */ usage(); } @@ -109,5 +119,5 @@ int main(int argc, char **argv) xerror("Can't open output file '%s'", outputname); } - return make_file(fdo, buf, st.st_size, buildid); + return make_file(fdo, buf, st.st_size, buildid, coroutine_env_offset); }