Add related code which make libcareplus can run basic demo on aarch64. Signed-off-by: Jiajie Li <lijiajie11@huawei.com>
1634 lines
39 KiB
Diff
1634 lines
39 KiB
Diff
From 03cbe62f9376144991f5593c89500174b86341e7 Mon Sep 17 00:00:00 2001
|
|
From: Roman Rashchupkin <rrashchupkin@cloudlinux.com>
|
|
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 <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <ctype.h>
|
|
+#include <unistd.h>
|
|
+#include <errno.h>
|
|
+#include <string.h>
|
|
+#include <sys/fcntl.h>
|
|
+
|
|
+#include <gelf.h>
|
|
+#include <libunwind.h>
|
|
+#include <libunwind-ptrace.h>
|
|
+
|
|
+#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 <dirent.h>
|
|
#include <regex.h>
|
|
#include <sys/fcntl.h>
|
|
-#include <sys/mman.h>
|
|
-#include <sys/vfs.h>
|
|
-#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/wait.h>
|
|
@@ -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
|
|
|