From 03cbe62f9376144991f5593c89500174b86341e7 Mon Sep 17 00:00:00 2001 From: Roman Rashchupkin Date: Wed, 17 Jan 2018 13:45:34 +0300 Subject: [PATCH 02/89] Split kpatch_patch.c from kpatch_user.c --- .gitignore | 1 + src/Makefile | 2 +- src/kpatch_patch.c | 761 +++++++++++++++++++++++++++++++++++++++++++++ src/kpatch_patch.h | 28 ++ src/kpatch_user.c | 757 +------------------------------------------- 5 files changed, 792 insertions(+), 757 deletions(-) create mode 100644 .gitignore create mode 100644 src/kpatch_patch.c create mode 100644 src/kpatch_patch.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5761abc --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/src/Makefile b/src/Makefile index c008535..cd766e1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -35,7 +35,7 @@ kpatch_make: kpatch_make.o LIBUNWIND_LIBS := $(shell pkg-config --libs libunwind libunwind-ptrace) -libcare-ctl: kpatch_user.o kpatch_storage.o kpatch_elf.o kpatch_ptrace.o kpatch_coro.o +libcare-ctl: kpatch_user.o kpatch_storage.o kpatch_patch.c kpatch_elf.o kpatch_ptrace.o kpatch_coro.o libcare-ctl: kpatch_process.o kpatch_common.o rbtree.o kpatch_log.o libcare-ctl: LDLIBS += -lelf -lrt $(LIBUNWIND_LIBS) diff --git a/src/kpatch_patch.c b/src/kpatch_patch.c new file mode 100644 index 0000000..e32c702 --- /dev/null +++ b/src/kpatch_patch.c @@ -0,0 +1,761 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kpatch_patch.h" +#include "kpatch_user.h" +#include "kpatch_storage.h" +#include "kpatch_process.h" +#include "kpatch_file.h" +#include "kpatch_common.h" +#include "kpatch_elf.h" +#include "kpatch_ptrace.h" +#include "list.h" +#include "kpatch_log.h" + + +static inline int +is_addr_in_info(unsigned long addr, + struct kpatch_info *info, + int direction) +{ +#define IS_ADDR_IN_HALF_INTERVAL(addr, start, len) ((addr >= start) && (addr < start + len)) + if (direction == ACTION_APPLY_PATCH) + return IS_ADDR_IN_HALF_INTERVAL(addr, info->daddr, info->dlen); + if (direction == ACTION_UNAPPLY_PATCH) + return IS_ADDR_IN_HALF_INTERVAL(addr, info->saddr, info->slen); + return 0; +} + +static void print_address_closest_func(int log_level, struct object_file *o, unw_cursor_t *cur, int in_oldpatch) +{ + unsigned long address, offset; + char fname[128]; + + unw_get_reg(cur, UNW_REG_IP, &address); + + if (in_oldpatch) + if (address >= o->kpta && address < o->kpta + o->kpfile.size) { + kplog(log_level, "\t[0x%lx](patch)\n", address); + return; + } + + if (!unw_get_proc_name(cur, fname, 128, &offset)) + kplog(log_level, "\t[0x%lx] %s+0x%lx\n", address, fname, offset); + else + kplog(log_level, "\t[0x%lx]\n", address); +} + +/** + * Verify that the function from file `o' is safe to be patched. + * + * If retip is given then the safe address is returned in it. + * What is considered a safe address depends on the `paranoid' value. When it + * is true, safe address is the upper of ALL functions that do have a patch. + * When it is false, safe address is the address of the first function + * instruction that have no patch. + * + * That is, for the call chain from left to right with functions that have + * patch marked with '+': + * + * foo -> bar+ -> baz -> qux+ + * + * With `paranoid=true' this function will return address of the `bar+' + * instruction being executed with *retip pointing to the `foo' instruction + * that comes after the call to `bar+'. With `paranoid=false' this function + * will return address of the `qux+' instruction being executed with *retip + * pointing to the `baz' instruction that comes after call to `qux+'. + */ +static unsigned long +object_patch_verify_safety_single(struct object_file *o, + unw_cursor_t *cur, + unsigned long *retip, + int paranoid, + int direction) +{ + unw_word_t ip; + struct kpatch_info *info = o->info; + size_t i, ninfo = o->ninfo; + int prev = 0, rv; + unsigned long last = 0; + + if (direction != ACTION_APPLY_PATCH && + direction != ACTION_UNAPPLY_PATCH) + kpfatal("unknown direction"); + + do { + print_address_closest_func(LOG_INFO, o, cur, direction == ACTION_UNAPPLY_PATCH); + + unw_get_reg(cur, UNW_REG_IP, &ip); + + for (i = 0; i < ninfo; i++) { + if (is_new_func(&info[i])) + continue; + + if (is_addr_in_info((long)ip, &info[i], direction)) { + if (direction == ACTION_APPLY_PATCH) + last = info[i].daddr; + else if (direction == ACTION_UNAPPLY_PATCH) + last = info[i].saddr; + prev = 1; + break; + } + } + + if (prev && i == ninfo) { + prev = 0; + if (retip) + *retip = ip; + if (!paranoid) + break; + } + + rv = unw_step(cur); + } while (rv > 0); + + if (rv < 0) + kperr("unw_step = %d\n", rv); + + return last; +} + +#define KPATCH_CORO_STACK_UNSAFE (1 << 20) + +static int +patch_verify_safety(struct object_file *o, + unsigned long *retips, + int direction) +{ + size_t nr = 0, failed = 0, count = 0; + struct kpatch_ptrace_ctx *p; + struct kpatch_coro *c; + unsigned long retip, ret; + unw_cursor_t cur; + + list_for_each_entry(c, &o->proc->coro.coros, list) { + void *ucoro; + + kpdebug("Verifying safety for coroutine %zd...\n", count); + kpinfo("Stacktrace to verify safety for coroutine %zd:\n", count); + ucoro = _UCORO_create(c, proc2pctx(o->proc)->pid); + if (!ucoro) { + kplogerror("can't create unwind coro context\n"); + return -1; + } + + ret = unw_init_remote(&cur, o->proc->coro.unwd, ucoro); + if (ret) { + kplogerror("can't create unwind remote context\n"); + _UCORO_destroy(ucoro); + return -1; + } + + ret = object_patch_verify_safety_single(o, &cur, NULL, 0, direction); + _UCORO_destroy(ucoro); + if (ret) { + kperr("safety check failed to %lx\n", ret); + failed++; + } else { + kpdebug("OK\n"); + } + count++; + } + if (failed) + return failed | KPATCH_CORO_STACK_UNSAFE; + + list_for_each_entry(p, &o->proc->ptrace.pctxs, list) { + void *upt; + + kpdebug("Verifying safety for pid %d...\n", p->pid); + kpinfo("Stacktrace to verify safety for pid %d:\n", p->pid); + upt = _UPT_create(p->pid); + if (!upt) { + kplogerror("can't create unwind ptrace context\n"); + return -1; + } + + ret = unw_init_remote(&cur, o->proc->ptrace.unwd, upt); + if (ret) { + kplogerror("can't create unwind remote context\n"); + _UPT_destroy(upt); + return -1; + } + + ret = object_patch_verify_safety_single(o, &cur, &retip, 0, direction); + _UPT_destroy(upt); + if (ret) { + /* TODO: dump full backtrace, with symbols where possible (shared libs) */ + if (retips) { + kperr("safety check failed for %lx, will continue until %lx\n", + ret, retip); + retips[nr] = retip; + } else { + kperr("safety check failed for %lx\n", ret); + errno = -EBUSY; + } + failed++; + } + kpdebug("OK\n"); + nr++; + } + + return failed; +} + +/* + * Ensure that it is safe to apply/unapply patch for the object file `o`. + * + * First, we verify the safety of the patch. + * + * It is safe to apply patch (ACTION_APPLY_PATCH) when no threads or coroutines + * are executing the functions to be patched. + * + * It is safe to unapply patch (ACTION_UNAPPLY_PATCH) when no threads or + * coroutines are executing the patched functions. + * + * If it is not safe to do the action we continue threads execution until they + * are out of the functions that we want to patch/unpatch. This is done using + * `kpatch_ptrace_execute_until` function with default timeout of 3000 seconds + * and checking for action safety again. + */ +static int +patch_ensure_safety(struct object_file *o, + int action) +{ + struct kpatch_ptrace_ctx *p; + unsigned long ret, *retips; + size_t nr = 0, i; + + list_for_each_entry(p, &o->proc->ptrace.pctxs, list) + nr++; + retips = malloc(nr * sizeof(unsigned long)); + if (retips == NULL) + return -1; + + memset(retips, 0, nr * sizeof(unsigned long)); + + ret = patch_verify_safety(o, retips, action); + /* + * For coroutines we can't "execute until" + */ + if (ret && !(ret & KPATCH_CORO_STACK_UNSAFE)) { + i = 0; + list_for_each_entry(p, &o->proc->ptrace.pctxs, list) { + p->execute_until = retips[i]; + i++; + } + + ret = kpatch_ptrace_execute_until(o->proc, 3000, 0); + + /* OK, at this point we may have new threads, discover them */ + if (ret == 0) + ret = kpatch_process_attach(o->proc); + if (ret == 0) + ret = patch_verify_safety(o, NULL, action); + } + + free(retips); + + return ret ? -1 : 0; +} + +/***************************************************************************** + * Patch application subroutines + ****************************************************************************/ +/* + * This flag is local, i.e. it is never stored to the + * patch applied to patient's memory. + */ +#define PATCH_APPLIED (1 << 31) + +#define HUNK_SIZE 5 + +static int +patch_apply_hunk(struct object_file *o, size_t nhunk) +{ + int ret; + char code[HUNK_SIZE] = {0xe9, 0x00, 0x00, 0x00, 0x00}; /* jmp IMM */ + struct kpatch_info *info = &o->info[nhunk]; + unsigned long pundo; + + if (is_new_func(info)) + return 0; + + pundo = o->kpta + o->kpfile.patch->user_undo + nhunk * HUNK_SIZE; + kpinfo("%s origcode from 0x%lx+0x%x to 0x%lx\n", + o->name, info->daddr, HUNK_SIZE, pundo); + ret = kpatch_process_memcpy(o->proc, pundo, + info->daddr, HUNK_SIZE); + if (ret < 0) + return ret; + + kpinfo("%s hunk 0x%lx+0x%x -> 0x%lx+0x%x\n", + o->name, info->daddr, info->dlen, info->saddr, info->slen); + *(unsigned int *)(code + 1) = (unsigned int)(info->saddr - info->daddr - 5); + ret = kpatch_process_mem_write(o->proc, + code, + info->daddr, + sizeof(code)); + /* + * NOTE(pboldin): This is only stored locally, as information have + * been copied to patient's memory already. + */ + info->flags |= PATCH_APPLIED; + return ret ? -1 : 0; +} + +static int +duplicate_kp_file(struct object_file *o) +{ + struct kpatch_file *patch; + + patch = malloc(o->skpfile->size); + if (patch == NULL) + return -1; + + memcpy(patch, o->skpfile->patch, o->skpfile->size); + o->kpfile.patch = patch; + o->kpfile.size = o->skpfile->size; + + return 0; +} + +static int +object_apply_patch(struct object_file *o) +{ + struct kpatch_file *kp; + size_t sz, i; + int undef, ret; + + if (o->skpfile == NULL || o->is_patch) + return 0; + + if (o->applied_patch) { + kpinfo("Object '%s' already have a patch, not patching\n", + o->name); + return 0; + } + + ret = duplicate_kp_file(o); + if (ret < 0) { + kplogerror("can't duplicate kp_file\n"); + return -1; + } + + ret = kpatch_elf_load_kpatch_info(o); + if (ret < 0) + return ret; + + kp = o->kpfile.patch; + + sz = ROUND_UP(kp->total_size, 8); + undef = kpatch_count_undefined(o); + if (undef) { + o->jmp_table = kpatch_new_jmp_table(undef); + kp->jmp_offset = sz; + kpinfo("Jump table %d bytes for %d syms at offset 0x%x\n", + o->jmp_table->size, undef, kp->jmp_offset); + sz = ROUND_UP(sz + o->jmp_table->size, 128); + } + + kp->user_info = (unsigned long)o->info - + (unsigned long)o->kpfile.patch; + kp->user_undo = sz; + sz = ROUND_UP(sz + HUNK_SIZE * o->ninfo, 16); + + sz = ROUND_UP(sz, 4096); + + /* + * Map patch as close to the original code as possible. + * Otherwise we can't use 32-bit jumps. + */ + ret = kpatch_object_allocate_patch(o, sz); + if (ret < 0) + return ret; + ret = kpatch_resolve(o); + if (ret < 0) + return ret; + ret = kpatch_relocate(o); + if (ret < 0) + return ret; + ret = kpatch_process_mem_write(o->proc, + kp, + o->kpta, + kp->total_size); + if (ret < 0) + return -1; + if (o->jmp_table) { + ret = kpatch_process_mem_write(o->proc, + o->jmp_table, + o->kpta + kp->jmp_offset, + o->jmp_table->size); + if (ret < 0) + return ret; + } + + ret = patch_ensure_safety(o, ACTION_APPLY_PATCH); + if (ret < 0) + return ret; + + for (i = 0; i < o->ninfo; i++) { + ret = patch_apply_hunk(o, i); + if (ret < 0) + return ret; + } + + return 1; +} + +static int +object_unapply_patch(struct object_file *o, int check_flag); + +static int +object_unapply_old_patch(struct object_file *o) +{ + struct kpatch_file *kpatch_applied, *kpatch_storage; + int ret; + + if (o->skpfile == NULL || o->is_patch || o->applied_patch == NULL) + return 0; + + kpatch_applied = o->applied_patch->kpfile.patch; + kpatch_storage = o->skpfile->patch; + + if (kpatch_applied->user_level >= kpatch_storage->user_level) { + kpinfo("'%s' applied patch level is %d (storage has %d\n)\n", + o->name, + kpatch_applied->user_level, + kpatch_storage->user_level); + return 1; + } + + printf("%s: replacing patch level %d with level %d\n", + o->name, + kpatch_applied->user_level, + kpatch_storage->user_level); + ret = object_unapply_patch(o, /* check_flag */ 0); + if (ret < 0) + kperr("can't unapply patch for %s\n", o->name); + else { + /* TODO(pboldin): handle joining the holes here */ + o->applied_patch = NULL; + o->info = NULL; + o->ninfo = 0; + } + + return ret; +} + +static int +kpatch_apply_patches(kpatch_process_t *proc) +{ + struct object_file *o; + int applied = 0, ret; + + list_for_each_entry(o, &proc->objs, list) { + + ret = object_unapply_old_patch(o); + if (ret < 0) + break; + + ret = object_apply_patch(o); + if (ret < 0) + goto unpatch; + if (ret) + applied++; + } + return applied; + +unpatch: + kperr("Patching %s failed, unapplying partially applied patch\n", o->name); + /* + * TODO(pboldin): close the holes so the state is the same + * after unpatch + */ + ret = object_unapply_patch(o, /* check_flag */ 1); + if (ret < 0) { + kperr("Can't unapply patch for %s\n", o->name); + } + return -1; +} + +int process_patch(int pid, void *_data) +{ + int ret; + kpatch_process_t _proc, *proc = &_proc; + struct patch_data *data = _data; + + kpatch_storage_t *storage = data->storage; + int is_just_started = data->is_just_started; + int send_fd = data->send_fd; + + ret = kpatch_process_init(proc, pid, is_just_started, send_fd); + if (ret < 0) { + kperr("cannot init process %d\n", pid); + goto out; + } + + kpatch_process_print_short(proc); + + ret = kpatch_process_mem_open(proc, MEM_READ); + if (ret < 0) + goto out_free; + + /* + * In case the process was just started we continue execution up to the + * entry point of a program just to allow ld.so to load up libraries + */ + ret = kpatch_process_load_libraries(proc); + if (ret < 0) + goto out_free; + + /* + * In case we got there from startup send_fd != -1. + */ + ret = kpatch_process_kick_send_fd(proc); + if (ret < 0) + goto out_free; + + /* + * For each object file that we want to patch (either binary itself or + * shared library) we need its ELF structure to perform relocations. + * Because we know uniq BuildID of the object the section addresses + * stored in the patch are valid for the original object. + */ + ret = kpatch_process_map_object_files(proc); + if (ret < 0) + goto out_free; + + /* + * Lookup for patches appicable for proc in storage. + */ + ret = storage_lookup_patches(storage, proc); + if (ret <= 0) + goto out_free; + + /* Finally, attach to process */ + ret = kpatch_process_attach(proc); + if (ret < 0) + goto out_free; + + ret = kpatch_coroutines_find(proc); + if (ret < 0) + goto out_free; + + ret = storage_execute_before_script(storage, proc); + if (ret < 0) + goto out_free; + + ret = kpatch_apply_patches(proc); + + if (storage_execute_after_script(storage, proc) < 0) + kperr("after script failed\n"); + + +out_free: + kpatch_process_free(proc); + +out: + if (ret < 0) { + printf("Failed to apply patch '%s'\n", storage->path); + kperr("Failed to apply patch '%s'\n", storage->path); + } else if (ret == 0) + printf("No patch(es) applicable to PID '%d' have been found\n", pid); + else { + printf("%d patch hunk(s) have been successfully applied to PID '%d'\n", ret, pid); + ret = 0; + } + + return ret; +} + + +/***************************************************************************** + * Patch cancellcation subroutines and cmd_unpatch_user + ****************************************************************************/ +static int +object_find_applied_patch_info(struct object_file *o) +{ + struct kpatch_info tmpinfo; + struct kpatch_info *remote_info; + size_t nalloc = 0; + struct process_mem_iter *iter; + int ret; + + if (o->info != NULL) + return 0; + + iter = kpatch_process_mem_iter_init(o->proc); + if (iter == NULL) + return -1; + + remote_info = (void *)o->kpta + o->kpfile.patch->user_info; + do { + ret = REMOTE_PEEK(iter, tmpinfo, remote_info); + if (ret < 0) + goto err; + + if (is_end_info(&tmpinfo)) + break; + + if (o->ninfo == nalloc) { + nalloc += 16; + o->info = realloc(o->info, nalloc * sizeof(tmpinfo)); + } + + o->info[o->ninfo] = tmpinfo; + + remote_info++; + o->ninfo++; + } while (1); + + o->applied_patch->info = o->info; + o->applied_patch->ninfo = o->ninfo; + +err: + kpatch_process_mem_iter_free(iter); + + return ret; +} + +static int +object_unapply_patch(struct object_file *o, int check_flag) +{ + int ret; + size_t i; + unsigned long orig_code_addr; + + ret = object_find_applied_patch_info(o); + if (ret < 0) + return ret; + + ret = patch_ensure_safety(o, ACTION_UNAPPLY_PATCH); + if (ret < 0) + return ret; + + orig_code_addr = o->kpta + o->kpfile.patch->user_undo; + + for (i = 0; i < o->ninfo; i++) { + if (is_new_func(&o->info[i])) + continue; + + if (check_flag && !(o->info[i].flags & PATCH_APPLIED)) + continue; + + ret = kpatch_process_memcpy(o->proc, + o->info[i].daddr, + orig_code_addr + i * HUNK_SIZE, + HUNK_SIZE); + /* XXX(pboldin) We are in deep trouble here, handle it + * by restoring the patch back */ + if (ret < 0) + return ret; + } + + ret = kpatch_munmap_remote(proc2pctx(o->proc), + o->kpta, + o->kpfile.size); + + return ret; +} + +static int +kpatch_should_unapply_patch(struct object_file *o, + char *buildids[], + int nbuildids) +{ + int i; + const char *bid; + + if (nbuildids == 0) + return 1; + + bid = kpatch_get_buildid(o); + + for (i = 0; i < nbuildids; i++) { + if (!strcmp(bid, buildids[i]) || + !strcmp(o->name, buildids[i])) + return 1; + } + + return 0; +} + +static int +kpatch_unapply_patches(kpatch_process_t *proc, + char *buildids[], + int nbuildids) +{ + struct object_file *o; + int ret; + size_t unapplied = 0; + + ret = kpatch_process_associate_patches(proc); + if (ret < 0) + return ret; + + list_for_each_entry(o, &proc->objs, list) { + if (o->applied_patch == NULL) + continue; + + if (!kpatch_should_unapply_patch(o, buildids, nbuildids)) + continue; + + ret = object_unapply_patch(o, /* check_flag */ 0); + if (ret < 0) + return ret; + unapplied++; + } + + return unapplied; +} + +int process_unpatch(int pid, void *_data) +{ + int ret; + kpatch_process_t _proc, *proc = &_proc; + struct unpatch_data *data = _data; + char **buildids = data->buildids; + int nbuildids = data->nbuildids; + + ret = kpatch_process_init(proc, pid, /* start */ 0, /* send_fd */ -1); + if (ret < 0) + return -1; + + kpatch_process_print_short(proc); + + ret = kpatch_process_attach(proc); + if (ret < 0) + goto out; + + ret = kpatch_process_map_object_files(proc); + if (ret < 0) + goto out; + + ret = kpatch_coroutines_find(proc); + if (ret < 0) + goto out; + + ret = kpatch_unapply_patches(proc, buildids, nbuildids); + +out: + kpatch_process_free(proc); + + if (ret < 0) + printf("Failed to cancel patches for %d\n", pid); + else if (ret == 0) + printf("No patch(es) cancellable from PID '%d' were found\n", pid); + else + printf("%d patch hunk(s) were successfully cancelled from PID '%d'\n", ret, pid); + + return ret; +} + diff --git a/src/kpatch_patch.h b/src/kpatch_patch.h new file mode 100644 index 0000000..44806ab --- /dev/null +++ b/src/kpatch_patch.h @@ -0,0 +1,28 @@ +#ifndef __KPATCH_PATCH__ +#define __KPATCH_PATCH__ + +#include "kpatch_common.h" +#include "kpatch_storage.h" +#include "kpatch_file.h" +#include "rbtree.h" + +enum { + ACTION_APPLY_PATCH, + ACTION_UNAPPLY_PATCH +}; + +struct patch_data { + kpatch_storage_t *storage; + int is_just_started; + int send_fd; +}; + +struct unpatch_data { + char **buildids; + int nbuildids; +}; + +int process_patch(int pid, void *_data); +int process_unpatch(int pid, void *_data); + +#endif diff --git a/src/kpatch_user.c b/src/kpatch_user.c index 672b4d4..9ab77b9 100644 --- a/src/kpatch_user.c +++ b/src/kpatch_user.c @@ -7,9 +7,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -20,11 +17,11 @@ #include "kpatch_user.h" #include "kpatch_storage.h" +#include "kpatch_patch.h" #include "kpatch_process.h" #include "kpatch_file.h" #include "kpatch_common.h" #include "kpatch_elf.h" -#include "kpatch_ptrace.h" #include "list.h" #include "kpatch_log.h" @@ -42,571 +39,6 @@ typedef int (callback_t)(int pid, void *data); static int processes_do(int pid, callback_t callback, void *data); -enum { - ACTION_APPLY_PATCH, - ACTION_UNAPPLY_PATCH -}; - -static inline int -is_addr_in_info(unsigned long addr, - struct kpatch_info *info, - int direction) -{ -#define IS_ADDR_IN_HALF_INTERVAL(addr, start, len) ((addr >= start) && (addr < start + len)) - if (direction == ACTION_APPLY_PATCH) - return IS_ADDR_IN_HALF_INTERVAL(addr, info->daddr, info->dlen); - if (direction == ACTION_UNAPPLY_PATCH) - return IS_ADDR_IN_HALF_INTERVAL(addr, info->saddr, info->slen); - return 0; -} - -static void print_address_closest_func(int log_level, struct object_file *o, unw_cursor_t *cur, int in_oldpatch) -{ - unsigned long address, offset; - char fname[128]; - - unw_get_reg(cur, UNW_REG_IP, &address); - - if (in_oldpatch) - if (address >= o->kpta && address < o->kpta + o->kpfile.size) { - kplog(log_level, "\t[0x%lx](patch)\n", address); - return; - } - - if (!unw_get_proc_name(cur, fname, 128, &offset)) - kplog(log_level, "\t[0x%lx] %s+0x%lx\n", address, fname, offset); - else - kplog(log_level, "\t[0x%lx]\n", address); -} - -/** - * Verify that the function from file `o' is safe to be patched. - * - * If retip is given then the safe address is returned in it. - * What is considered a safe address depends on the `paranoid' value. When it - * is true, safe address is the upper of ALL functions that do have a patch. - * When it is false, safe address is the address of the first function - * instruction that have no patch. - * - * That is, for the call chain from left to right with functions that have - * patch marked with '+': - * - * foo -> bar+ -> baz -> qux+ - * - * With `paranoid=true' this function will return address of the `bar+' - * instruction being executed with *retip pointing to the `foo' instruction - * that comes after the call to `bar+'. With `paranoid=false' this function - * will return address of the `qux+' instruction being executed with *retip - * pointing to the `baz' instruction that comes after call to `qux+'. - */ -static unsigned long -object_patch_verify_safety_single(struct object_file *o, - unw_cursor_t *cur, - unsigned long *retip, - int paranoid, - int direction) -{ - unw_word_t ip; - struct kpatch_info *info = o->info; - size_t i, ninfo = o->ninfo; - int prev = 0, rv; - unsigned long last = 0; - - if (direction != ACTION_APPLY_PATCH && - direction != ACTION_UNAPPLY_PATCH) - kpfatal("unknown direction"); - - do { - print_address_closest_func(LOG_INFO, o, cur, direction == ACTION_UNAPPLY_PATCH); - - unw_get_reg(cur, UNW_REG_IP, &ip); - - for (i = 0; i < ninfo; i++) { - if (is_new_func(&info[i])) - continue; - - if (is_addr_in_info((long)ip, &info[i], direction)) { - if (direction == ACTION_APPLY_PATCH) - last = info[i].daddr; - else if (direction == ACTION_UNAPPLY_PATCH) - last = info[i].saddr; - prev = 1; - break; - } - } - - if (prev && i == ninfo) { - prev = 0; - if (retip) - *retip = ip; - if (!paranoid) - break; - } - - rv = unw_step(cur); - } while (rv > 0); - - if (rv < 0) - kperr("unw_step = %d\n", rv); - - return last; -} - -#define KPATCH_CORO_STACK_UNSAFE (1 << 20) - -static int -patch_verify_safety(struct object_file *o, - unsigned long *retips, - int direction) -{ - size_t nr = 0, failed = 0, count = 0; - struct kpatch_ptrace_ctx *p; - struct kpatch_coro *c; - unsigned long retip, ret; - unw_cursor_t cur; - - list_for_each_entry(c, &o->proc->coro.coros, list) { - void *ucoro; - - kpdebug("Verifying safety for coroutine %zd...\n", count); - kpinfo("Stacktrace to verify safety for coroutine %zd:\n", count); - ucoro = _UCORO_create(c, proc2pctx(o->proc)->pid); - if (!ucoro) { - kplogerror("can't create unwind coro context\n"); - return -1; - } - - ret = unw_init_remote(&cur, o->proc->coro.unwd, ucoro); - if (ret) { - kplogerror("can't create unwind remote context\n"); - _UCORO_destroy(ucoro); - return -1; - } - - ret = object_patch_verify_safety_single(o, &cur, NULL, 0, direction); - _UCORO_destroy(ucoro); - if (ret) { - kperr("safety check failed to %lx\n", ret); - failed++; - } else { - kpdebug("OK\n"); - } - count++; - } - if (failed) - return failed | KPATCH_CORO_STACK_UNSAFE; - - list_for_each_entry(p, &o->proc->ptrace.pctxs, list) { - void *upt; - - kpdebug("Verifying safety for pid %d...\n", p->pid); - kpinfo("Stacktrace to verify safety for pid %d:\n", p->pid); - upt = _UPT_create(p->pid); - if (!upt) { - kplogerror("can't create unwind ptrace context\n"); - return -1; - } - - ret = unw_init_remote(&cur, o->proc->ptrace.unwd, upt); - if (ret) { - kplogerror("can't create unwind remote context\n"); - _UPT_destroy(upt); - return -1; - } - - ret = object_patch_verify_safety_single(o, &cur, &retip, 0, direction); - _UPT_destroy(upt); - if (ret) { - /* TODO: dump full backtrace, with symbols where possible (shared libs) */ - if (retips) { - kperr("safety check failed for %lx, will continue until %lx\n", - ret, retip); - retips[nr] = retip; - } else { - kperr("safety check failed for %lx\n", ret); - errno = -EBUSY; - } - failed++; - } - kpdebug("OK\n"); - nr++; - } - - return failed; -} - -/* - * Ensure that it is safe to apply/unapply patch for the object file `o`. - * - * First, we verify the safety of the patch. - * - * It is safe to apply patch (ACTION_APPLY_PATCH) when no threads or coroutines - * are executing the functions to be patched. - * - * It is safe to unapply patch (ACTION_UNAPPLY_PATCH) when no threads or - * coroutines are executing the patched functions. - * - * If it is not safe to do the action we continue threads execution until they - * are out of the functions that we want to patch/unpatch. This is done using - * `kpatch_ptrace_execute_until` function with default timeout of 3000 seconds - * and checking for action safety again. - */ -static int -patch_ensure_safety(struct object_file *o, - int action) -{ - struct kpatch_ptrace_ctx *p; - unsigned long ret, *retips; - size_t nr = 0, i; - - list_for_each_entry(p, &o->proc->ptrace.pctxs, list) - nr++; - retips = malloc(nr * sizeof(unsigned long)); - if (retips == NULL) - return -1; - - memset(retips, 0, nr * sizeof(unsigned long)); - - ret = patch_verify_safety(o, retips, action); - /* - * For coroutines we can't "execute until" - */ - if (ret && !(ret & KPATCH_CORO_STACK_UNSAFE)) { - i = 0; - list_for_each_entry(p, &o->proc->ptrace.pctxs, list) { - p->execute_until = retips[i]; - i++; - } - - ret = kpatch_ptrace_execute_until(o->proc, 3000, 0); - - /* OK, at this point we may have new threads, discover them */ - if (ret == 0) - ret = kpatch_process_attach(o->proc); - if (ret == 0) - ret = patch_verify_safety(o, NULL, action); - } - - free(retips); - - return ret ? -1 : 0; -} - -/***************************************************************************** - * Patch application subroutines and cmd_patch_user - ****************************************************************************/ -/* - * This flag is local, i.e. it is never stored to the - * patch applied to patient's memory. - */ -#define PATCH_APPLIED (1 << 31) - -#define HUNK_SIZE 5 - -static int -patch_apply_hunk(struct object_file *o, size_t nhunk) -{ - int ret; - char code[HUNK_SIZE] = {0xe9, 0x00, 0x00, 0x00, 0x00}; /* jmp IMM */ - struct kpatch_info *info = &o->info[nhunk]; - unsigned long pundo; - - if (is_new_func(info)) - return 0; - - pundo = o->kpta + o->kpfile.patch->user_undo + nhunk * HUNK_SIZE; - kpinfo("%s origcode from 0x%lx+0x%x to 0x%lx\n", - o->name, info->daddr, HUNK_SIZE, pundo); - ret = kpatch_process_memcpy(o->proc, pundo, - info->daddr, HUNK_SIZE); - if (ret < 0) - return ret; - - kpinfo("%s hunk 0x%lx+0x%x -> 0x%lx+0x%x\n", - o->name, info->daddr, info->dlen, info->saddr, info->slen); - *(unsigned int *)(code + 1) = (unsigned int)(info->saddr - info->daddr - 5); - ret = kpatch_process_mem_write(o->proc, - code, - info->daddr, - sizeof(code)); - /* - * NOTE(pboldin): This is only stored locally, as information have - * been copied to patient's memory already. - */ - info->flags |= PATCH_APPLIED; - return ret ? -1 : 0; -} - -static int -duplicate_kp_file(struct object_file *o) -{ - struct kpatch_file *patch; - - patch = malloc(o->skpfile->size); - if (patch == NULL) - return -1; - - memcpy(patch, o->skpfile->patch, o->skpfile->size); - o->kpfile.patch = patch; - o->kpfile.size = o->skpfile->size; - - return 0; -} - -static int -object_apply_patch(struct object_file *o) -{ - struct kpatch_file *kp; - size_t sz, i; - int undef, ret; - - if (o->skpfile == NULL || o->is_patch) - return 0; - - if (o->applied_patch) { - kpinfo("Object '%s' already have a patch, not patching\n", - o->name); - return 0; - } - - ret = duplicate_kp_file(o); - if (ret < 0) { - kplogerror("can't duplicate kp_file\n"); - return -1; - } - - ret = kpatch_elf_load_kpatch_info(o); - if (ret < 0) - return ret; - - kp = o->kpfile.patch; - - sz = ROUND_UP(kp->total_size, 8); - undef = kpatch_count_undefined(o); - if (undef) { - o->jmp_table = kpatch_new_jmp_table(undef); - kp->jmp_offset = sz; - kpinfo("Jump table %d bytes for %d syms at offset 0x%x\n", - o->jmp_table->size, undef, kp->jmp_offset); - sz = ROUND_UP(sz + o->jmp_table->size, 128); - } - - kp->user_info = (unsigned long)o->info - - (unsigned long)o->kpfile.patch; - kp->user_undo = sz; - sz = ROUND_UP(sz + HUNK_SIZE * o->ninfo, 16); - - sz = ROUND_UP(sz, 4096); - - /* - * Map patch as close to the original code as possible. - * Otherwise we can't use 32-bit jumps. - */ - ret = kpatch_object_allocate_patch(o, sz); - if (ret < 0) - return ret; - ret = kpatch_resolve(o); - if (ret < 0) - return ret; - ret = kpatch_relocate(o); - if (ret < 0) - return ret; - ret = kpatch_process_mem_write(o->proc, - kp, - o->kpta, - kp->total_size); - if (ret < 0) - return -1; - if (o->jmp_table) { - ret = kpatch_process_mem_write(o->proc, - o->jmp_table, - o->kpta + kp->jmp_offset, - o->jmp_table->size); - if (ret < 0) - return ret; - } - - ret = patch_ensure_safety(o, ACTION_APPLY_PATCH); - if (ret < 0) - return ret; - - for (i = 0; i < o->ninfo; i++) { - ret = patch_apply_hunk(o, i); - if (ret < 0) - return ret; - } - - return 1; -} - -static int -object_unapply_patch(struct object_file *o, int check_flag); - -static int -object_unapply_old_patch(struct object_file *o) -{ - struct kpatch_file *kpatch_applied, *kpatch_storage; - int ret; - - if (o->skpfile == NULL || o->is_patch || o->applied_patch == NULL) - return 0; - - kpatch_applied = o->applied_patch->kpfile.patch; - kpatch_storage = o->skpfile->patch; - - if (kpatch_applied->user_level >= kpatch_storage->user_level) { - kpinfo("'%s' applied patch level is %d (storage has %d\n)\n", - o->name, - kpatch_applied->user_level, - kpatch_storage->user_level); - return 1; - } - - printf("%s: replacing patch level %d with level %d\n", - o->name, - kpatch_applied->user_level, - kpatch_storage->user_level); - ret = object_unapply_patch(o, /* check_flag */ 0); - if (ret < 0) - kperr("can't unapply patch for %s\n", o->name); - else { - /* TODO(pboldin): handle joining the holes here */ - o->applied_patch = NULL; - o->info = NULL; - o->ninfo = 0; - } - - return ret; -} - -static int -kpatch_apply_patches(kpatch_process_t *proc) -{ - struct object_file *o; - int applied = 0, ret; - - list_for_each_entry(o, &proc->objs, list) { - - ret = object_unapply_old_patch(o); - if (ret < 0) - break; - - ret = object_apply_patch(o); - if (ret < 0) - goto unpatch; - if (ret) - applied++; - } - return applied; - -unpatch: - kperr("Patching %s failed, unapplying partially applied patch\n", o->name); - /* - * TODO(pboldin): close the holes so the state is the same - * after unpatch - */ - ret = object_unapply_patch(o, /* check_flag */ 1); - if (ret < 0) { - kperr("Can't unapply patch for %s\n", o->name); - } - return -1; -} - -struct patch_data { - kpatch_storage_t *storage; - int is_just_started; - int send_fd; -}; - -static int process_patch(int pid, void *_data) -{ - int ret; - kpatch_process_t _proc, *proc = &_proc; - struct patch_data *data = _data; - - kpatch_storage_t *storage = data->storage; - int is_just_started = data->is_just_started; - int send_fd = data->send_fd; - - ret = kpatch_process_init(proc, pid, is_just_started, send_fd); - if (ret < 0) { - kperr("cannot init process %d\n", pid); - goto out; - } - - kpatch_process_print_short(proc); - - ret = kpatch_process_mem_open(proc, MEM_READ); - if (ret < 0) - goto out_free; - - /* - * In case the process was just started we continue execution up to the - * entry point of a program just to allow ld.so to load up libraries - */ - ret = kpatch_process_load_libraries(proc); - if (ret < 0) - goto out_free; - - /* - * In case we got there from startup send_fd != -1. - */ - ret = kpatch_process_kick_send_fd(proc); - if (ret < 0) - goto out_free; - - /* - * For each object file that we want to patch (either binary itself or - * shared library) we need its ELF structure to perform relocations. - * Because we know uniq BuildID of the object the section addresses - * stored in the patch are valid for the original object. - */ - ret = kpatch_process_map_object_files(proc); - if (ret < 0) - goto out_free; - - /* - * Lookup for patches appicable for proc in storage. - */ - ret = storage_lookup_patches(storage, proc); - if (ret <= 0) - goto out_free; - - /* Finally, attach to process */ - ret = kpatch_process_attach(proc); - if (ret < 0) - goto out_free; - - ret = kpatch_coroutines_find(proc); - if (ret < 0) - goto out_free; - - ret = storage_execute_before_script(storage, proc); - if (ret < 0) - goto out_free; - - ret = kpatch_apply_patches(proc); - - if (storage_execute_after_script(storage, proc) < 0) - kperr("after script failed\n"); - - -out_free: - kpatch_process_free(proc); - -out: - if (ret < 0) { - printf("Failed to apply patch '%s'\n", storage->path); - kperr("Failed to apply patch '%s'\n", storage->path); - } else if (ret == 0) - printf("No patch(es) applicable to PID '%d' have been found\n", pid); - else { - printf("%d patch hunk(s) have been successfully applied to PID '%d'\n", ret, pid); - ret = 0; - } - - return ret; -} static int processes_patch(kpatch_storage_t *storage, @@ -690,193 +122,6 @@ out_err: return ret; } -/***************************************************************************** - * Patch cancellcation subroutines and cmd_unpatch_user - ****************************************************************************/ -static int -object_find_applied_patch_info(struct object_file *o) -{ - struct kpatch_info tmpinfo; - struct kpatch_info *remote_info; - size_t nalloc = 0; - struct process_mem_iter *iter; - int ret; - - if (o->info != NULL) - return 0; - - iter = kpatch_process_mem_iter_init(o->proc); - if (iter == NULL) - return -1; - - remote_info = (void *)o->kpta + o->kpfile.patch->user_info; - do { - ret = REMOTE_PEEK(iter, tmpinfo, remote_info); - if (ret < 0) - goto err; - - if (is_end_info(&tmpinfo)) - break; - - if (o->ninfo == nalloc) { - nalloc += 16; - o->info = realloc(o->info, nalloc * sizeof(tmpinfo)); - } - - o->info[o->ninfo] = tmpinfo; - - remote_info++; - o->ninfo++; - } while (1); - - o->applied_patch->info = o->info; - o->applied_patch->ninfo = o->ninfo; - -err: - kpatch_process_mem_iter_free(iter); - - return ret; -} - -static int -object_unapply_patch(struct object_file *o, int check_flag) -{ - int ret; - size_t i; - unsigned long orig_code_addr; - - ret = object_find_applied_patch_info(o); - if (ret < 0) - return ret; - - ret = patch_ensure_safety(o, ACTION_UNAPPLY_PATCH); - if (ret < 0) - return ret; - - orig_code_addr = o->kpta + o->kpfile.patch->user_undo; - - for (i = 0; i < o->ninfo; i++) { - if (is_new_func(&o->info[i])) - continue; - - if (check_flag && !(o->info[i].flags & PATCH_APPLIED)) - continue; - - ret = kpatch_process_memcpy(o->proc, - o->info[i].daddr, - orig_code_addr + i * HUNK_SIZE, - HUNK_SIZE); - /* XXX(pboldin) We are in deep trouble here, handle it - * by restoring the patch back */ - if (ret < 0) - return ret; - } - - ret = kpatch_munmap_remote(proc2pctx(o->proc), - o->kpta, - o->kpfile.size); - - return ret; -} - -static int -kpatch_should_unapply_patch(struct object_file *o, - char *buildids[], - int nbuildids) -{ - int i; - const char *bid; - - if (nbuildids == 0) - return 1; - - bid = kpatch_get_buildid(o); - - for (i = 0; i < nbuildids; i++) { - if (!strcmp(bid, buildids[i]) || - !strcmp(o->name, buildids[i])) - return 1; - } - - return 0; -} - -static int -kpatch_unapply_patches(kpatch_process_t *proc, - char *buildids[], - int nbuildids) -{ - struct object_file *o; - int ret; - size_t unapplied = 0; - - ret = kpatch_process_associate_patches(proc); - if (ret < 0) - return ret; - - list_for_each_entry(o, &proc->objs, list) { - if (o->applied_patch == NULL) - continue; - - if (!kpatch_should_unapply_patch(o, buildids, nbuildids)) - continue; - - ret = object_unapply_patch(o, /* check_flag */ 0); - if (ret < 0) - return ret; - unapplied++; - } - - return unapplied; -} - -struct unpatch_data { - char **buildids; - int nbuildids; -}; - -static int -process_unpatch(int pid, void *_data) -{ - int ret; - kpatch_process_t _proc, *proc = &_proc; - struct unpatch_data *data = _data; - char **buildids = data->buildids; - int nbuildids = data->nbuildids; - - ret = kpatch_process_init(proc, pid, /* start */ 0, /* send_fd */ -1); - if (ret < 0) - return -1; - - kpatch_process_print_short(proc); - - ret = kpatch_process_attach(proc); - if (ret < 0) - goto out; - - ret = kpatch_process_map_object_files(proc); - if (ret < 0) - goto out; - - ret = kpatch_coroutines_find(proc); - if (ret < 0) - goto out; - - ret = kpatch_unapply_patches(proc, buildids, nbuildids); - -out: - kpatch_process_free(proc); - - if (ret < 0) - printf("Failed to cancel patches for %d\n", pid); - else if (ret == 0) - printf("No patch(es) cancellable from PID '%d' were found\n", pid); - else - printf("%d patch hunk(s) were successfully cancelled from PID '%d'\n", ret, pid); - - return ret; -} - static int processes_unpatch(int pid, char *buildids[], int nbuildids) { -- 2.23.0