diff --git a/0001-Split-kpatch_storage.c-from-kpatch_user.c.patch b/0001-Split-kpatch_storage.c-from-kpatch_user.c.patch new file mode 100644 index 0000000..15d9868 --- /dev/null +++ b/0001-Split-kpatch_storage.c-from-kpatch_user.c.patch @@ -0,0 +1,1149 @@ +From 252f1bdb9a5ab1c1793e213f97389d79e55838b3 Mon Sep 17 00:00:00 2001 +From: Roman Rashchupkin +Date: Wed, 17 Jan 2018 13:34:50 +0300 +Subject: [PATCH 01/89] Split kpatch_storage.c from kpatch_user.c + +--- + src/Makefile | 4 +- + src/kpatch_storage.c | 488 +++++++++++++++++++++++++++++++++++++++++++ + src/kpatch_storage.h | 66 ++++++ + src/kpatch_user.c | 482 +----------------------------------------- + src/kpatch_user.h | 39 ---- + 5 files changed, 557 insertions(+), 522 deletions(-) + create mode 100644 src/kpatch_storage.c + create mode 100644 src/kpatch_storage.h + +diff --git a/src/Makefile b/src/Makefile +index 72ec073..c008535 100644 +--- a/src/Makefile ++++ b/src/Makefile +@@ -35,8 +35,8 @@ kpatch_make: kpatch_make.o + LIBUNWIND_LIBS := $(shell pkg-config --libs libunwind libunwind-ptrace) + + +-libcare-ctl: kpatch_user.o kpatch_elf.o kpatch_ptrace.o kpatch_coro.o rbtree.o kpatch_log.o +-libcare-ctl: kpatch_process.o kpatch_common.o ++libcare-ctl: kpatch_user.o kpatch_storage.o 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) + + libcare-client: libcare-client.o +diff --git a/src/kpatch_storage.c b/src/kpatch_storage.c +new file mode 100644 +index 0000000..a466460 +--- /dev/null ++++ b/src/kpatch_storage.c +@@ -0,0 +1,488 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "kpatch_storage.h" ++#include "kpatch_file.h" ++#include "kpatch_common.h" ++#include "kpatch_elf.h" ++#include "kpatch_ptrace.h" ++#include "list.h" ++#include "kpatch_log.h" ++ ++ ++/***************************************************************************** ++ * Patch storage subroutines. ++ ****************************************************************************/ ++ ++static int ++patch_file_verify(struct kp_file *kpfile) ++{ ++ GElf_Ehdr *hdr; ++ struct kpatch_file *k = kpfile->patch; ++ ssize_t size = kpfile->size; ++ ++ kpdebug("Verifying patch for '%s'...", k->modulename); ++ ++ if (memcmp(k->magic, KPATCH_FILE_MAGIC1, sizeof(k->magic))) { ++ kperr("'%s' patch is invalid: Invalid magic.\n", ++ k->modulename); ++ return -1; ++ } ++ if (k->total_size > size) { ++ kperr("'%s' patch is invalid: Invalid size: %u/%ld.\n", ++ k->modulename, k->total_size, size); ++ return -1; ++ } ++ hdr = (void *)k + k->kpatch_offset; ++ if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) || ++ hdr->e_type != ET_REL || ++ hdr->e_shentsize != sizeof(GElf_Shdr)) { ++ kperr("'%s' patch is invalid: Wrong ELF header or not ET_REL\n", ++ k->modulename); ++ return -1; ++ } ++ kpdebug("OK\n"); ++ return 1; ++} ++ ++int storage_init(kpatch_storage_t *storage, ++ const char *fname) ++{ ++ int patch_fd = -1; ++ struct stat stat = { .st_mode = 0 }; ++ ++ if (fname != NULL) { ++ patch_fd = open(fname, O_RDONLY | O_CLOEXEC); ++ if (patch_fd < 0) ++ goto out_err; ++ ++ if (fstat(patch_fd, &stat) < 0) ++ goto out_close; ++ } ++ ++ storage->patch_fd = patch_fd; ++ storage->is_patch_dir = S_ISDIR(stat.st_mode); ++ storage->path = NULL; ++ ++ if (storage->is_patch_dir) { ++ rb_init(&storage->tree); ++ } else { ++ int ret; ++ ++ ret = kpatch_open_fd(storage->patch_fd, &storage->patch.kpfile); ++ if (ret < 0) ++ goto out_close; ++ ++ ret = patch_file_verify(&storage->patch.kpfile); ++ if (ret < 0) { ++ kpatch_close_file(&storage->patch.kpfile); ++ goto out_close; ++ } ++ strcpy(storage->patch.buildid, storage->patch.kpfile.patch->uname); ++ } ++ ++ storage->path = strdup(fname); ++ ++ return 0; ++ ++out_close: ++ close(patch_fd); ++out_err: ++ kplogerror("cannot open storage '%s'\n", fname); ++ return -1; ++} ++ ++static void ++free_storage_patch_cb(struct rb_node *node) ++{ ++ struct kpatch_storage_patch *patch; ++ ++ patch = rb_entry(node, struct kpatch_storage_patch, node); ++ kpatch_close_file(&patch->kpfile); ++ ++ free(patch->desc); ++ free(patch); ++} ++ ++void storage_free(kpatch_storage_t *storage) ++{ ++ close(storage->patch_fd); ++ if (storage->is_patch_dir) ++ rb_destroy(&storage->tree, free_storage_patch_cb); ++ free(storage->path); ++} ++ ++static int ++cmp_buildid(struct rb_node *node, unsigned long key) ++{ ++ const char *bid = (const char *)key; ++ struct kpatch_storage_patch *patch; ++ ++ patch = rb_entry(node, struct kpatch_storage_patch, node); ++ ++ return strcmp(patch->buildid, bid); ++} ++ ++#define PATCHLEVEL_TEMPLATE_NUM 0 ++ ++static char *pathtemplates[] = { ++ "%s/latest/kpatch.bin", ++ "%s.kpatch" ++}; ++ ++static int ++readlink_patchlevel(int dirfd, const char *fname) ++{ ++ ssize_t r; ++ char buf[32]; ++ ++ *strrchr(fname, '/') = '\0'; ++ r = readlinkat(dirfd, fname, buf, sizeof(buf)); ++ if (r > 0 && r < 32) { ++ buf[r] = '\0'; ++ return atoi(buf); ++ } else if (r >= 32) { ++ r = -1; ++ errno = ERANGE; ++ } ++ ++ kplogerror("can't readlink '%s' to find patchlevel\n", ++ fname); ++ return -1; ++} ++ ++static inline int ++storage_open_patch(kpatch_storage_t *storage, ++ const char *buildid, ++ struct kpatch_storage_patch* patch) ++{ ++ char fname[96]; ++ int i, rv; ++ ++ for (i = 0; i < ARRAY_SIZE(pathtemplates); i++) { ++ sprintf(fname, pathtemplates[i], buildid); ++ ++ rv = kpatch_openat_file(storage->patch_fd, fname, &patch->kpfile); ++ if (rv == 0) { ++ rv = patch_file_verify(&patch->kpfile); ++ ++ if (rv < 0) ++ kpatch_close_file(&patch->kpfile); ++ else ++ rv = PATCH_FOUND; ++ break; ++ } ++ } ++ ++ if (rv == PATCH_FOUND && i == PATCHLEVEL_TEMPLATE_NUM) { ++ rv = readlink_patchlevel(storage->patch_fd, fname); ++ if (rv < 0) { ++ rv = PATCH_OPEN_ERROR; ++ kpatch_close_file(&patch->kpfile); ++ } else { ++ patch->patchlevel = rv; ++ patch->kpfile.patch->user_level = patch->patchlevel; ++ rv = PATCH_FOUND; ++ } ++ ++ } ++ ++ return rv; ++} ++ ++static inline int ++storage_stat_patch(kpatch_storage_t *storage, ++ const char *buildid, ++ struct kpatch_storage_patch* patch) ++{ ++ char fname[96]; ++ struct stat buf; ++ int i, rv; ++ ++ for (i = 0; i < ARRAY_SIZE(pathtemplates); i++) { ++ sprintf(fname, pathtemplates[i], buildid); ++ ++ rv = fstatat(storage->patch_fd, fname, &buf, /* flags */ 0); ++ ++ if (rv == 0) { ++ rv = PATCH_FOUND; ++ patch->kpfile.size = buf.st_size; ++ break; ++ } else if (rv < 0 && errno == ENOENT) { ++ rv = PATCH_NOT_FOUND; ++ } ++ } ++ ++ if (rv == PATCH_FOUND && i == PATCHLEVEL_TEMPLATE_NUM) { ++ rv = readlink_patchlevel(storage->patch_fd, fname); ++ if (rv < 0) { ++ rv = PATCH_OPEN_ERROR; ++ } else { ++ patch->patchlevel = rv; ++ rv = PATCH_FOUND; ++ } ++ } ++ ++ return rv; ++} ++ ++/* ++ * TODO(pboldin) I duplicate a lot of code kernel has for filesystems already. ++ * Should we avoid this caching at all? ++ */ ++#define ERR_PATCH ((struct kpatch_storage_patch *)1) ++static struct kpatch_storage_patch * ++storage_get_patch(kpatch_storage_t *storage, const char *buildid, ++ int load) ++{ ++ struct kpatch_storage_patch *patch = NULL; ++ struct rb_node *node; ++ int rv; ++ ++ if (!storage->is_patch_dir) { ++ if (!strcmp(storage->patch.buildid, buildid)) { ++ return &storage->patch; ++ } ++ return NULL; ++ } ++ ++ /* Look here, could be loaded already */ ++ node = rb_search_node(&storage->tree, cmp_buildid, ++ (unsigned long)buildid); ++ if (node != NULL) ++ return rb_entry(node, struct kpatch_storage_patch, node); ++ ++ /* OK, look at the filesystem */ ++ patch = malloc(sizeof(*patch)); ++ if (patch == NULL) ++ return ERR_PATCH; ++ ++ memset(patch, 0, sizeof(*patch)); ++ patch->patchlevel = -1; ++ init_kp_file(&patch->kpfile); ++ ++ if (load) ++ rv = storage_open_patch(storage, buildid, patch); ++ else ++ rv = storage_stat_patch(storage, buildid, patch); ++ ++ if (rv == PATCH_OPEN_ERROR) { ++ free(patch); ++ return ERR_PATCH; ++ } ++ ++ strcpy(patch->buildid, buildid); ++ ++ rb_insert_node(&storage->tree, ++ &patch->node, ++ cmp_buildid, ++ (unsigned long)patch->buildid); ++ ++ return patch; ++} ++ ++int storage_patch_found(struct kpatch_storage_patch *patch) ++{ ++ return patch && patch->kpfile.size >= 0; ++} ++ ++static int ++storage_load_patch(kpatch_storage_t *storage, const char *buildid, ++ struct kp_file **pkpfile) ++{ ++ struct kpatch_storage_patch *patch = NULL; ++ ++ if (pkpfile == NULL) { ++ kperr("pkpfile == NULL\n"); ++ return PATCH_OPEN_ERROR; ++ } ++ ++ patch = storage_get_patch(storage, buildid, /* load */ 1); ++ if (patch == ERR_PATCH) ++ return PATCH_OPEN_ERROR; ++ if (patch == NULL) ++ return PATCH_NOT_FOUND; ++ ++ *pkpfile = &patch->kpfile; ++ ++ return storage_patch_found(patch) ? PATCH_FOUND : PATCH_NOT_FOUND; ++} ++ ++int storage_have_patch(kpatch_storage_t *storage, const char *buildid, ++ struct kpatch_storage_patch **ppatch) ++{ ++ struct kpatch_storage_patch *patch = NULL; ++ ++ if (ppatch) ++ *ppatch = NULL; ++ ++ patch = storage_get_patch(storage, buildid, /* load */ 0); ++ if (patch == ERR_PATCH) ++ return PATCH_OPEN_ERROR; ++ ++ if (!storage_patch_found(patch)) ++ return PATCH_NOT_FOUND; ++ ++ if (ppatch) ++ *ppatch = patch; ++ return PATCH_FOUND; ++} ++ ++char *storage_get_description(kpatch_storage_t *storage, ++ struct kpatch_storage_patch *patch) ++{ ++ char *desc = NULL; ++ char path[PATH_MAX]; ++ int fd, rv, alloc = 0, sz = 0; ++ ++ if (!storage->is_patch_dir) ++ return NULL; ++ ++ if (patch->desc) ++ return patch->desc; ++ ++ sprintf(path, "%s/%d/description", patch->buildid, patch->patchlevel); ++ fd = openat(storage->patch_fd, path, O_RDONLY); ++ if (fd == -1) ++ return NULL; ++ ++ while (1) { ++ if (sz + 1024 >= alloc) { ++ char *olddesc = desc; ++ alloc += PAGE_SIZE; ++ ++ desc = malloc(alloc); ++ ++ if (olddesc != NULL) { ++ memcpy(desc, olddesc, sz); ++ free(olddesc); ++ } ++ ++ olddesc = desc; ++ } ++ ++ rv = read(fd, desc + sz, alloc - sz); ++ if (rv == -1 && errno == EINTR) ++ continue; ++ ++ if (rv == -1) ++ goto err_free; ++ ++ if (rv == 0) ++ break; ++ ++ sz += rv; ++ } ++ ++ patch->desc = desc; ++ ++ return desc; ++ ++err_free: ++ free(desc); ++ close(fd); ++ return NULL; ++} ++ ++int storage_lookup_patches(kpatch_storage_t *storage, kpatch_process_t *proc) ++{ ++ struct kp_file *pkpfile; ++ struct object_file *o; ++ const char *bid; ++ int found = 0, ret; ++ ++ list_for_each_entry(o, &proc->objs, list) { ++ if (!o->is_elf || is_kernel_object_name(o->name)) ++ continue; ++ ++ bid = kpatch_get_buildid(o); ++ if (bid == NULL) { ++ kpinfo("can't get buildid for %s\n", ++ o->name); ++ continue; ++ } ++ ++ ret = storage_load_patch(storage, bid, &pkpfile); ++ if (ret == PATCH_OPEN_ERROR) { ++ if (errno != ENOENT) ++ kplogerror("error finding patch for %s (%s)\n", ++ o->name, bid); ++ continue; ++ } ++ ++ if (ret == PATCH_FOUND) { ++ o->skpfile = pkpfile; ++ found++; ++ } ++ } ++ ++ kpinfo("%d object(s) have valid patch(es)\n", found); ++ ++ kpdebug("Object files dump:\n"); ++ list_for_each_entry(o, &proc->objs, list) ++ kpatch_object_dump(o); ++ ++ return found; ++} ++ ++static int ++storage_execute_script(kpatch_storage_t *storage, ++ kpatch_process_t *proc, ++ const char *name) ++{ ++ int childpid, rv = 0, status; ++ char pidbuf[16], pathbuf[PATH_MAX]; ++ ++ if (!storage->is_patch_dir) ++ return 0; ++ ++ sprintf(pathbuf, "%s/%s", storage->path, name); ++ ++ rv = access(pathbuf, X_OK); ++ /* No file -- no problems */ ++ if (rv < 0) ++ return errno == ENOENT ? 0 : -1; ++ ++ sprintf(pidbuf, "%d", proc->pid); ++ ++ childpid = fork(); ++ if (childpid == 0) { ++ rv = execl(pathbuf, name, pidbuf, NULL); ++ if (rv < 0) ++ kplogerror("execl failed\n"); ++ exit(EXIT_FAILURE); ++ } else { ++ rv = waitpid(childpid, &status, 0); ++ if (rv < 0) ++ kplogerror("waitpid failed for %d\n", childpid); ++ ++ if (WIFEXITED(status)) ++ rv = WEXITSTATUS(status); ++ else if (WIFSIGNALED(status)) ++ rv = WTERMSIG(status); ++ if (rv) ++ kperr("child script failed %d\n", rv); ++ } ++ ++ return -rv; ++} ++ ++int storage_execute_before_script(kpatch_storage_t *storage, kpatch_process_t *proc) ++{ ++ return storage_execute_script(storage, proc, "before"); ++} ++ ++int storage_execute_after_script(kpatch_storage_t *storage, kpatch_process_t *proc) ++{ ++ return storage_execute_script(storage, proc, "after"); ++} ++ +diff --git a/src/kpatch_storage.h b/src/kpatch_storage.h +new file mode 100644 +index 0000000..eb8064b +--- /dev/null ++++ b/src/kpatch_storage.h +@@ -0,0 +1,66 @@ ++#ifndef __KPATCH_STORAGE__ ++#define __KPATCH_STORAGE__ ++ ++#include "kpatch_common.h" ++#include "kpatch_file.h" ++#include "kpatch_process.h" ++#include "rbtree.h" ++ ++struct kpatch_storage_patch { ++ /* Pointer to the object's patch (if any) */ ++ struct kp_file kpfile; ++ ++ /* Build id kept here for negative caching */ ++ char buildid[41]; ++ ++ /* Patch level */ ++ int patchlevel; ++ ++ /* Description cache */ ++ char *desc; ++ ++ /* Node for rb_root */ ++ struct rb_node node; ++}; ++ ++struct kpatch_storage { ++ /* Patch storage path */ ++ char *path; ++ ++ /* Patch file (or directory) descriptor */ ++ int patch_fd; ++ ++ /* Is patch_fd a directory or a file? */ ++ char is_patch_dir; ++ ++ union { ++ /* Tree with BuildID keyed `kp_file's, ++ * is_patch_dir = 1 */ ++ struct rb_root tree; ++ ++ /* A single file, is_patch_dir = 0 */ ++ struct kpatch_storage_patch patch; ++ }; ++}; ++ ++typedef struct kpatch_storage kpatch_storage_t; ++ ++int storage_init(kpatch_storage_t *storage, ++ const char *fname); ++void storage_free(kpatch_storage_t *storage); ++ ++enum { ++ PATCH_OPEN_ERROR = -1, ++ PATCH_NOT_FOUND = 0, ++ PATCH_FOUND = 1, ++}; ++int storage_lookup_patches(kpatch_storage_t *storage, kpatch_process_t *proc); ++int storage_have_patch(kpatch_storage_t *storage, const char *buildid, ++ struct kpatch_storage_patch **ppatch); ++int storage_patch_found(struct kpatch_storage_patch *patch); ++int storage_execute_before_script(kpatch_storage_t *storage, kpatch_process_t *proc); ++int storage_execute_after_script(kpatch_storage_t *storage, kpatch_process_t *proc); ++char *storage_get_description(kpatch_storage_t *storage, ++ struct kpatch_storage_patch *patch); ++ ++#endif +diff --git a/src/kpatch_user.c b/src/kpatch_user.c +index 21a90ab..672b4d4 100644 +--- a/src/kpatch_user.c ++++ b/src/kpatch_user.c +@@ -19,6 +19,7 @@ + #include + + #include "kpatch_user.h" ++#include "kpatch_storage.h" + #include "kpatch_process.h" + #include "kpatch_file.h" + #include "kpatch_common.h" +@@ -41,487 +42,6 @@ typedef int (callback_t)(int pid, void *data); + static int + processes_do(int pid, callback_t callback, void *data); + +-/***************************************************************************** +- * Patch storage subroutines. +- ****************************************************************************/ +- +-static int +-patch_file_verify(struct kp_file *kpfile) +-{ +- GElf_Ehdr *hdr; +- struct kpatch_file *k = kpfile->patch; +- ssize_t size = kpfile->size; +- +- kpdebug("Verifying patch for '%s'...", k->modulename); +- +- if (memcmp(k->magic, KPATCH_FILE_MAGIC1, sizeof(k->magic))) { +- kperr("'%s' patch is invalid: Invalid magic.\n", +- k->modulename); +- return -1; +- } +- if (k->total_size > size) { +- kperr("'%s' patch is invalid: Invalid size: %u/%ld.\n", +- k->modulename, k->total_size, size); +- return -1; +- } +- hdr = (void *)k + k->kpatch_offset; +- if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) || +- hdr->e_type != ET_REL || +- hdr->e_shentsize != sizeof(GElf_Shdr)) { +- kperr("'%s' patch is invalid: Wrong ELF header or not ET_REL\n", +- k->modulename); +- return -1; +- } +- kpdebug("OK\n"); +- return 1; +-} +- +-static int +-storage_init(kpatch_storage_t *storage, +- const char *fname) +-{ +- int patch_fd = -1; +- struct stat stat = { .st_mode = 0 }; +- +- if (fname != NULL) { +- patch_fd = open(fname, O_RDONLY | O_CLOEXEC); +- if (patch_fd < 0) +- goto out_err; +- +- if (fstat(patch_fd, &stat) < 0) +- goto out_close; +- } +- +- storage->patch_fd = patch_fd; +- storage->is_patch_dir = S_ISDIR(stat.st_mode); +- storage->path = NULL; +- +- if (storage->is_patch_dir) { +- rb_init(&storage->tree); +- } else { +- int ret; +- +- ret = kpatch_open_fd(storage->patch_fd, &storage->patch.kpfile); +- if (ret < 0) +- goto out_close; +- +- ret = patch_file_verify(&storage->patch.kpfile); +- if (ret < 0) { +- kpatch_close_file(&storage->patch.kpfile); +- goto out_close; +- } +- strcpy(storage->patch.buildid, storage->patch.kpfile.patch->uname); +- } +- +- storage->path = strdup(fname); +- +- return 0; +- +-out_close: +- close(patch_fd); +-out_err: +- kplogerror("cannot open storage '%s'\n", fname); +- return -1; +-} +- +-static void +-free_storage_patch_cb(struct rb_node *node) +-{ +- struct kpatch_storage_patch *patch; +- +- patch = rb_entry(node, struct kpatch_storage_patch, node); +- kpatch_close_file(&patch->kpfile); +- +- free(patch->desc); +- free(patch); +-} +- +-static void +-storage_free(kpatch_storage_t *storage) +-{ +- close(storage->patch_fd); +- if (storage->is_patch_dir) +- rb_destroy(&storage->tree, free_storage_patch_cb); +- free(storage->path); +-} +- +-static int +-cmp_buildid(struct rb_node *node, unsigned long key) +-{ +- const char *bid = (const char *)key; +- struct kpatch_storage_patch *patch; +- +- patch = rb_entry(node, struct kpatch_storage_patch, node); +- +- return strcmp(patch->buildid, bid); +-} +- +-#define PATCHLEVEL_TEMPLATE_NUM 0 +- +-static char *pathtemplates[] = { +- "%s/latest/kpatch.bin", +- "%s.kpatch" +-}; +- +-static int +-readlink_patchlevel(int dirfd, const char *fname) +-{ +- ssize_t r; +- char buf[32]; +- +- *strrchr(fname, '/') = '\0'; +- r = readlinkat(dirfd, fname, buf, sizeof(buf)); +- if (r > 0 && r < 32) { +- buf[r] = '\0'; +- return atoi(buf); +- } else if (r >= 32) { +- r = -1; +- errno = ERANGE; +- } +- +- kplogerror("can't readlink '%s' to find patchlevel\n", +- fname); +- return -1; +-} +- +-enum { +- PATCH_OPEN_ERROR = -1, +- PATCH_NOT_FOUND = 0, +- PATCH_FOUND = 1, +-}; +- +-static inline int +-storage_open_patch(kpatch_storage_t *storage, +- const char *buildid, +- struct kpatch_storage_patch* patch) +-{ +- char fname[96]; +- int i, rv; +- +- for (i = 0; i < ARRAY_SIZE(pathtemplates); i++) { +- sprintf(fname, pathtemplates[i], buildid); +- +- rv = kpatch_openat_file(storage->patch_fd, fname, &patch->kpfile); +- if (rv == 0) { +- rv = patch_file_verify(&patch->kpfile); +- +- if (rv < 0) +- kpatch_close_file(&patch->kpfile); +- else +- rv = PATCH_FOUND; +- break; +- } +- } +- +- if (rv == PATCH_FOUND && i == PATCHLEVEL_TEMPLATE_NUM) { +- rv = readlink_patchlevel(storage->patch_fd, fname); +- if (rv < 0) { +- rv = PATCH_OPEN_ERROR; +- kpatch_close_file(&patch->kpfile); +- } else { +- patch->patchlevel = rv; +- patch->kpfile.patch->user_level = patch->patchlevel; +- rv = PATCH_FOUND; +- } +- +- } +- +- return rv; +-} +- +-static inline int +-storage_stat_patch(kpatch_storage_t *storage, +- const char *buildid, +- struct kpatch_storage_patch* patch) +-{ +- char fname[96]; +- struct stat buf; +- int i, rv; +- +- for (i = 0; i < ARRAY_SIZE(pathtemplates); i++) { +- sprintf(fname, pathtemplates[i], buildid); +- +- rv = fstatat(storage->patch_fd, fname, &buf, /* flags */ 0); +- +- if (rv == 0) { +- rv = PATCH_FOUND; +- patch->kpfile.size = buf.st_size; +- break; +- } else if (rv < 0 && errno == ENOENT) { +- rv = PATCH_NOT_FOUND; +- } +- } +- +- if (rv == PATCH_FOUND && i == PATCHLEVEL_TEMPLATE_NUM) { +- rv = readlink_patchlevel(storage->patch_fd, fname); +- if (rv < 0) { +- rv = PATCH_OPEN_ERROR; +- } else { +- patch->patchlevel = rv; +- rv = PATCH_FOUND; +- } +- } +- +- return rv; +-} +- +-/* +- * TODO(pboldin) I duplicate a lot of code kernel has for filesystems already. +- * Should we avoid this caching at all? +- */ +-#define ERR_PATCH ((struct kpatch_storage_patch *)1) +-static struct kpatch_storage_patch * +-storage_get_patch(kpatch_storage_t *storage, const char *buildid, +- int load) +-{ +- struct kpatch_storage_patch *patch = NULL; +- struct rb_node *node; +- int rv; +- +- if (!storage->is_patch_dir) { +- if (!strcmp(storage->patch.buildid, buildid)) { +- return &storage->patch; +- } +- return NULL; +- } +- +- /* Look here, could be loaded already */ +- node = rb_search_node(&storage->tree, cmp_buildid, +- (unsigned long)buildid); +- if (node != NULL) +- return rb_entry(node, struct kpatch_storage_patch, node); +- +- /* OK, look at the filesystem */ +- patch = malloc(sizeof(*patch)); +- if (patch == NULL) +- return ERR_PATCH; +- +- memset(patch, 0, sizeof(*patch)); +- patch->patchlevel = -1; +- init_kp_file(&patch->kpfile); +- +- if (load) +- rv = storage_open_patch(storage, buildid, patch); +- else +- rv = storage_stat_patch(storage, buildid, patch); +- +- if (rv == PATCH_OPEN_ERROR) { +- free(patch); +- return ERR_PATCH; +- } +- +- strcpy(patch->buildid, buildid); +- +- rb_insert_node(&storage->tree, +- &patch->node, +- cmp_buildid, +- (unsigned long)patch->buildid); +- +- return patch; +-} +- +-static int +-storage_patch_found(struct kpatch_storage_patch *patch) +-{ +- return patch && patch->kpfile.size >= 0; +-} +- +-static int +-storage_load_patch(kpatch_storage_t *storage, const char *buildid, +- struct kp_file **pkpfile) +-{ +- struct kpatch_storage_patch *patch = NULL; +- +- if (pkpfile == NULL) { +- kperr("pkpfile == NULL\n"); +- return PATCH_OPEN_ERROR; +- } +- +- patch = storage_get_patch(storage, buildid, /* load */ 1); +- if (patch == ERR_PATCH) +- return PATCH_OPEN_ERROR; +- if (patch == NULL) +- return PATCH_NOT_FOUND; +- +- *pkpfile = &patch->kpfile; +- +- return storage_patch_found(patch) ? PATCH_FOUND : PATCH_NOT_FOUND; +-} +- +-static int +-storage_have_patch(kpatch_storage_t *storage, const char *buildid, +- struct kpatch_storage_patch **ppatch) +-{ +- struct kpatch_storage_patch *patch = NULL; +- +- if (ppatch) +- *ppatch = NULL; +- +- patch = storage_get_patch(storage, buildid, /* load */ 0); +- if (patch == ERR_PATCH) +- return PATCH_OPEN_ERROR; +- +- if (!storage_patch_found(patch)) +- return PATCH_NOT_FOUND; +- +- if (ppatch) +- *ppatch = patch; +- return PATCH_FOUND; +-} +- +-static char * +-storage_get_description(kpatch_storage_t *storage, +- struct kpatch_storage_patch *patch) +-{ +- char *desc = NULL; +- char path[PATH_MAX]; +- int fd, rv, alloc = 0, sz = 0; +- +- if (!storage->is_patch_dir) +- return NULL; +- +- if (patch->desc) +- return patch->desc; +- +- sprintf(path, "%s/%d/description", patch->buildid, patch->patchlevel); +- fd = openat(storage->patch_fd, path, O_RDONLY); +- if (fd == -1) +- return NULL; +- +- while (1) { +- if (sz + 1024 >= alloc) { +- char *olddesc = desc; +- alloc += PAGE_SIZE; +- +- desc = malloc(alloc); +- +- if (olddesc != NULL) { +- memcpy(desc, olddesc, sz); +- free(olddesc); +- } +- +- olddesc = desc; +- } +- +- rv = read(fd, desc + sz, alloc - sz); +- if (rv == -1 && errno == EINTR) +- continue; +- +- if (rv == -1) +- goto err_free; +- +- if (rv == 0) +- break; +- +- sz += rv; +- } +- +- patch->desc = desc; +- +- return desc; +- +-err_free: +- free(desc); +- close(fd); +- return NULL; +-} +- +-static int +-storage_lookup_patches(kpatch_storage_t *storage, kpatch_process_t *proc) +-{ +- struct kp_file *pkpfile; +- struct object_file *o; +- const char *bid; +- int found = 0, ret; +- +- list_for_each_entry(o, &proc->objs, list) { +- if (!o->is_elf || is_kernel_object_name(o->name)) +- continue; +- +- bid = kpatch_get_buildid(o); +- if (bid == NULL) { +- kpinfo("can't get buildid for %s\n", +- o->name); +- continue; +- } +- +- ret = storage_load_patch(storage, bid, &pkpfile); +- if (ret == PATCH_OPEN_ERROR) { +- if (errno != ENOENT) +- kplogerror("error finding patch for %s (%s)\n", +- o->name, bid); +- continue; +- } +- +- if (ret == PATCH_FOUND) { +- o->skpfile = pkpfile; +- found++; +- } +- } +- +- kpinfo("%d object(s) have valid patch(es)\n", found); +- +- kpdebug("Object files dump:\n"); +- list_for_each_entry(o, &proc->objs, list) +- kpatch_object_dump(o); +- +- return found; +-} +- +-static int +-storage_execute_script(kpatch_storage_t *storage, +- kpatch_process_t *proc, +- const char *name) +-{ +- int childpid, rv = 0, status; +- char pidbuf[16], pathbuf[PATH_MAX]; +- +- if (!storage->is_patch_dir) +- return 0; +- +- sprintf(pathbuf, "%s/%s", storage->path, name); +- +- rv = access(pathbuf, X_OK); +- /* No file -- no problems */ +- if (rv < 0) +- return errno == ENOENT ? 0 : -1; +- +- sprintf(pidbuf, "%d", proc->pid); +- +- childpid = fork(); +- if (childpid == 0) { +- rv = execl(pathbuf, name, pidbuf, NULL); +- if (rv < 0) +- kplogerror("execl failed\n"); +- exit(EXIT_FAILURE); +- } else { +- rv = waitpid(childpid, &status, 0); +- if (rv < 0) +- kplogerror("waitpid failed for %d\n", childpid); +- +- if (WIFEXITED(status)) +- rv = WEXITSTATUS(status); +- else if (WIFSIGNALED(status)) +- rv = WTERMSIG(status); +- if (rv) +- kperr("child script failed %d\n", rv); +- } +- +- return -rv; +-} +- +-static int +-storage_execute_before_script(kpatch_storage_t *storage, kpatch_process_t *proc) +-{ +- return storage_execute_script(storage, proc, "before"); +-} +- +-static int +-storage_execute_after_script(kpatch_storage_t *storage, kpatch_process_t *proc) +-{ +- return storage_execute_script(storage, proc, "after"); +-} +- + enum { + ACTION_APPLY_PATCH, + ACTION_UNAPPLY_PATCH +diff --git a/src/kpatch_user.h b/src/kpatch_user.h +index 091c463..c0b52ff 100644 +--- a/src/kpatch_user.h ++++ b/src/kpatch_user.h +@@ -5,45 +5,6 @@ + #include "kpatch_file.h" + #include "rbtree.h" + +-struct kpatch_storage_patch { +- /* Pointer to the object's patch (if any) */ +- struct kp_file kpfile; +- +- /* Build id kept here for negative caching */ +- char buildid[41]; +- +- /* Patch level */ +- int patchlevel; +- +- /* Description cache */ +- char *desc; +- +- /* Node for rb_root */ +- struct rb_node node; +-}; +- +-struct kpatch_storage { +- /* Patch storage path */ +- char *path; +- +- /* Patch file (or directory) descriptor */ +- int patch_fd; +- +- /* Is patch_fd a directory or a file? */ +- char is_patch_dir; +- +- union { +- /* Tree with BuildID keyed `kp_file's, +- * is_patch_dir = 1 */ +- struct rb_root tree; +- +- /* A single file, is_patch_dir = 0 */ +- struct kpatch_storage_patch patch; +- }; +-}; +- +-typedef struct kpatch_storage kpatch_storage_t; +- + int cmd_patch_user(int argc, char *argv[]); + int cmd_unpatch_user(int argc, char *argv[]); + +-- +2.23.0 + diff --git a/0002-Split-kpatch_patch.c-from-kpatch_user.c.patch b/0002-Split-kpatch_patch.c-from-kpatch_user.c.patch new file mode 100644 index 0000000..922b055 --- /dev/null +++ b/0002-Split-kpatch_patch.c-from-kpatch_user.c.patch @@ -0,0 +1,1633 @@ +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 + diff --git a/0003-cmd_patch-pass-arguments-directly.patch b/0003-cmd_patch-pass-arguments-directly.patch new file mode 100644 index 0000000..7283818 --- /dev/null +++ b/0003-cmd_patch-pass-arguments-directly.patch @@ -0,0 +1,152 @@ +From 98833d06737a9a1128f548d15344b1cbaeed049f Mon Sep 17 00:00:00 2001 +From: Pavel Boldin +Date: Wed, 24 Jan 2018 13:14:10 +0200 +Subject: [PATCH 03/89] cmd_patch: pass arguments directly + +Signed-off-by: Pavel Boldin +--- + src/kpatch_user.c | 84 ++++++++++++++++------------------------------- + 1 file changed, 29 insertions(+), 55 deletions(-) + +diff --git a/src/kpatch_user.c b/src/kpatch_user.c +index 9ab77b9..d257b67 100644 +--- a/src/kpatch_user.c ++++ b/src/kpatch_user.c +@@ -63,19 +63,36 @@ static int usage_patch(const char *err) + { + if (err) + fprintf(stderr, "err: %s\n", err); +- fprintf(stderr, "usage: libcare-ctl patch [options] <-p PID> <-r fd> \n"); ++ fprintf(stderr, "usage: libcare-ctl patch [options] <-p PID> \n"); + fprintf(stderr, "\nOptions:\n"); + fprintf(stderr, " -h - this message\n"); +- fprintf(stderr, " -s - process was just executed\n"); + fprintf(stderr, " -p - target process\n"); +- fprintf(stderr, " -r fd - fd used with LD_PRELOAD=execve.so.\n"); + return err ? 0 : -1; + } + +-int cmd_patch_user(int argc, char *argv[]) ++static int ++patch_user(const char *storage_path, int pid, ++ int is_just_started, int send_fd) + { ++ int ret; + kpatch_storage_t storage; +- int opt, pid = -1, is_pid_set = 0, ret, start = 0, send_fd = -1; ++ ++ ret = storage_init(&storage, storage_path); ++ if (ret < 0) ++ return ret; ++ ++ ret = processes_patch(&storage, pid, is_just_started, send_fd); ++ ++ storage_free(&storage); ++ ++ return ret; ++} ++ ++ ++int cmd_patch_user(int argc, char *argv[]) ++{ ++ int opt, pid = -1, is_pid_set = 0, ret; ++ const char *storage_path; + + if (argc < 4) + return usage_patch(NULL); +@@ -89,12 +106,6 @@ int cmd_patch_user(int argc, char *argv[]) + pid = atoi(optarg); + is_pid_set = 1; + break; +- case 'r': +- send_fd = atoi(optarg); +- break; +- case 's': +- start = 1; +- break; + default: + return usage_patch("unknown option"); + } +@@ -109,14 +120,9 @@ int cmd_patch_user(int argc, char *argv[]) + if (!kpatch_check_system()) + goto out_err; + +- ret = storage_init(&storage, argv[argc - 1]); +- if (ret < 0) +- goto out_err; +- +- +- ret = processes_patch(&storage, pid, start, send_fd); +- +- storage_free(&storage); ++ storage_path = argv[argc - 1]; ++ ret = patch_user(storage_path, pid, ++ /* is_just_started */ 0, /* send_fd */ -1); + + out_err: + return ret; +@@ -474,24 +480,6 @@ static int + cmd_execve_startup(int fd, int argc, char *argv[], int is_just_started) + { + int rv, pid; +- char pid_str[64], send_fd_str[64]; +- char *patch_pid_argv_execve[] = { +- "patch", +- "-s", +- "-p", +- pid_str, +- "-r", +- send_fd_str, +- storage_dir +- }; +- char *patch_pid_argv_startup[] = { +- "patch", +- "-p", +- pid_str, +- "-r", +- send_fd_str, +- storage_dir +- }; + + rv = sscanf(argv[1], "%d", &pid); + if (rv != 1) { +@@ -499,16 +487,8 @@ cmd_execve_startup(int fd, int argc, char *argv[], int is_just_started) + return -1; + } + +- sprintf(pid_str, "%d", pid); +- sprintf(send_fd_str, "%d", fd); +- + optind = 1; +- if (is_just_started) +- rv = cmd_patch_user(ARRAY_SIZE(patch_pid_argv_execve), +- patch_pid_argv_execve); +- else +- rv = cmd_patch_user(ARRAY_SIZE(patch_pid_argv_startup), +- patch_pid_argv_startup); ++ rv = patch_user(storage_dir, pid, is_just_started, fd); + + if (rv < 0) + kperr("can't patch pid %d\n", pid); +@@ -578,15 +558,9 @@ cmd_storage(int argc, char *argv[]) + static int + cmd_update(int argc, char *argv[]) + { +- char *patch_all[] = { +- "patch", +- "-p", +- "all", +- storage_dir +- }; +- +- optind = 1; +- return cmd_patch_user(ARRAY_SIZE(patch_all), patch_all); ++ return patch_user(storage_dir, /* pid */ -1, ++ /* is_just_started */ 0, ++ /* send_fd */ -1); + } + + static int +-- +2.23.0 + diff --git a/0004-travis-use-VM-for-now.patch b/0004-travis-use-VM-for-now.patch new file mode 100644 index 0000000..c286c1d --- /dev/null +++ b/0004-travis-use-VM-for-now.patch @@ -0,0 +1,71 @@ +From f4b6b37575e514f3e54f08166dc14e35815a8ebb Mon Sep 17 00:00:00 2001 +From: Pavel Boldin +Date: Tue, 30 Jan 2018 08:15:11 +0200 +Subject: [PATCH 04/89] travis: use VM for now + +Attaching even to a children process is broken in travis's Docker. +Use VM until https://github.com/travis-ci/travis-ci/issues/9033 +is fixed. + +Signed-off-by: Pavel Boldin +--- + .travis.yml | 2 +- + Makefile | 2 +- + tests/run_tests.sh | 18 +++++++++++++++++- + 3 files changed, 19 insertions(+), 3 deletions(-) + +diff --git a/.travis.yml b/.travis.yml +index 4bb2614..f3ccb4e 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -3,7 +3,7 @@ script: make tests + language: c + + dist: trusty +-sudo: false ++sudo: required + + addons: + apt: +diff --git a/Makefile b/Makefile +index 2da227f..c5a2837 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,4 +1,4 @@ +- ++#dummy + + all: src + +diff --git a/tests/run_tests.sh b/tests/run_tests.sh +index fcfcd56..fbfb273 100755 +--- a/tests/run_tests.sh ++++ b/tests/run_tests.sh +@@ -3,7 +3,23 @@ + set -e + + wait_file() { +- while ! test -s $1; do sleep ${2-1}; done ++ local file="$1" ++ local pause="${2-1}" ++ local i=0 ++ local timeout=60 ++ ++ while test $i -lt $timeout; do ++ if test -s $file; then ++ break ++ fi ++ sleep $pause ++ i=$((i + 1)) ++ done ++ ++ if test $i -eq $timeout; then ++ return 1 ++ fi ++ + return 0 + } + +-- +2.23.0 + diff --git a/0005-scripts-pkgbuild-add-prepare_env-hook.patch b/0005-scripts-pkgbuild-add-prepare_env-hook.patch new file mode 100644 index 0000000..cf607af --- /dev/null +++ b/0005-scripts-pkgbuild-add-prepare_env-hook.patch @@ -0,0 +1,39 @@ +From 8e1d1d24e39464e88e92695ea5951639e789f72d Mon Sep 17 00:00:00 2001 +From: Pavel Boldin +Date: Sat, 28 Oct 2017 03:47:48 +0300 +Subject: [PATCH 05/89] scripts/pkgbuild: add prepare_env hook + +Change-Id: Iabb96a1b9f9cd8d1415029380f825da29156a840 +Signed-off-by: Pavel Boldin +--- + scripts/pkgbuild | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/scripts/pkgbuild b/scripts/pkgbuild +index ea1f411..3697594 100755 +--- a/scripts/pkgbuild ++++ b/scripts/pkgbuild +@@ -73,6 +73,11 @@ clean_dirs() { + rm -rf $KP_PROJECT_BUILD_ROOT /root/root.original /root/root.patched + } + ++kp_prepare_env_hook() { ++ # use this to add repos ++ : ++} ++ + kp_pack_prebuilt() { + echo " packing prebuilt $KP_PROJECT into $KP_PROJECT_PREBUILT" + pushd $KP_PROJECT_BUILD_ROOT +@@ -386,6 +391,8 @@ main() { + + overwrite_utils + ++ kp_prepare_env_hook ++ + if [ "$ACTION" == "prebuild" ]; then + kp_prepare_source + kp_prebuild_hook +-- +2.23.0 + diff --git a/0006-pkgbuild-fix-for-non-root-rpmbuild-built-root.patch b/0006-pkgbuild-fix-for-non-root-rpmbuild-built-root.patch new file mode 100644 index 0000000..3d93ea1 --- /dev/null +++ b/0006-pkgbuild-fix-for-non-root-rpmbuild-built-root.patch @@ -0,0 +1,42 @@ +From 859be3502b57c87f3241c11d36059313cbdde46a Mon Sep 17 00:00:00 2001 +From: Pavel Boldin +Date: Tue, 31 Oct 2017 04:04:58 +0100 +Subject: [PATCH 06/89] pkgbuild: fix for non-/root/rpmbuild built root + +--- + scripts/pkgbuild | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/scripts/pkgbuild b/scripts/pkgbuild +index 3697594..e2ac7c7 100755 +--- a/scripts/pkgbuild ++++ b/scripts/pkgbuild +@@ -120,7 +120,8 @@ kp_prepare_source_rpm() { + + sed -i 's/.rpm$//g' $KP_PROJECT_BUILD_ROOT/dependencies.txt + +- rpm -ivh /kcdata/$KP_PROJECT_SOURCE ++ rpm -ivh /kcdata/$KP_PROJECT_SOURCE \ ++ --define "_topdir $KP_PROJECT_BUILD_ROOT" + } + + kp_prepare_source_deb() { +@@ -152,6 +153,7 @@ kp_prebuild_rpm() { + eval rpmbuild --nocheck --noclean \ + -bc \ + $KP_RPMBUILD_FLAGS \ ++ '--define "_topdir $KP_PROJECT_BUILD_ROOT"' \ + $KP_PROJECT_BUILD_ROOT/SPECS/$KP_PROJECT_SPEC 2>&1 | \ + tee $KP_PROJECT_BUILD_ROOT/prebuild.log + } +@@ -237,6 +239,7 @@ kp_build_rpm() { + --short-circuit \ + -bc \ + $KP_RPMBUILD_FLAGS \ ++ '--define "_topdir $KP_PROJECT_BUILD_ROOT"' \ + $KP_PROJECT_BUILD_ROOT/SPECS/$KP_PROJECT_SPEC + } + +-- +2.23.0 + diff --git a/0007-Toil-package-builder.patch b/0007-Toil-package-builder.patch new file mode 100644 index 0000000..df51f15 --- /dev/null +++ b/0007-Toil-package-builder.patch @@ -0,0 +1,1327 @@ +From 2d1416fc62016533e159f27352f17a7d97f022b1 Mon Sep 17 00:00:00 2001 +From: Pavel Boldin +Date: Fri, 13 Oct 2017 11:52:32 +0300 +Subject: [PATCH 07/89] Toil package builder + +Introduce a toil[1]-based workflow to automate patch building for +a package. + +[1] https://toil.readthedocs.io/ + +Change-Id: I7e3a11ad95781b8989d24fbf3992dcb49e4c8dba +Signed-off-by: Pavel Boldin +--- + packages/rhel7/glibc/glibc-2.17-55.el7/info | 23 +- + .../glibc/glibc-2.17-55.el7/pkgfile.yaml | 5 + + scripts/pkgbuild | 28 +- + scripts/toil/README.md | 1 + + scripts/toil/build-patch.sh | 18 + + scripts/toil/pkgbuild.py | 894 ++++++++++++++++++ + scripts/toil/requirements.txt | 4 + + scripts/toil/tests_pkgbuild.py | 218 +++++ + 8 files changed, 1175 insertions(+), 16 deletions(-) + create mode 100644 packages/rhel7/glibc/glibc-2.17-55.el7/pkgfile.yaml + create mode 100644 scripts/toil/README.md + create mode 100755 scripts/toil/build-patch.sh + create mode 100755 scripts/toil/pkgbuild.py + create mode 100644 scripts/toil/requirements.txt + create mode 100644 scripts/toil/tests_pkgbuild.py + +diff --git a/packages/rhel7/glibc/glibc-2.17-55.el7/info b/packages/rhel7/glibc/glibc-2.17-55.el7/info +index 9b13a12..8cebabb 100644 +--- a/packages/rhel7/glibc/glibc-2.17-55.el7/info ++++ b/packages/rhel7/glibc/glibc-2.17-55.el7/info +@@ -72,7 +72,16 @@ _install_originals() { + } + + _run_tests() { +- export LD_PRELOAD=/libcare/tests/execve/execve.so ++ if test -f $LIBCARE_DIR/execve/execve.so; then ++ LD_PRELOAD=$LIBCARE_DIR/execve/execve.so ++ elif test -f $LIBCARE_DIR/tests/execve/execve.so; then ++ LD_PRELOAD=$LIBCARE_DIR/tests/execve/execve.so ++ else ++ echo "Can't find execve.so required to run tests" ++ exit 1 ++ fi ++ ++ export LD_PRELOAD + + local ld_linux="$KP_PROJECT_BUILD_DIR/elf/ld*" + +@@ -110,9 +119,11 @@ kp_patch_test() { + rm -f /var/run/libcare.sock + + PATCH_ROOT=/root/${KP_PROJECT_PATCH%.*} +- /libcare/src/libcare-ctl -v server /var/run/libcare.sock $PATCH_ROOT \ +- >/root/test.log 2>&1 & : ++ $KPATCH_PATH/libcare-ctl -v server /var/run/libcare.sock $PATCH_ROOT \ ++ >/data/test.log 2>&1 & : + LISTENER_PID=$! ++ sleep 1 ++ kill -0 $LISTENER_PID + + pushd $KP_PROJECT_BUILD_DIR + +@@ -123,11 +134,11 @@ kp_patch_test() { + + popd + +- local patched=$(awk '/kpatch_ctl targeting/ { n++ } END { print n }' /root/test.log) ++ local patched=$(awk '/kpatch_ctl targeting/ { n++ } END { print n }' /data/test.log) + + test $patched -ge $executed + +- grep -vq 'No patch(es) applicable to' /root/test.log +- grep 'patch hunk(s) have been successfully applied' /root/test.log \ ++ grep -vq 'No patch(es) applicable to' /data/test.log ++ grep 'patch hunk(s) have been successfully applied' /data/test.log \ + | wc -l + } +diff --git a/packages/rhel7/glibc/glibc-2.17-55.el7/pkgfile.yaml b/packages/rhel7/glibc/glibc-2.17-55.el7/pkgfile.yaml +new file mode 100644 +index 0000000..51028ed +--- /dev/null ++++ b/packages/rhel7/glibc/glibc-2.17-55.el7/pkgfile.yaml +@@ -0,0 +1,5 @@ ++image: kernelcare/centos7:gcc-4.8.2-16.el7 ++prebuild: /tmp/build.orig-glibc-2.17-55.el7.x86_64.rpm.tgz ++input: ++- package: . ++- patches: ../../../../patches/ +diff --git a/scripts/pkgbuild b/scripts/pkgbuild +index e2ac7c7..8803db3 100755 +--- a/scripts/pkgbuild ++++ b/scripts/pkgbuild +@@ -54,8 +54,9 @@ prepare() { + + # Export env vars that are needed during the build + SCRIPTS="$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" +- SPWD="$(realpath $SCRIPTS/../)" +- export KPATCH_PATH=$SPWD/src ++ LIBCARE_DIR="${LIBCARE_DIR:-$SCRIPTS/..}" ++ KPATCH_PATH="${KPATCH_PATH:-$LIBCARE_DIR/src}" ++ export LIBCARE_DIR KPATCH_PATH + export OLDPATH=$PATH + export KPATCH_PASSTHROUGH_ASM=1 + CPUS=`cat /proc/cpuinfo | grep ^processor | wc -l` +@@ -130,18 +131,20 @@ kp_prepare_source_deb() { + } + + kp_prepare_source() { +- echo " downloading source for $KP_PROJECT" +- kp_download_source_$KP_PROJECT_FORMAT ++ if ! test -f /kcdata/$KP_PROJECT_SOURCE; then ++ echo " downloading source for $KP_PROJECT" ++ kp_download_source_$KP_PROJECT_FORMAT ++ fi + echo " preparing source for $KP_PROJECT" + kp_prepare_source_$KP_PROJECT_FORMAT + } + + kp_patch_source() { + echo " patching project" +- PATCH_DIR=$SPWD/patches ++ PATCH_DIR=$LIBCARE_DIR/patches + #patch_list_apply requires this dir + mkdir -p /tmp/build.kpatch +- $SPWD/scripts/patch_list_apply $KP_PROJECT_DIR $PDIR/plist $PATCH_DIR ++ $SCRIPTS/patch_list_apply $KP_PROJECT_DIR $PDIR/plist $PATCH_DIR + } + + kp_prebuild_rpm() { +@@ -364,10 +367,10 @@ overwrite_utils() { + TMPBIN=$(mktemp -d --tmpdir) + + mkdir $TMPBIN/bin +- ln -fs /libcare/src/libcare-cc $TMPBIN/gcc +- ln -fs /libcare/src/libcare-cc $TMPBIN/cc +- ln -fs /libcare/src/libcare-cc $TMPBIN/g++ +- ln -fs /libcare/src/libcare-cc $TMPBIN/c++ ++ ln -fs $KPATCH_PATH/libcare-cc $TMPBIN/gcc ++ ln -fs $KPATCH_PATH/libcare-cc $TMPBIN/cc ++ ln -fs $KPATCH_PATH/libcare-cc $TMPBIN/g++ ++ ln -fs $KPATCH_PATH/libcare-cc $TMPBIN/c++ + if ! test -x /usr/bin/g++; then + rm -f $TMPBIN/g++ + fi +@@ -379,6 +382,11 @@ overwrite_utils() { + fi + + export PATH=$TMPBIN:$PATH ++ ++ if test "$(command -v cc)" != "$TMPBIN/cc"; then ++ echo "Can't install our wrappers, missing?" ++ exit 1 ++ fi + } + + kp_patch_test() { +diff --git a/scripts/toil/README.md b/scripts/toil/README.md +new file mode 100644 +index 0000000..5bc77b6 +--- /dev/null ++++ b/scripts/toil/README.md +@@ -0,0 +1 @@ ++This is where [toil](https://toil.readthedocs.io/)-based scripts do live. +diff --git a/scripts/toil/build-patch.sh b/scripts/toil/build-patch.sh +new file mode 100755 +index 0000000..0e12de4 +--- /dev/null ++++ b/scripts/toil/build-patch.sh +@@ -0,0 +1,18 @@ ++#!/bin/sh -e ++ ++exec 1>&2 ++ ++ln -fs /data /kcdata ++ ++ls -lR /data ++ ++yum install -y rpm-build ++ ++LIBCARE_DIR="/data" ++KPATCH_PATH="/data/src" ++export LIBCARE_DIR KPATCH_PATH ++make -C $KPATCH_PATH clean all ++make -C /data/execve clean all ++ ++/kcdata/scripts/pkgbuild $@ /kcdata/package ++ls /kcdata -lR +diff --git a/scripts/toil/pkgbuild.py b/scripts/toil/pkgbuild.py +new file mode 100755 +index 0000000..c4b009a +--- /dev/null ++++ b/scripts/toil/pkgbuild.py +@@ -0,0 +1,894 @@ ++#!/usr/bin/python ++""" ++Build package patches for libcare using toil[1] workflow framework. ++The basic components are the following: ++ ++#. `Storage` class that works like a filesystem directory. Changes made to it ++ are passed down the chain to all the jobs via `Promise` mechanism. The ++ `Storage` class is also responsible for importing and exporting local files. ++ ++#. `FileFetcherJob` job that downloads specified files from the net and puts them in ++ the storage. Supported are AWS S3, HTTP and FTP files. This is usually the ++ first job. ++ ++#. `DoBuild` checks presence of the object in the Storage and runs ++ `prebuildJob` chained with `uploadJob` and `buildJob` if the object is missing. ++ Only `buildJob` is run otherwise. ++ ++ This is used to build missing parts such as an archive with the baseline ++ source code called `prebuilt` which is listed as optional for the ++ `FileFetcherJob` job. ++ ++#. `DockerScriptJob` that brings up a Docker container from the specified image ++ and runs the Storage-suplied script with Storage content exposed into the ++ `/data` directory. ++ ++#. `UploadJob` that uploads specified objects from the Storage. ++ ++#. `DoIfMissing` checks presence of the object in the Storage and runs ++ specified job if it is missing. ++ ++#. `ChainJobs` is used to chain the jobs at the runtime. ++ ++ ++ ++All the updates to the Storage are carried from the children to the followOns ++via `Promise` mechanism. The root job returns Storage from the last followOn, ++this storage is then exported to a directory. ++ ++[1] https://toil.readthedocs.io/ ++""" ++ ++from __future__ import print_function ++ ++import boto3 ++import contextlib ++import collections ++import datetime ++import dateutil ++import errno ++import logging ++import os ++import requests ++import shutil ++import tarfile ++import tempfile ++import time ++import urlparse ++import yaml ++ ++from toil.common import Toil ++from toil.job import Job as toilJob ++from toil.lib.docker import dockerCall, dockerCheckOutput, STOP, RM, FORGO ++ ++ ++def myabspath(path_or_url, root=None): ++ """Return absolute path for an url or path.""" ++ if '://' not in path_or_url or path_or_url.startswith('file://'): ++ path_or_url = path_or_url.replace('file://', '') ++ if (path_or_url[0] == '.' or '/' not in path_or_url) and root: ++ path_or_url = os.path.join(root, path_or_url) ++ path_or_url = os.path.abspath(path_or_url) ++ return path_or_url ++ return ++ ++FileInfo = collections.namedtuple('FileInfo', ('fileName', 'url', 'required')) ++ ++def parseFileInfo(fileInfo): ++ """Parse fileInfo which is either an URL or a dictionary of form ++ {"fileName": "url"}. When url is prefixed with '*' the file is optional. ++ ++ Samples: ++ >>> parseFileInfo('http://kernel.org/index.html') ++ ... "index.html", "http://kernel.org/index.html", True ++ >>> parseFileInfo('*http://kernel.org/index.html') ++ ... "index.html", "http://kernel.org/index.html", False ++ >>> parseFileInfo({'foobar.html': '*http://kernel.org/index.html'}) ++ ... "foobar.html", "http://kernel.org/index.html", False ++ """ ++ if isinstance(fileInfo, basestring): ++ url = fileInfo ++ fileName = os.path.basename(url) ++ elif isinstance(fileInfo, dict): ++ items = fileInfo.items() ++ if len(items) != 1: ++ raise ValueError("only one-entry dicts are allowed as fileInfo") ++ fileName, url = items[0] ++ ++ required = True ++ if url.startswith('*'): ++ url = url.lstrip('*') ++ required = False ++ return FileInfo(fileName, url, required) ++ ++ ++class WorkflowToFileStoreProxy(object): ++ """ ++ Make File.copy and Directory.copy work with toil.common.Toil object. ++ """ ++ def __init__(self, workflow): ++ self.workflow = workflow ++ ++ def readGlobalFile(self, fileId): ++ tmpfile = tempfile.mktemp() ++ self.workflow.exportFile(fileId, 'file://' + tmpfile) ++ return tmpfile ++ ++ def writeGlobalFile(self, path): ++ return self.workflow.importFile('file:///' + path) ++ ++ def getLocalTempFile(self): ++ return tempfile.mktemp() ++ ++ ++class Storage(dict): ++ """Keeps files organized for the workflow. ++ ++ This is a dictionary that associates fileName with a file object such ++ as `File` or `Directory` class. ++ ++ Additional field `promised_updates` is used to keep track of updates ++ resulting from `Promise`s of children. ++ """ ++ def __init__(self, *args, **kwargs): ++ self.promised_updates = [] ++ if args: ++ self.promised_updates = list(args[0].promised_updates) ++ ++ super(Storage, self).__init__(*args, **kwargs) ++ ++ def update(self, other): ++ """Update this Storage with files from another.""" ++ if isinstance(other, Storage): ++ self.promised_updates.extend(other.promised_updates) ++ ++ if isinstance(other, dict): ++ super(Storage, self).update(other) ++ else: ++ self.promised_updates.append(other) ++ ++ def __setstate__(self, state): ++ """Unfold finished promised_updates.""" ++ self.promised_updates = [] ++ for promise in state['promised_updates']: ++ self.update(promise) ++ ++ def __repr__(self): ++ s = super(Storage, self).__repr__() ++ return s + (" %r" % self.promised_updates) ++ ++ @contextlib.contextmanager ++ def expose(self, rootDir=None): ++ """Expose Storage to a directory and pick up changes from it.""" ++ if rootDir is None: ++ rootDir = self.fileStore.getLocalTempDir() ++ try: ++ self.toDirectory(rootDir) ++ yield rootDir ++ finally: ++ self.cleanupDirectory(rootDir) ++ self.pickupDirectory(rootDir) ++ ++ def toDirectory(self, rootDir, export=False): ++ """Copy Storage content to the specified directory.""" ++ for fileObj in self.values(): ++ if export and isinstance(fileObj, (ImportedFile, ImportedDirectory)): ++ continue ++ fileObj.copy(self.fileStore, rootDir) ++ ++ def pickupDirectory(self, directory): ++ """Pick up content of the specified directory into the Storage.""" ++ files = ((name, os.path.join(directory, name)) ++ for name in os.listdir(directory)) ++ return self.pickupFiles(self.fileStore, files) ++ ++ def cleanupDirectory(self, rootDir): ++ """Remove storage files from the directory.""" ++ return ++ for fileName in self: ++ path = os.path.join(rootDir, fileName) ++ ++ if os.path.isdir(path): ++ shutil.rmtree(path) ++ else: ++ os.remove(path) ++ ++ def pickupFiles(self, fileStore, files): ++ """Pick up listed files into the Storage.""" ++ for fileName, path in files: ++ if fileName in self: ++ continue ++ ++ if os.path.isdir(path): ++ self[fileName] = Directory.fromFiles( ++ fileStore, [(fileName, path)]) ++ else: ++ self[fileName] = File.fromLocalFile( ++ fileStore, fileName, path) ++ ++ def importLocalFiles(self, workflow, inputs, rootDir=None): ++ """Import local files at the start of the workflow. ++ ++ This is usually a first step and is done: ++ >>> with Toil(options) as toil: ++ ... storage = Storage.importLocalFiles( ++ ... toil, ++ ... ['localfile.tar', 'secondfile', 'ascript.sh']) ++ ... rootJob = RootJob(storage) ++ """ ++ ++ localFiles = [] ++ remoteFiles = [] ++ ++ for fileInfo in inputs: ++ ++ fileName, url, required = parseFileInfo(fileInfo) ++ ++ path = myabspath(url, rootDir) ++ if path: ++ localFiles.append((fileName, path)) ++ else: ++ remoteFiles.append(fileInfo) ++ ++ for fileName, path in localFiles: ++ if fileName in self: ++ continue ++ ++ if os.path.isdir(path): ++ self[fileName] = ImportedDirectory.fromFiles( ++ workflow, fileName, [(fileName, path)]) ++ else: ++ try: ++ self[fileName] = ImportedFile.fromLocalFile( ++ workflow, fileName, path) ++ except IOError as ioerr: ++ if ioerr.errno != errno.ENOENT: ++ raise ++ ++ print("imported localFiles %s" % (", ".join(self))) ++ ++ inputs[:] = remoteFiles ++ ++ return self ++ ++ @classmethod ++ def fromLocalFiles(cls, workflow, inputs, rootDir=None): ++ storage = cls() ++ storage.importLocalFiles(workflow, inputs, rootDir) ++ return storage ++ ++ def exportLocalFiles(self, workflow, outputDir): ++ """Export local files at the end of workflow. ++ ++ This is the last step: ++ >>> with Toil(options) as toil: ++ ... ... ++ ... storage = toil.start(rootJob) ++ ... storage.exportLocalFiles(toil, 'outputDir') ++ """ ++ try: ++ self.fileStore = WorkflowToFileStoreProxy(workflow) ++ return self.toDirectory(outputDir, export=True) ++ finally: ++ del self.fileStore ++ ++ ++class File(object): ++ """A file in Storage.""" ++ def __init__(self, fileName, fileId, mode=None): ++ super(File, self).__init__() ++ self.fileName = fileName ++ self.fileId = fileId ++ self.mode = mode ++ ++ def copy(self, fileStore, dest): ++ localCopy = self.localCopy(fileStore) ++ if os.path.isdir(dest): ++ dest = os.path.join(dest, self.fileName) ++ shutil.copy(localCopy, dest) ++ if self.mode: ++ os.chmod(dest, self.mode) ++ ++ def localCopy(self, fileStore): ++ return fileStore.readGlobalFile(self.fileId) ++ ++ @contextlib.contextmanager ++ def open(self, fileStore): ++ localFile = self.localCopy(fileStore) ++ with open(localFile) as fh: ++ yield fh ++ ++ @classmethod ++ def fromLocalFile(cls, fileStore, fileName, path): ++ return cls(fileName, fileStore.writeGlobalFile(path), ++ mode=os.stat(path).st_mode) ++ ++ def __str__(self): ++ return "" % (self.fileName, self.fileId) ++ ++ __repr__ = __str__ ++ ++ ++class ImportedFile(object): ++ def __init__(self, fileName, mode=None): ++ super(ImportedFile, self).__init__() ++ self.fileName = fileName ++ self.mode = mode ++ ++ def copy(self, fileStore, dest): ++ if os.path.isdir(dest): ++ dest = os.path.join(dest, self.fileName) ++ with self.open(fileStore) as fhin: ++ with open(dest, 'w') as fhout: ++ shutil.copyfileobj(fhin, fhout) ++ if self.mode: ++ os.chmod(dest, self.mode) ++ ++ def localCopy(self, fileStore): ++ fileName = fileStore.getLocalTempFile() ++ self.copy(fileStore, fileName) ++ return fileName ++ ++ @contextlib.contextmanager ++ def open(self, fileStore): ++ with fileStore.jobStore.readSharedFileStream(self.fileName) as fh: ++ yield fh ++ ++ @classmethod ++ def fromLocalFile(cls, workflow, fileName, path): ++ workflow.importFile('file://' + path, sharedFileName=fileName) ++ ++ return cls( ++ fileName, ++ mode=os.stat(path).st_mode) ++ ++ def __str__(self): ++ return "" % self.fileName ++ ++ __repr__ = __str__ ++ ++ ++class Directory(object): ++ """A directory in Storage. Kept as an archive file.""" ++ def __init__(self, fileId): ++ super(Directory, self).__init__() ++ self.fileId = fileId ++ ++ def localCopy(self, fileStore, outDir=None): ++ localDir = outDir or fileStore.getLocalTempDir() ++ localFile = fileStore.readGlobalFile(self.fileId) ++ ++ tar = tarfile.TarFile.taropen(localFile) ++ tar.extractall(path=localDir) ++ ++ return localDir ++ ++ def copy(self, fileStore, dest): ++ self.localCopy(fileStore, dest) ++ ++ @classmethod ++ def fromFiles(cls, fileStore, files): ++ ++ with fileStore.writeGlobalFileStream() as (fh, fileId): ++ tar = tarfile.TarFile.open(fileobj=fh, mode='w') ++ ++ for fileName, path in files: ++ tar.add(path, arcname=fileName) ++ ++ return cls(fileId) ++ ++ def __str__(self): ++ return "" % self.fileId ++ ++ __repr__ = __str__ ++ ++ ++class ImportedDirectory(Directory): ++ ++ def localCopy(self, fileStore, outDir=None): ++ localDir = outDir or fileStore.getLocalTempDir() ++ ++ with fileStore.jobStore.readSharedFileStream(self.fileId) as fh: ++ tar = tarfile.TarFile(fileobj=fh, mode='r') ++ tar.extractall(path=localDir) ++ ++ return localDir ++ ++ @classmethod ++ def fromFiles(cls, workflow, dirName, files): ++ tmpfile = tempfile.mktemp() ++ ++ tar = tarfile.TarFile.open(tmpfile, mode='w') ++ ++ for fileName, path in files: ++ tar.add(path, arcname=fileName) ++ ++ tar.close() ++ ++ workflow.importFile('file://' + tmpfile, sharedFileName=dirName) ++ os.unlink(tmpfile) ++ ++ return cls(dirName) ++ ++ ++class IncorrectStorageUse(Exception): ++ message = "Child `Job`s with storage added outside of running parent " \ ++ "wont influence parent's storage. If this is not required " \ ++ "consider using addChildNoStorage and setting child's " \ ++ "storage via `self._storage = child.resultStorage`." ++ ++ def __init__(self): ++ super(IncorrectStorageUse, self).__init__(self.message) ++ ++ ++class Job(toilJob): ++ """Job with a Storage. ++ ++ This Job passes Storage down the workflow via `Promise` mechanism. ++ Updates to Storage from the children are merged and passed to the ++ followOns. ++ """ ++ ++ _running = False ++ def __init__(self, *args, **kwargs): ++ super(Job, self).__init__(*args, **kwargs) ++ self._storage = Storage() ++ self._childStorage = Storage() ++ ++ def rv(self, *path): ++ return super(Job, self).rv('rv', *path) ++ ++ @property ++ def storage(self): ++ if self._running: ++ return self._storage ++ else: ++ return super(Job, self).rv('storage') ++ ++ @property ++ def resultStorage(self): ++ return super(Job, self).rv('childStorage') ++ ++ def _updateStorage(self, succ, promise, ourStorage): ++ if not isinstance(succ, Job): ++ return ++ ++ succ._storage = promise ++ ++ if ourStorage is not None: ++ if self._running: ++ ourStorage.update(succ.storage) ++ else: ++ raise IncorrectStorageUse() ++ ++ def addChild(self, child): ++ """Add a child and update storage with it's promised storage""" ++ rv = super(Job, self).addChild(child) ++ self._updateStorage(child, self.storage, self._childStorage) ++ return rv ++ ++ def addFollowOn(self, followOn): ++ "Add a followOn. FollowOns receive storages with updates from children" ++ rv = super(Job, self).addFollowOn(followOn) ++ self._updateStorage(followOn, self.resultStorage, None) ++ return rv ++ ++ def addChildNoStorage(self, child): ++ """Add a child that won't contribute to the Storage. ++ ++ Typically used to add a child whose Storage will be used as a ++ predecessor' result. ++ """ ++ rv = super(Job, self).addChild(child) ++ self._updateStorage(child, self.storage, None) ++ return rv ++ ++ def _run(self, jobGraph, fileStore): ++ ++ self._running = True ++ ++ storage = self.storage ++ storage.fileStore = fileStore ++ ++ rv = super(Job, self)._run(jobGraph, fileStore) ++ ++ try: ++ del storage.fileStore ++ except AttributeError: ++ pass ++ self._running = False ++ ++ self._childStorage.update(self._storage) ++ ++ return { ++ 'rv': rv, ++ 'storage': self._storage, ++ 'childStorage': self._childStorage ++ } ++ ++ ++class NoSuchFile(Exception): ++ pass ++ ++ ++class FileFetcherJob(Job): ++ """Fetches files from the Web into the Storage. ++ ++ Accepts a list `fileInfo`s to download. There must be no local filesystem ++ files. Instead, fetcher should be primed with a Storage having them ++ imported via `importLocalFiles`. ++ """ ++ def __init__(self, files, storage=None, localRootDir=None): ++ super(FileFetcherJob, self).__init__(cores=1, memory="256M") ++ ++ if storage is not None: ++ self._storage = storage ++ ++ self.files = files ++ if localRootDir is None: ++ localRootDir = os.path.dirname(os.path.realpath(__file__)) ++ self.localRootDir = localRootDir ++ ++ remoteFiles = self.remoteFiles = [] ++ localFiles = [] ++ ++ for fileInfo in self.files: ++ ++ fileName, url, required = parseFileInfo(fileInfo) ++ if fileName in self._storage: ++ continue ++ ++ path = myabspath(url, self.localRootDir) ++ if path: ++ localFiles.append((fileName, path)) ++ else: ++ remoteFiles.append((fileName, url, required)) ++ ++ if localFiles: ++ raise Exception("Non-empty localFiles, did you forgot to importLocalFiles: %s?" % localFiles) ++ ++ ++ def run(self, fileStore): ++ ++ for fileInfo in self.remoteFiles: ++ ++ fileName, url, required = fileInfo ++ if fileName in self.storage: ++ continue ++ ++ try: ++ if url.startswith('s3://'): ++ child = S3DownloadJob(fileName, url) ++ else: ++ child = DownloadJob(fileName, url) ++ self.addChild(child) ++ except NoSuchFile: ++ if required: ++ raise ++ ++ ++class DownloadJob(Job): ++ """Download a HTTP or FTP file.""" ++ def __init__(self, fileName, url): ++ self.url = url ++ ++ request = requests.get(self.url, stream=True) ++ if request.status_code != 200: ++ raise NoSuchFile(self.url) ++ ++ disk = request.headers.get('Content-Length', '1G') ++ request.close() ++ self.fileName = fileName ++ ++ Job.__init__(self, unitName=url, memory="128M", cores=1, disk=disk) ++ ++ def run(self, fileStore): ++ r = requests.get(self.url, stream=True) ++ ++ with fileStore.writeGlobalFileStream() as (fh, fileId): ++ for chunk in r.iter_content(4096): ++ fh.write(chunk) ++ self.storage[self.fileName] = File(self.fileName, fileId) ++ ++ ++_CLIENT = None ++class S3FileJob(Job): ++ ++ def parseurl(self, desturl): ++ url = urlparse.urlparse(desturl) ++ if url.scheme != "s3": ++ raise Exception("URL %s is not S3 url" % desturl) ++ ++ self.url = url ++ self.bucket = self.url.netloc ++ self.key = self.url.path[1:] ++ self.desturl = desturl ++ ++ def client(cls): ++ ++ global _CLIENT ++ if _CLIENT is None: ++ _CLIENT = boto3.client('s3') ++ return _CLIENT ++ ++ ++class S3DownloadJob(S3FileJob): ++ """Download a file from AWS S3.""" ++ def __init__(self, fileName, url): ++ self.parseurl(url) ++ ++ try: ++ self.obj = self.client().get_object(Bucket=self.bucket, Key=self.key) ++ except self.client().exceptions.NoSuchKey: ++ raise NoSuchFile(url) ++ ++ self.fileName = fileName ++ super(S3DownloadJob, self).__init__( ++ memory="1M", cores=1, unitName="download %s" % url, ++ disk=self.obj['ContentLength']) ++ ++ def run(self, fileStore): ++ with fileStore.writeGlobalFileStream() as (fh, fileId): ++ self.client().download_fileobj(Bucket=self.bucket, Key=self.key, ++ Fileobj=fh) ++ self.storage[self.fileName] = File(self.fileName, fileId) ++ ++ ++class S3UploadJob(S3FileJob): ++ """Upload a file to AWS S3.""" ++ def __init__(self, fileId, url): ++ self.parseurl(url) ++ self.fileId = fileId ++ ++ super(S3UploadJob, self).__init__(memory="1M", cores=1, unitName="upload %s" % url) ++ ++ ++ def run(self, fileStore): ++ localFile = fileStore.readGlobalFile(self.fileId) ++ self.client().upload_file(localFile, self.bucket, self.key) ++ ++ ++class DockerScriptJob(Job): ++ """Run docker script on top of an exposed Storage.""" ++ ++ def __init__(self, script, image, ++ args=[], cores=4, disk="4G", ++ logfileName="docker-logfile"): ++ unitName = "image={image} script={script} args={args!r}".format( ++ image=image, script=script, args=args) ++ Job.__init__(self, cores=cores, disk=disk, unitName=unitName) ++ ++ self.script = script ++ ++ self.image = image ++ self.args = args ++ self.logfileName = logfileName ++ ++ def run(self, fileStore): ++ ++ e = None ++ with self.storage.expose() as dockerRoot: ++ logfilePath = os.path.join(dockerRoot, self.logfileName) ++ with open(logfilePath, "a") as logfilefh: ++ dockerDefer = fileStore.jobStore.config.dockerDefer ++ ++ dockerParameters = [] ++ if dockerDefer == RM: ++ dockerParameters.append('--rm') ++ ++ dockerParameters += ['-t', '-v', os.path.abspath(dockerRoot) + ':/data'] ++ #dockerParameters += ['--privileged'] ++ dockerParameters += ['--cap-add=SYS_PTRACE'] ++ ++ dockerCall(self, ++ tool=self.image, workDir=dockerRoot, ++ defer=dockerDefer, outfile=logfilefh, ++ parameters=["/data/build"] ++ + self.args, ++ dockerParameters=dockerParameters) ++ ++ return str(e) ++ ++ def __str__(self): ++ name = "build--script=%s--image=%s" % ( ++ os.path.basename(self.script), self.image) ++ return name.replace(':', '-').replace('=', '-').replace('/', '-') ++ ++ ++ ++DEFAULT_SCRIPT = './build-patch.sh' ++DEFAULT_IMAGE = 'centos:centos7' ++ ++UploadInfo = collections.namedtuple('UploadInfo', ('fileName', 'url')) ++ ++class UploadJob(Job): ++ """Upload files from the Storage. ++ ++ URLs passed are `uploadInfo` instances. ++ """ ++ def __init__(self, urls): ++ self.urls = [UploadInfo(*url) for url in urls] ++ ++ super(UploadJob, self).__init__() ++ ++ def run(self, fileStore): ++ for url in self.urls: ++ fileName, url = url ++ ++ fileObj = self.storage.get(fileName) ++ if fileObj is None: ++ continue ++ ++ path = myabspath(url) ++ if not path: ++ self.addChild(S3UploadJob(fileObj.fileId, url)) ++ else: ++ localFile = fileStore.readGlobalFile(fileObj.fileId) ++ shutil.copy(localFile, path) ++ ++ ++class DoBuild(Job): ++ """If prebuild archive is not in storage do a prebuild and upload it to the ++ specified location. Otherwise just do a build.""" ++ ++ def __init__(self, fileName, prebuildJob, uploadJob, buildJob): ++ super(DoBuild, self).__init__(memory="256M") ++ ++ self.fileName = fileName ++ self.prebuildJob = prebuildJob ++ self.buildJob = buildJob ++ self.uploadJob = uploadJob ++ ++ def run(self, fileStore): ++ if self.fileName not in self.storage: ++ self.addChild(self.prebuildJob) ++ ++ self.prebuildJob.addChildNoStorage(self.buildJob) ++ self.prebuildJob.addChildNoStorage(self.uploadJob) ++ else: ++ self.addChild(self.buildJob) ++ ++ self._storage = self.buildJob.storage ++ ++ ++class BuildPatchJob(toilJob): ++ """Root job for patch building. Fetches files and runs Docker jobs. ++ ++ The algorithm is the following: ++ 1. FileFetcherJob(inputs + optional prebuild). ++ ++ 2. If prebuild is there, just run DockerScriptJob with a build job. ++ ++ 3. If prebuild is missing, run DockerScriptJob. Chain UploadJob for it. ++ Start build job DockerScriptJob it. ++ """ ++ def __init__(self, storage, packageDescription): ++ super(BuildPatchJob, self).__init__(memory="256M") ++ ++ self.storage = storage ++ self.packageDescription = packageDescription ++ ++ self.script = packageDescription.get('script', DEFAULT_SCRIPT) ++ self.image = packageDescription.get('image', DEFAULT_IMAGE) ++ ++ self.inputs = list(packageDescription.get('input')) ++ ++ def run(self, fileStore): ++ ++ tail = self ++ ++ fetcher = FileFetcherJob(self.inputs, storage=self.storage) ++ tail.addFollowOn(fetcher) ++ tail = fetcher ++ ++ prebuildUrl = self.packageDescription['prebuild'] ++ prebuildName = os.path.basename(prebuildUrl) ++ ++ prebuildJob = DockerScriptJob( ++ script=self.script, ++ image=self.image, ++ args=['-p'], ++ logfileName="prebuild.log") ++ uploadJob = UploadJob([(prebuildName, prebuildUrl)]) ++ ++ buildJob = DockerScriptJob( ++ script=self.script, ++ image=self.image, ++ logfileName="build.log") ++ ++ ++ doBuild = DoBuild(fileName=prebuildName, prebuildJob=prebuildJob, ++ uploadJob=uploadJob, buildJob=buildJob) ++ tail.addFollowOn(doBuild) ++ tail = doBuild ++ ++ return tail.storage ++ ++ ++def get_default_outputDir(): ++ outputDir = os.path.join(os.getcwd(), 'libcare-output') ++ outputDir = os.path.join(outputDir, datetime.datetime.utcnow().isoformat()) ++ return outputDir ++ ++def readPackageDescription(packageFile): ++ with open(packageFile) as fh: ++ packageDescription = yaml.load(fh) ++ ++ inputs = packageDescription['input'] ++ ++ cwd = os.path.dirname(os.path.realpath(__file__)) ++ ++ script = packageDescription.get( ++ 'script', os.path.join(cwd, DEFAULT_SCRIPT)) ++ ++ inputs.append({'build': script}) ++ inputs.append({'scripts': os.path.join(cwd, '../../scripts')}) ++ inputs.append({'src': os.path.join(cwd, '../../src/')}) ++ inputs.append({'execve': os.path.join(cwd, '../../tests/execve')}) ++ ++ prebuildUrl = packageDescription['prebuild'] ++ if not prebuildUrl.startswith('*'): ++ prebuildUrl = '*' + prebuildUrl ++ inputs.append(prebuildUrl) ++ ++ return packageDescription ++ ++def start(toil): ++ ++ options = toil.options ++ ++ packageDescription = readPackageDescription(options.packageFile) ++ ++ path = myabspath(options.outputDir) ++ if path: ++ print("output directory is", path) ++ options.outputDir = path ++ else: ++ print("output url is", options.outputDir) ++ ++ rootDir = os.path.realpath(os.path.dirname(options.packageFile)) ++ ++ storage = Storage() ++ storage.importLocalFiles(toil, packageDescription['input'], rootDir=rootDir) ++ theJob = BuildPatchJob(storage, packageDescription) ++ ++ return toil.start(theJob) ++ ++def restart(toil): ++ packageDescription = readPackageDescription(toil.options.packageFile) ++ ++ rootDir = os.path.realpath(os.path.dirname(toil.options.packageFile)) ++ ++ Storage.fromLocalFiles(toil, packageDescription['input'], rootDir=rootDir) ++ ++ return toil.restart() ++ ++def main(): ++ parser = Job.Runner.getDefaultArgumentParser() ++ parser.add_argument('packageFile') ++ parser.add_argument('--outputDir', required=False, ++ default=get_default_outputDir()) ++ parser.add_argument('--dockerDefer', required=False, ++ default='RM') ++ options = parser.parse_args() ++ ++ options.disableCaching = True ++ ++ with Toil(options) as toil: ++ toil.config.dockerDefer = globals()[options.dockerDefer.upper()] ++ toil._jobStore.writeConfig() ++ ++ if not toil.options.restart: ++ storage = start(toil) ++ else: ++ storage = restart(toil) ++ ++ try: ++ os.makedirs(options.outputDir) ++ except OSError as e: ++ if e.errno != errno.EEXIST: ++ raise ++ storage.exportLocalFiles(toil, options.outputDir) ++ ++if __name__ == "__main__": ++ main() +diff --git a/scripts/toil/requirements.txt b/scripts/toil/requirements.txt +new file mode 100644 +index 0000000..ea481fa +--- /dev/null ++++ b/scripts/toil/requirements.txt +@@ -0,0 +1,4 @@ ++toil==3.10.1 ++boto3 ++requests ++pyyaml +diff --git a/scripts/toil/tests_pkgbuild.py b/scripts/toil/tests_pkgbuild.py +new file mode 100644 +index 0000000..f60643d +--- /dev/null ++++ b/scripts/toil/tests_pkgbuild.py +@@ -0,0 +1,218 @@ ++#!/usr/bin/python ++ ++import contextlib ++import functools ++import os ++import shutil ++from StringIO import StringIO ++import tempfile ++ ++from toil.common import Toil ++ ++import pkgbuild ++ ++def test_storage_update(): ++ Storage = pkgbuild.Storage ++ ++ storage = Storage(**{'test.sh': 'nothing'}) ++ newStorage = Storage(**{'test2.sh': 'nothing'}) ++ ++ storage.update('promised_value2') ++ newStorage.update('promised_value') ++ ++ newStorage.update(storage) ++ ++ assert newStorage == {'test.sh': 'nothing', 'test2.sh': 'nothing'} ++ assert newStorage.promised_updates == ['promised_value', 'promised_value2'] ++ assert Storage(newStorage) == newStorage ++ ++def test_storage_pickle(): ++ import cPickle ++ Storage = pkgbuild.Storage ++ ++ storage = Storage() ++ storage['abc'] = 'test' ++ storage.update('promised_value') ++ ++ storage = cPickle.loads(cPickle.dumps(storage, protocol=-1)) ++ assert storage['abc'] == 'test' ++ assert storage.promised_updates == ['promised_value'] ++ ++def test_parseFileInfo(): ++ parseFileInfo = pkgbuild.parseFileInfo ++ ++ info = parseFileInfo('http://kernel.org/index.html') ++ assert info == pkgbuild.FileInfo( ++ fileName='index.html', ++ url='http://kernel.org/index.html', ++ required=True) ++ ++ info = parseFileInfo('*http://kernel.org/index.html') ++ assert info == pkgbuild.FileInfo( ++ fileName='index.html', ++ url='http://kernel.org/index.html', ++ required=False) ++ ++ info = parseFileInfo({'foobar.html': 'http://kernel.org/index.html'}) ++ assert info == pkgbuild.FileInfo( ++ fileName='foobar.html', ++ url='http://kernel.org/index.html', ++ required=True) ++ ++ info = parseFileInfo({'foobar.html': '*http://kernel.org/index.html'}) ++ assert info == pkgbuild.FileInfo( ++ fileName='foobar.html', ++ url='http://kernel.org/index.html', ++ required=False) ++ ++ try: ++ info = parseFileInfo({ ++ 'foobar.html': '*http://kernel.org/index.html', ++ 'other': 'things'}) ++ except ValueError: ++ pass ++ else: ++ assert False ++ ++class FakeJobStorage(object): ++ def __init__(self, imported): ++ self.imported = imported ++ ++ @contextlib.contextmanager ++ def readSharedFileStream(self, fileId): ++ buf = fileId ++ if self.imported and fileId in self.imported: ++ buf = self.imported[fileId] ++ yield StringIO(buf) ++ ++class FakeFileStorage(object): ++ def __init__(self, imported=None): ++ self.localTempDir = tempfile.mkdtemp() ++ self.jobStore = FakeJobStorage(imported) ++ ++ def getLocalTempDir(self): ++ return self.localTempDir ++ ++ def readGlobalFile(self, fileId): ++ fileName = tempfile.mktemp() ++ with open(fileName, "w") as fh: ++ print(fileId) ++ fh.write(fileId) ++ return fileName ++ ++ def writeGlobalFile(self, path): ++ with open(path) as fh: ++ return fh.read() ++ ++ @contextlib.contextmanager ++ def writeGlobalFileStream(self): ++ fh_and_fileId = StringIO() ++ yield fh_and_fileId, fh_and_fileId ++ ++class FakeWorkflow(object): ++ def __init__(self): ++ self.imported = {} ++ ++ def importFile(self, path, sharedFileName): ++ self.imported[sharedFileName] = slurp(path.replace('file://', '')) ++ ++def slurp(fileName): ++ return open(fileName).read() ++ ++def test_storage(): ++ storage = pkgbuild.Storage( ++ **{ ++ 'file.txt': pkgbuild.File('file.txt', 'some content'), ++ 'imported.txt': pkgbuild.ImportedFile('imported.txt') ++ } ++ ) ++ storage.fileStore = FakeFileStorage() ++ ++ with storage.expose() as dirname: ++ ++ assert set(os.listdir(dirname)) == set(['file.txt', 'imported.txt']) ++ ++ assert slurp(os.path.join(dirname, 'file.txt')) == 'some content' ++ assert slurp(os.path.join(dirname, 'imported.txt')) == 'imported.txt' ++ ++ with open(os.path.join(dirname, 'newfile.txt'), 'w') as fh: ++ fh.write('and some content here') ++ ++ ++ assert storage['newfile.txt'].fileId == 'and some content here' ++ ++ with storage.expose() as dirname: ++ ++ ++ newdir = os.path.join(dirname, 'testdir') ++ os.mkdir(newdir) ++ with open(os.path.join(newdir, 'subfile'), "w") as fh: ++ fh.write('foobar') ++ ++ inputs = os.listdir(dirname) + ['http://somefile.com/index.html'] ++ workflow = FakeWorkflow() ++ newStorage = pkgbuild.Storage.fromLocalFiles( ++ workflow, inputs, dirname) ++ assert inputs == ['http://somefile.com/index.html'] ++ ++ ++ assert isinstance(newStorage['testdir'], pkgbuild.ImportedDirectory) ++ ++ newStorage.fileStore = FakeFileStorage(workflow.imported) ++ with newStorage.expose() as dirname: ++ ++ assert slurp(os.path.join(dirname, 'file.txt')) == 'some content' ++ assert slurp(os.path.join(dirname, 'imported.txt')) == 'imported.txt' ++ assert slurp(os.path.join(dirname, 'newfile.txt')) == 'and some content here' ++ assert slurp(os.path.join(dirname, 'testdir', 'subfile')) == 'foobar' ++ ++ ++class DummyStorageUpdate(pkgbuild.Job): ++ def __init__(self, filename): ++ super(DummyStorageUpdate, self).__init__(unitName=filename) ++ self.filename = filename ++ ++ def run(self, fileStore): ++ self.storage[self.filename] = 'something' ++ ++class DummyStorageAsChild(pkgbuild.Job): ++ def run(self, fileStore): ++ a = DummyStorageUpdate('bar') ++ self.addChild(a) ++ ++class RootJob(pkgbuild.Job): ++ def __init__(self, storage): ++ super(RootJob, self).__init__() ++ self._storage = storage ++ ++ def run(self, fileStore): ++ ++ tail = self ++ ++ a = DummyStorageUpdate('foo') ++ self.addChild(a) ++ tail = a ++ ++ b = DummyStorageAsChild() ++ tail.addFollowOn(b) ++ tail = b ++ ++ return tail.resultStorage ++ ++def test_job_storage(): ++ parser = pkgbuild.Job.Runner.getDefaultArgumentParser() ++ options = parser.parse_args(['test_job_storage']) ++ ++ options.workDir = tempfile.mkdtemp() ++ options.jobStore = os.path.join(options.workDir, 'workdir') ++ ++ storage = pkgbuild.Storage(foobar='here i go') ++ root = RootJob(storage=storage) ++ ++ try: ++ with Toil(options) as toil: ++ storage = toil.start(root) ++ finally: ++ shutil.rmtree(options.workDir) ++ ++ assert storage == {'foo': 'something', 'bar': 'something', 'foobar': 'here i go'} +-- +2.23.0 + diff --git a/0008-pkgbuild-use-yumdownloader-if-source-url-is-missing.patch b/0008-pkgbuild-use-yumdownloader-if-source-url-is-missing.patch new file mode 100644 index 0000000..12416ff --- /dev/null +++ b/0008-pkgbuild-use-yumdownloader-if-source-url-is-missing.patch @@ -0,0 +1,30 @@ +From 066da27aaf6b153c215bee353e7190fe226f5b3b Mon Sep 17 00:00:00 2001 +From: Pavel Boldin +Date: Thu, 14 Dec 2017 18:02:15 +0200 +Subject: [PATCH 08/89] pkgbuild: use yumdownloader if source url is missing + +Signed-off-by: Pavel Boldin +--- + scripts/pkgbuild | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/scripts/pkgbuild b/scripts/pkgbuild +index 8803db3..97c78a9 100755 +--- a/scripts/pkgbuild ++++ b/scripts/pkgbuild +@@ -105,7 +105,11 @@ kp_prepare_source_raw() { + + kp_download_source_rpm() { + mkdir -p /kcdata +- curl $KP_PROJECT_SOURCE_URL -o /kcdata/$KP_PROJECT_SOURCE ++ if test -n "$KP_PROJECT_SOURCE_URL"; then ++ curl $KP_PROJECT_SOURCE_URL -o /kcdata/$KP_PROJECT_SOURCE ++ else ++ yumdownloader --source --destdir /kcdata ${KP_PROJECT_SOURCE%.src.rpm} ++ fi + } + + kp_prepare_source_rpm() { +-- +2.23.0 + diff --git a/0009-execve-abort-on-failure.patch b/0009-execve-abort-on-failure.patch new file mode 100644 index 0000000..df6707d --- /dev/null +++ b/0009-execve-abort-on-failure.patch @@ -0,0 +1,52 @@ +From 2343fef023a8cd64473161fa82a0d81cb5007cf9 Mon Sep 17 00:00:00 2001 +From: Pavel Boldin +Date: Tue, 30 Jan 2018 00:04:54 +0200 +Subject: [PATCH 09/89] execve: abort() on failure + +Signed-off-by: Pavel Boldin +--- + tests/execve/execve.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/tests/execve/execve.c b/tests/execve/execve.c +index b3df0e4..eb3e9d3 100644 +--- a/tests/execve/execve.c ++++ b/tests/execve/execve.c +@@ -81,7 +81,7 @@ notify_listener(void) + sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sock == -1) { + dprintf("socket() error: %s(%d)\n", strerror(errno), errno); +- return; ++ abort(); + } + dprintf("socket()\n"); + +@@ -96,7 +96,7 @@ notify_listener(void) + if (rv == -1) { + fprintf(stderr, "libcare-execve: connect() error: %s(%d)\n", strerror(errno), errno); + (void) close(sock); +- return; ++ abort(); + } + dprintf("connect()\n"); + +@@ -113,7 +113,7 @@ notify_listener(void) + if (rv == -1) { + fprintf(stderr, "send() error: %s(%d)\n", strerror(errno), errno); + (void) close(sock); +- return; ++ abort(); + } + dprintf("send()\n"); + +@@ -123,6 +123,7 @@ notify_listener(void) + + if (rv == -1) { + fprintf(stderr, "recv() error: %s(%d)\n", strerror(errno), errno); ++ abort(); + } + dprintf("recv()\n"); + +-- +2.23.0 + diff --git a/0010-Add-test-stage-to-pkgbuild.patch b/0010-Add-test-stage-to-pkgbuild.patch new file mode 100644 index 0000000..55f72dd --- /dev/null +++ b/0010-Add-test-stage-to-pkgbuild.patch @@ -0,0 +1,379 @@ +From 7d431211063a9bf6c789bb67a2ed216025279a66 Mon Sep 17 00:00:00 2001 +From: Roman Rashchupkin +Date: Tue, 23 Jan 2018 13:41:38 +0300 +Subject: [PATCH 10/89] Add --test stage to pkgbuild + +Signed-off-by: Roman Rashchupkin +--- + packages/rhel7/glibc/glibc-2.17-55.el7/info | 36 ++++----- + .../glibc/glibc-2.17-55.el7/pkgfile.yaml | 1 + + scripts/pkgbuild | 81 ++++++++++++++++--- + scripts/toil/build-patch.sh | 1 - + scripts/toil/pkgbuild.py | 57 +++++++++---- + 5 files changed, 126 insertions(+), 50 deletions(-) + +diff --git a/packages/rhel7/glibc/glibc-2.17-55.el7/info b/packages/rhel7/glibc/glibc-2.17-55.el7/info +index 8cebabb..e5805c3 100644 +--- a/packages/rhel7/glibc/glibc-2.17-55.el7/info ++++ b/packages/rhel7/glibc/glibc-2.17-55.el7/info +@@ -55,21 +55,6 @@ kp_build_hook() { + $KP_PROJECT_BUILD_ROOT/SPECS/$KP_PROJECT_SPEC + } + +-# Replace patch build results with original libraries for testing +-_install_originals() { +- eval set -- $KP_INSTALL_FILES +- while test -n "$1"; do +- local buildpath="$1" +- local installpath="$2" +- shift 2 +- +- if test "$installpath" = "IGNORE"; then +- continue +- fi +- +- /bin/cp -r /root/root.original/$installpath $KP_PROJECT_BUILD_DIR/$buildpath +- done +-} + + _run_tests() { + if test -f $LIBCARE_DIR/execve/execve.so; then +@@ -113,15 +98,22 @@ _run_tests() { + export PATH=$KCPATH + } + +-kp_patch_test() { +- _install_originals ++kp_prepare_test_binaries() { ++ # Replace patch build results with original libraries for testing ++ kp_install_files /root/root.original \ ++ $KP_PROJECT_BUILD_DIR \ ++ "to_prebuild" \ ++ "$KP_INSTALL_FILES" ++} + ++kp_patch_test() { + rm -f /var/run/libcare.sock + +- PATCH_ROOT=/root/${KP_PROJECT_PATCH%.*} ++ PATCH_ROOT=$KP_PROJECT_BUILD_ROOT/storage + $KPATCH_PATH/libcare-ctl -v server /var/run/libcare.sock $PATCH_ROOT \ +- >/data/test.log 2>&1 & : ++ >/data/libcare-ctl.log 2>&1 & : + LISTENER_PID=$! ++ + sleep 1 + kill -0 $LISTENER_PID + +@@ -134,11 +126,11 @@ kp_patch_test() { + + popd + +- local patched=$(awk '/kpatch_ctl targeting/ { n++ } END { print n }' /data/test.log) ++ local patched=$(awk '/kpatch_ctl targeting/ { n++ } END { print n }' /data/libcare-ctl.log) + + test $patched -ge $executed + +- grep -vq 'No patch(es) applicable to' /data/test.log +- grep 'patch hunk(s) have been successfully applied' /data/test.log \ ++ grep -vq 'No patch(es) applicable to' /data/libcare-ctl.log ++ grep 'patch hunk(s) have been successfully applied' /data/libcare-ctl.log \ + | wc -l + } +diff --git a/packages/rhel7/glibc/glibc-2.17-55.el7/pkgfile.yaml b/packages/rhel7/glibc/glibc-2.17-55.el7/pkgfile.yaml +index 51028ed..b17e895 100644 +--- a/packages/rhel7/glibc/glibc-2.17-55.el7/pkgfile.yaml ++++ b/packages/rhel7/glibc/glibc-2.17-55.el7/pkgfile.yaml +@@ -1,5 +1,6 @@ + image: kernelcare/centos7:gcc-4.8.2-16.el7 + prebuild: /tmp/build.orig-glibc-2.17-55.el7.x86_64.rpm.tgz ++patch: /tmp/kpatch-glibc-2.17-55.el7.x86_64.tgz + input: + - package: . + - patches: ../../../../patches/ +diff --git a/scripts/pkgbuild b/scripts/pkgbuild +index 97c78a9..f91af57 100755 +--- a/scripts/pkgbuild ++++ b/scripts/pkgbuild +@@ -17,6 +17,7 @@ die() { + usage() { + echo "Usage: build [--prebuild] [--help] [--arch ARCH] DIR" + echo " -p|--prebuild prebuild project for further use" ++ echo " -t|--test run unit and stress tests" + echo " -a|--arch ARCH target architecture(x86_64 by default)" + echo " -h|--help print this message" + echo " DIR directory with project's info file and other resources" +@@ -32,6 +33,9 @@ prepare() { + -p|--prebuild) + ACTION=prebuild + ;; ++ -t|--test) ++ ACTION=test ++ ;; + -a|--arch) + shift + ARCH=$1 +@@ -247,13 +251,17 @@ kp_build_rpm() { + -bc \ + $KP_RPMBUILD_FLAGS \ + '--define "_topdir $KP_PROJECT_BUILD_ROOT"' \ +- $KP_PROJECT_BUILD_ROOT/SPECS/$KP_PROJECT_SPEC ++ $KP_PROJECT_BUILD_ROOT/SPECS/$KP_PROJECT_SPEC 2>&1 | ++ tee $KP_PROJECT_BUILD_ROOT/build.log + } + +-kp_install_generic() { +- local ROOT_PATCHED="$HOME/root.patched" ++kp_install_files() { ++ local src="$1" ++ local dest="$2" ++ local direction="$3" ++ local files="$4" + +- eval set -- $KP_INSTALL_FILES ++ eval set -- $files + while test -n "$1"; do + local buildpath="$1" + local installpath="$2" +@@ -263,12 +271,16 @@ kp_install_generic() { + continue + fi + +- installpath="$ROOT_PATCHED/$installpath" +- +- mkdir -p "$(dirname "$installpath")" +- +- /bin/cp -ra $KP_PROJECT_BUILD_DIR/$buildpath $installpath ++ if test $direction = "from_prebuild"; then ++ install -D $src/$buildpath $dest/$installpath ++ else ++ install -D $src/$installpath $dest/$buildpath ++ fi + done ++} ++ ++kp_check_missing_files() { ++ local builddir="$1" + + local failed= + pushd $KP_PROJECT_BUILD_DIR +@@ -295,6 +307,16 @@ kp_install_generic() { + fi + } + ++kp_install_generic() { ++ local root_patched="$HOME/root.patched" ++ ++ kp_install_files $KP_PROJECT_BUILD_DIR \ ++ $root_patched \ ++ "from_prebuild" \ ++ "$KP_INSTALL_FILES" ++ kp_check_missing_files $KP_PROJECT_BUILD_DIR ++} ++ + kp_install_rpm() { + kp_install_orig_rpm + kp_install_generic +@@ -367,6 +389,33 @@ kp_pack_patch() { + popd + } + ++kp_unpack_patch() { ++ local tmpdir=$(mktemp -d --tmpdir) ++ ++ echo " unpacking patches for $KP_PROJECT into $KP_PROJECT_BUILD_ROOT/storage" ++ ++ tar -xf /kcdata/$KP_PROJECT_PATCH -C $tmpdir ++ ++ find $tmpdir -name \*.kpatch > $tmpdir/patchlist ++ ++ while read patchfile; do ++ local patchname=${patchfile##*/} ++ local buildid=${patchname%.kpatch} ++ ++ local KP_STORAGE=$KP_PROJECT_BUILD_ROOT/storage/$buildid ++ ++ mkdir -p $KP_STORAGE/1 ++ cp $patchfile $KP_STORAGE/1/kpatch.bin ++ ln -rs $KP_STORAGE/1 $KP_STORAGE/latest ++ done < $tmpdir/patchlist ++ ++ rm -fr $tmpdir ++} ++ ++kp_mark_tests_fail() { ++ touch /kcdata/Tests-FAIL ++} ++ + overwrite_utils() { + TMPBIN=$(mktemp -d --tmpdir) + +@@ -394,7 +443,8 @@ overwrite_utils() { + } + + kp_patch_test() { +- : ++ echo "Empty kp_patch_test called, override it!" ++ exit 1 + } + + main() { +@@ -408,20 +458,25 @@ main() { + + kp_prepare_env_hook + +- if [ "$ACTION" == "prebuild" ]; then ++ if test "$ACTION" == "prebuild"; then + kp_prepare_source + kp_prebuild_hook + kp_prebuild + kp_pack_prebuilt +- else ++ elif test "$ACTION" == "build"; then + kp_unpack_prebuilt + kp_patch_source + kp_build_hook + kp_build + kp_sanity_check + kp_gen_kpatch +- kp_patch_test + kp_pack_patch ++ elif test "$ACTION" == "test"; then ++ kp_unpack_prebuilt ++ kp_prepare_test_binaries ++ kp_unpack_patch ++ #kp_patch_source ++ kp_patch_test + fi + + #clean_dirs +diff --git a/scripts/toil/build-patch.sh b/scripts/toil/build-patch.sh +index 0e12de4..28720a1 100755 +--- a/scripts/toil/build-patch.sh ++++ b/scripts/toil/build-patch.sh +@@ -15,4 +15,3 @@ make -C $KPATCH_PATH clean all + make -C /data/execve clean all + + /kcdata/scripts/pkgbuild $@ /kcdata/package +-ls /kcdata -lR +diff --git a/scripts/toil/pkgbuild.py b/scripts/toil/pkgbuild.py +index c4b009a..e091032 100755 +--- a/scripts/toil/pkgbuild.py ++++ b/scripts/toil/pkgbuild.py +@@ -12,8 +12,9 @@ The basic components are the following: + first job. + + #. `DoBuild` checks presence of the object in the Storage and runs +- `prebuildJob` chained with `uploadJob` and `buildJob` if the object is missing. +- Only `buildJob` is run otherwise. ++ `prebuildJob` chained with `uploadPrebuildJob`, `buildJob`, ++ `uploadPatchJob` and `testJob` if the object is missing. ++ Only `buildJob` and it's children are run otherwise. + + This is used to build missing parts such as an archive with the baseline + source code called `prebuilt` which is listed as optional for the +@@ -625,7 +626,7 @@ class S3DownloadJob(S3FileJob): + self.fileName = fileName + super(S3DownloadJob, self).__init__( + memory="1M", cores=1, unitName="download %s" % url, +- disk=self.obj['ContentLength']) ++ disk=max(4096, self.obj['ContentLength'])) + + def run(self, fileStore): + with fileStore.writeGlobalFileStream() as (fh, fileId): +@@ -731,24 +732,35 @@ class DoBuild(Job): + """If prebuild archive is not in storage do a prebuild and upload it to the + specified location. Otherwise just do a build.""" + +- def __init__(self, fileName, prebuildJob, uploadJob, buildJob): ++ def __init__(self, prebuildFileName, buildFileName, prebuildJob, uploadPrebuildJob, buildJob, uploadPatchJob, testJob): + super(DoBuild, self).__init__(memory="256M") + +- self.fileName = fileName ++ self.prebuildFileName = prebuildFileName ++ self.buildFileName = buildFileName + self.prebuildJob = prebuildJob + self.buildJob = buildJob +- self.uploadJob = uploadJob ++ self.uploadPrebuildJob = uploadPrebuildJob ++ self.uploadPatchJob = uploadPatchJob ++ self.testJob = testJob + + def run(self, fileStore): +- if self.fileName not in self.storage: ++ if self.prebuildFileName not in self.storage: + self.addChild(self.prebuildJob) + + self.prebuildJob.addChildNoStorage(self.buildJob) +- self.prebuildJob.addChildNoStorage(self.uploadJob) ++ self.prebuildJob.addChildNoStorage(self.uploadPrebuildJob) ++ ++ self.buildJob.addChildNoStorage(self.uploadPatchJob) ++ self.buildJob.addChildNoStorage(self.testJob) + else: +- self.addChild(self.buildJob) ++ if self.buildFileName not in self.storage: ++ self.addChild(self.buildJob) ++ self.buildJob.addChildNoStorage(self.uploadPatchJob) ++ self.buildJob.addChildNoStorage(self.testJob) ++ else: ++ self.addChild(self.testJob) + +- self._storage = self.buildJob.storage ++ self._storage = self.testJob.storage + + + class BuildPatchJob(toilJob): +@@ -784,21 +796,33 @@ class BuildPatchJob(toilJob): + prebuildUrl = self.packageDescription['prebuild'] + prebuildName = os.path.basename(prebuildUrl) + ++ patchUrl = self.packageDescription['patch'] ++ buildName = os.path.basename(patchUrl) ++ + prebuildJob = DockerScriptJob( + script=self.script, + image=self.image, +- args=['-p'], ++ args=['--prebuild'], + logfileName="prebuild.log") +- uploadJob = UploadJob([(prebuildName, prebuildUrl)]) ++ uploadPrebuildJob = UploadJob([(prebuildName, prebuildUrl)]) ++ + + buildJob = DockerScriptJob( + script=self.script, + image=self.image, + logfileName="build.log") ++ uploadPatchJob = UploadJob([(buildName, patchUrl)]) + ++ testJob = DockerScriptJob( ++ script=self.script, ++ image=self.image, ++ args=['--test'], ++ logfileName="test.log") ++ ++ doBuild = DoBuild(prebuildFileName=prebuildName, buildFileName=buildName, prebuildJob=prebuildJob, ++ uploadPrebuildJob=uploadPrebuildJob, buildJob=buildJob, ++ uploadPatchJob=uploadPatchJob, testJob=testJob) + +- doBuild = DoBuild(fileName=prebuildName, prebuildJob=prebuildJob, +- uploadJob=uploadJob, buildJob=buildJob) + tail.addFollowOn(doBuild) + tail = doBuild + +@@ -831,6 +855,11 @@ def readPackageDescription(packageFile): + prebuildUrl = '*' + prebuildUrl + inputs.append(prebuildUrl) + ++ patchUrl = packageDescription['patch'] ++ if not patchUrl.startswith('*'): ++ patchUrl = '*' + patchUrl ++ inputs.append(patchUrl) ++ + return packageDescription + + def start(toil): +-- +2.23.0 + diff --git a/0011-glibc-minimal-readme-for-toil-builder.patch b/0011-glibc-minimal-readme-for-toil-builder.patch new file mode 100644 index 0000000..25e2f6e --- /dev/null +++ b/0011-glibc-minimal-readme-for-toil-builder.patch @@ -0,0 +1,36 @@ +From 0c0f6387230d6164eaa58ae4a95656539ace5159 Mon Sep 17 00:00:00 2001 +From: Pavel Boldin +Date: Tue, 30 Jan 2018 16:42:32 +0200 +Subject: [PATCH 11/89] glibc: minimal readme for toil builder + +Signed-off-by: Pavel Boldin +--- + packages/rhel7/glibc/glibc-2.17-55.el7/README.md | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + create mode 100644 packages/rhel7/glibc/glibc-2.17-55.el7/README.md + +diff --git a/packages/rhel7/glibc/glibc-2.17-55.el7/README.md b/packages/rhel7/glibc/glibc-2.17-55.el7/README.md +new file mode 100644 +index 0000000..740d294 +--- /dev/null ++++ b/packages/rhel7/glibc/glibc-2.17-55.el7/README.md +@@ -0,0 +1,16 @@ ++Use toil-based build script to build patches for the `glibc`. For that simple ++run:: ++ ++```shell ++$ LIBCARE_DIR=~/libcare-opensource ++$ pip install -r $LIBCARE_DIR/scripts/toil/requirements.txt ++$ python $LIBCARE_DIR/scripts/toil/pkgbuild.py workdir pkgfile.yaml ++... ++``` ++ ++This should build the following files: ++```shell ++$ ls /tmp/build.orig-glibc-2.17-55.el7.x86_64.rpm.tgz /tmp/kpatch-glibc-2.17-55.el7.x86_64.tgz ++/tmp/build.orig-glibc-2.17-55.el7.x86_64.rpm.tgz ++/tmp/kpatch-glibc-2.17-55.el7.x86_64.tgz ++``` +-- +2.23.0 + diff --git a/0012-Fix-kpatch_process_init-kpatch_coroutines_free.patch b/0012-Fix-kpatch_process_init-kpatch_coroutines_free.patch new file mode 100644 index 0000000..4cad170 --- /dev/null +++ b/0012-Fix-kpatch_process_init-kpatch_coroutines_free.patch @@ -0,0 +1,46 @@ +From 3566489ee43a348f41bb0fd9c779f1064956ea82 Mon Sep 17 00:00:00 2001 +From: Roman Rashchupkin +Date: Thu, 25 Jan 2018 10:18:33 +0300 +Subject: [PATCH 12/89] Fix kpatch_process_init/kpatch_coroutines_free + +--- + src/kpatch_coro.c | 7 ++++--- + src/kpatch_process.c | 4 ++-- + 2 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/src/kpatch_coro.c b/src/kpatch_coro.c +index ff485fc..45d4a0b 100644 +--- a/src/kpatch_coro.c ++++ b/src/kpatch_coro.c +@@ -622,7 +622,8 @@ void kpatch_coroutines_free(struct kpatch_process *proc) + if (proc->coro.unwd) + unw_destroy_addr_space(proc->coro.unwd); + +- list_for_each_entry_safe(c, tmp, &proc->coro.coros, list) { +- kpatch_coro_free(c); +- } ++ if (!list_empty(&proc->coro.coros)) ++ list_for_each_entry_safe(c, tmp, &proc->coro.coros, list) { ++ kpatch_coro_free(c); ++ } + } +diff --git a/src/kpatch_process.c b/src/kpatch_process.c +index d394925..2f85373 100644 +--- a/src/kpatch_process.c ++++ b/src/kpatch_process.c +@@ -1121,10 +1121,10 @@ kpatch_process_init(kpatch_process_t *proc, + list_init(&proc->vmaholes); + proc->num_objs = 0; + +- if (process_get_comm(proc)) +- goto out_unlock; + if (kpatch_coroutines_init(proc)) + goto out_unlock; ++ if (process_get_comm(proc)) ++ goto out_unlock; + + return 0; + +-- +2.23.0 + diff --git a/0013-Add-libcare-stresstest.patch b/0013-Add-libcare-stresstest.patch new file mode 100644 index 0000000..314113e --- /dev/null +++ b/0013-Add-libcare-stresstest.patch @@ -0,0 +1,206 @@ +From 4cd3610aa11c7b6add5cf090127c522d8004a528 Mon Sep 17 00:00:00 2001 +From: Roman Rashchupkin +Date: Wed, 24 Jan 2018 16:01:51 +0300 +Subject: [PATCH 13/89] Add libcare-stresstest + +--- + src/Makefile | 10 ++++- + src/kpatch_user.c | 106 +++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 114 insertions(+), 2 deletions(-) + +diff --git a/src/Makefile b/src/Makefile +index cd766e1..58e942a 100644 +--- a/src/Makefile ++++ b/src/Makefile +@@ -3,7 +3,8 @@ TARGETS = kpatch_gensrc \ + kpatch_strip \ + libcare-cc \ + libcare-client \ +- libcare-ctl ++ libcare-ctl \ ++ libcare-stresstest + DEBUG = yes # comment out this line if not debug + + CC = gcc +@@ -39,6 +40,10 @@ libcare-ctl: kpatch_user.o kpatch_storage.o kpatch_patch.c kpatch_elf.o kpatch_p + libcare-ctl: kpatch_process.o kpatch_common.o rbtree.o kpatch_log.o + libcare-ctl: LDLIBS += -lelf -lrt $(LIBUNWIND_LIBS) + ++libcare-stresstest: kpatch_user-stresstest.o kpatch_storage.o kpatch_patch.c kpatch_elf.o kpatch_ptrace.o kpatch_coro.o ++libcare-stresstest: kpatch_process.o kpatch_common.o rbtree.o kpatch_log.o ++libcare-stresstest: LDLIBS += -lelf -lrt $(LIBUNWIND_LIBS) ++ + libcare-client: libcare-client.o + + kpatch_strip: kpatch_strip.o kpatch_elf_objinfo.o kpatch_log.o +@@ -61,6 +66,9 @@ endif + %.o: %.c deps/%.d + $(CC) $(CFLAGS) $(CFLAGS_$(*)) -o $(@) -c $(<) + ++%-stresstest.o: %.c ++ $(CC) -DSTRESS_TEST=1 $(CFLAGS) $(CFLAGS_$(*)) -o $(@) -c $(<) ++ + clean: + rm -rf *.o core.* deps/*.d $(TARGETS) + for f in tests/gensrc/*.s; do \ +diff --git a/src/kpatch_user.c b/src/kpatch_user.c +index d257b67..e6649b0 100644 +--- a/src/kpatch_user.c ++++ b/src/kpatch_user.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -563,6 +564,88 @@ cmd_update(int argc, char *argv[]) + /* send_fd */ -1); + } + ++#ifdef STRESS_TEST ++ ++struct test_data { ++ int option_period; ++ int stat_cycle_num; ++} test_info = { .option_period = 0, .stat_cycle_num = 0 }; ++ ++static int ++server_wait(int pid, int period) ++{ ++ struct timespec req, rem; ++ int i; ++ req.tv_sec = 0; ++ req.tv_nsec = 1000*1000; ++ for (i=0; i 0) ++ close(fd); ++ fd = -1; ++ if (test_info.option_period == 0) ++ return 0; ++ delay = rand() % test_info.option_period; ++ if (server_wait(pid, delay) < 0) ++ return 0; ++ ++ while (processes_unpatch(pid, 0, 0) < 0) ++ if (server_wait(pid, 1) < 0) ++ return 0; ++ test_info.stat_cycle_num++; ++ ++ delay = rand() % test_info.option_period; ++ if (server_wait(pid, delay) < 0) ++ return 0; ++ } ++ ++ return 0; ++} ++ ++static int cmd_stress_test(int fd, int argc, char *argv[]) ++{ ++ int child = fork(); ++ if (child == 0) { ++ int rv = server_stress_test(fd, argc, argv); ++ exit(rv); ++ } ++ close(fd); ++ return 0; ++} ++ ++static int usage_stresstest() ++{ ++ fprintf(stderr, "usage: libcare-stresstest PERIOD(ms, 0 - only patch) [STORAGE ROOT]\n"); ++ return -1; ++} ++ ++#endif ++ + static int + server_execute_cmd(int fd, int argc, char *argv[]) + { +@@ -572,8 +655,13 @@ server_execute_cmd(int fd, int argc, char *argv[]) + + if (!strcmp(cmd, "execve")) + return cmd_execve_startup(fd, argc, argv, 1); +- if (!strcmp(cmd, "startup")) ++ if (!strcmp(cmd, "startup")) { ++#ifdef STRESS_TEST ++ return cmd_stress_test(fd, argc, argv); ++#else + return cmd_execve_startup(fd, argc, argv, 0); ++#endif ++ } + if (!strcmp(cmd, "update")) + return cmd_update(argc, argv); + if (!strcmp(cmd, "storage")) +@@ -739,6 +827,12 @@ cmd_server(int argc, char *argv[]) + return -1; + } + ++#ifdef STRESS_TEST ++ if (sscanf(argv[0], "%d", &test_info.option_period) != 1) { ++ kplogerror("Can't parse period from %s\n", argv[0]); ++ } ++#endif ++ + sfd = server_bind_socket(argv[1]); + if (sfd < 0) + return sfd; +@@ -824,6 +918,9 @@ static int usage(const char *err) + { + if (err) + fprintf(stderr, "err: %s\n", err); ++#ifdef STRESS_TEST ++ return usage_stresstest(); ++#endif + fprintf(stderr, "usage: libcare-ctl [options] [args]\n"); + fprintf(stderr, "\nOptions:\n"); + fprintf(stderr, " -v - verbose mode\n"); +@@ -872,6 +969,12 @@ int main(int argc, char *argv[]) + argc -= optind; + argv += optind; + ++#ifdef STRESS_TEST ++ if (argc < 3) ++ return usage("not enough arguments."); ++ signal(SIGCHLD, SIG_IGN); ++ return cmd_server(argc, argv); ++#else + if (argc < 1) + return usage("not enough arguments."); + +@@ -879,4 +982,5 @@ int main(int argc, char *argv[]) + return cmd_server(argc, argv); + else + return execute_cmd(argc, argv); ++#endif + } +-- +2.23.0 + diff --git a/0014-read-auxv-from-proc-pid-auxv.patch b/0014-read-auxv-from-proc-pid-auxv.patch new file mode 100644 index 0000000..94c7f62 --- /dev/null +++ b/0014-read-auxv-from-proc-pid-auxv.patch @@ -0,0 +1,93 @@ +From 212e98a668117a11aa528e5669189b58dcaacd95 Mon Sep 17 00:00:00 2001 +From: Pavel Boldin +Date: Wed, 7 Feb 2018 09:14:39 +0200 +Subject: [PATCH 14/89] read auxv from /proc/pid/auxv + +Signed-off-by: Pavel Boldin +--- + src/kpatch_ptrace.c | 54 +++++++++++++++++---------------------------- + 1 file changed, 20 insertions(+), 34 deletions(-) + +diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c +index f732004..f91b80e 100644 +--- a/src/kpatch_ptrace.c ++++ b/src/kpatch_ptrace.c +@@ -143,55 +143,41 @@ int kpatch_process_mem_iter_peek_ulong(struct process_mem_iter *iter, + return kpatch_process_mem_iter_peek(iter, dst, sizeof(*dst), remote_addr); + } + +-/* FIXME(pboldin): read these from /proc/pid/auxv */ + int kpatch_ptrace_get_entry_point(struct kpatch_ptrace_ctx *pctx, + unsigned long *pentry_point) + { +- int ret; +- unsigned long *rstack, val; +- struct user_regs_struct regs; +- struct process_mem_iter *iter; ++ int fd, ret; ++ unsigned long entry[2] = { AT_NULL, 0 }; ++ char path[sizeof("/proc/0123456789/auxv")]; + + kpdebug("Looking for entry point..."); + +- ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, ®s); +- if (ret < 0) { +- kplogerror("can't get regs\n"); +- return -1; +- } +- +- iter = kpatch_process_mem_iter_init(pctx->proc); +- if (!iter) { +- kplogerror("can't allocate iterator\n"); ++ sprintf(path, "/proc/%d/auxv", pctx->pid); ++ fd = open(path, O_RDONLY); ++ if (fd == -1) { ++ kplogerror("can't open %s\n", path); + return -1; + } + +- /* Read stack and look for AUX data */ +- rstack = (unsigned long*)regs.rsp; +- +- /* rstack now points to envs */ +- rstack += PEEK_ULONG(rstack) + 2; +- +- /* Skip envs */ +- for (; PEEK_ULONG(rstack); rstack++) +- continue; ++ do { ++ ret = read(fd, entry, sizeof(entry)); ++ if (ret < 0 && errno == EINTR) ++ continue; ++ if (ret != sizeof(entry)) ++ break; + +- /* Now got to AUX */ +- for (rstack++; (val = PEEK_ULONG(rstack)) != AT_NULL; rstack += 2) { +- if (val == AT_ENTRY) { +- *pentry_point = PEEK_ULONG(rstack + 1); ++ if (entry[0] == AT_ENTRY) { ++ *pentry_point = entry[1]; + break; + } +- } ++ } while (1); + +- if (val != AT_ENTRY) +- kpdebug("FAIL\n"); +- else +- kpdebug("OK\n"); ++ if (ret < 0) ++ kplogerror("reading %s\n", path); + +- kpatch_process_mem_iter_free(iter); ++ close(fd); + +- return val == AT_ENTRY ? 0 : -1; ++ return entry[0] == AT_ENTRY ? 0 : -1; + } + + #define BREAK_INSN_LENGTH 1 +-- +2.23.0 + diff --git a/0015-add-fail-to-unpatch-test.patch b/0015-add-fail-to-unpatch-test.patch new file mode 100644 index 0000000..d21ea94 --- /dev/null +++ b/0015-add-fail-to-unpatch-test.patch @@ -0,0 +1,147 @@ +From f25aa052cd31cab4c6301cca5eb8e5e5f129d5bd Mon Sep 17 00:00:00 2001 +From: Pavel Boldin +Date: Wed, 7 Feb 2018 23:59:39 +0200 +Subject: [PATCH 15/89] add `fail to unpatch` test + +Add a test sample where patch is always busy in the loop and cannot +be unapplied. + +Signed-off-by: Pavel Boldin +--- + tests/fail_unpatch/Makefile | 4 ++++ + tests/fail_unpatch/desc | 1 + + tests/fail_unpatch/fail_unpatch.c | 29 ++++++++++++++++++++++++++++ + tests/fail_unpatch/fail_unpatch.diff | 11 +++++++++++ + tests/run_tests.sh | 25 ++++++++++++++++++++++-- + 5 files changed, 68 insertions(+), 2 deletions(-) + create mode 100644 tests/fail_unpatch/Makefile + create mode 100644 tests/fail_unpatch/desc + create mode 100644 tests/fail_unpatch/fail_unpatch.c + create mode 100644 tests/fail_unpatch/fail_unpatch.diff + +diff --git a/tests/fail_unpatch/Makefile b/tests/fail_unpatch/Makefile +new file mode 100644 +index 0000000..d7680e9 +--- /dev/null ++++ b/tests/fail_unpatch/Makefile +@@ -0,0 +1,4 @@ ++ ++LDLIBS:=-lpthread ++ ++include ../makefile.inc +diff --git a/tests/fail_unpatch/desc b/tests/fail_unpatch/desc +new file mode 100644 +index 0000000..02151c5 +--- /dev/null ++++ b/tests/fail_unpatch/desc +@@ -0,0 +1 @@ ++fails to unpatch the code +diff --git a/tests/fail_unpatch/fail_unpatch.c b/tests/fail_unpatch/fail_unpatch.c +new file mode 100644 +index 0000000..6b5c50f +--- /dev/null ++++ b/tests/fail_unpatch/fail_unpatch.c +@@ -0,0 +1,29 @@ ++#include ++#include ++ ++void print_greetings_patched(void) ++{ ++ while (1) { ++ printf("Hello. This a PATCHED version!\n"); ++ sleep(1); ++ } ++} ++ ++void print_greetings(void) ++{ ++ printf("Hello. This is an UNPATCHED version!\n"); ++} ++ ++void do_work() { ++ while (1) { ++ print_greetings(); ++ sleep(1); ++ } ++} ++ ++int main() ++{ ++ do_work(); ++ ++ return 0; ++} +diff --git a/tests/fail_unpatch/fail_unpatch.diff b/tests/fail_unpatch/fail_unpatch.diff +new file mode 100644 +index 0000000..49738bd +--- /dev/null ++++ b/tests/fail_unpatch/fail_unpatch.diff +@@ -0,0 +1,11 @@ ++--- ./fail_unpatch.c 2018-02-07 18:39:27.145493215 +0200 +++++ ./fail_unpatch.c 2018-02-07 18:39:43.349482218 +0200 ++@@ -11,7 +11,7 @@ ++ ++ void print_greetings(void) ++ { ++- printf("Hello. This is an UNPATCHED version!\n"); +++ print_greetings_patched(); ++ } ++ ++ void do_work() { +diff --git a/tests/run_tests.sh b/tests/run_tests.sh +index fbfb273..2bdd303 100755 +--- a/tests/run_tests.sh ++++ b/tests/run_tests.sh +@@ -105,6 +105,10 @@ check_result() { + ! grep_tail 'UNPATCHED' + return $? + ;; ++ fail_unpatch) ++ grep_tail '\${outfile}_patched ++ cat ${outfile}_patched + ++ echo "============unpatching===============" >>$logfile + libcare_ctl unpatch-user -p $pid \ +- >$logfile 2>&1 || : ++ >>$logfile 2>&1 || : + + sleep 2 + +-- +2.23.0 + diff --git a/0016-Waitpid-for-finished-threads-after-detach.patch b/0016-Waitpid-for-finished-threads-after-detach.patch new file mode 100644 index 0000000..06e525d --- /dev/null +++ b/0016-Waitpid-for-finished-threads-after-detach.patch @@ -0,0 +1,67 @@ +From b6b35d80755caed0528dfdf3825ecf055fe9ea76 Mon Sep 17 00:00:00 2001 +From: Roman Rashchupkin +Date: Wed, 28 Feb 2018 23:05:50 +0300 +Subject: [PATCH 16/89] Waitpid for finished threads after detach. + +--- + src/kpatch_process.c | 14 +++++++++++++- + src/kpatch_ptrace.c | 2 +- + 2 files changed, 14 insertions(+), 2 deletions(-) + +diff --git a/src/kpatch_process.c b/src/kpatch_process.c +index 2f85373..5c0374a 100644 +--- a/src/kpatch_process.c ++++ b/src/kpatch_process.c +@@ -12,6 +12,11 @@ + #include + #include + ++#include ++#include ++#include ++#include ++ + #include + #include + #include +@@ -537,6 +542,8 @@ static void + process_detach(kpatch_process_t *proc) + { + struct kpatch_ptrace_ctx *p, *ptmp; ++ int status; ++ pid_t pid; + + if (proc->memfd >= 0 && close(proc->memfd) < 0) + kplogerror("can't close memfd"); +@@ -546,9 +553,14 @@ process_detach(kpatch_process_t *proc) + unw_destroy_addr_space(proc->ptrace.unwd); + + list_for_each_entry_safe(p, ptmp, &proc->ptrace.pctxs, list) { +- kpatch_ptrace_detach(p); ++ if (kpatch_ptrace_detach(p) == -ESRCH) { ++ do { ++ pid = waitpid(p->pid, &status, __WALL); ++ } while (pid > 0 && !WIFEXITED(status)); ++ } + kpatch_ptrace_ctx_destroy(p); + } ++ kpinfo("Finished ptrace detaching."); + } + + static int +diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c +index f91b80e..a5f61b3 100644 +--- a/src/kpatch_ptrace.c ++++ b/src/kpatch_ptrace.c +@@ -1186,7 +1186,7 @@ int kpatch_ptrace_detach(struct kpatch_ptrace_ctx *pctx) + ret = ptrace(PTRACE_DETACH, pctx->pid, NULL, NULL); + if (ret < 0) { + kplogerror("can't detach from %d\n", pctx->pid); +- return -1; ++ return -errno; + } + + kpdebug("OK\n"); +-- +2.23.0 + diff --git a/0017-.gitignore-build-artefacts.patch b/0017-.gitignore-build-artefacts.patch new file mode 100644 index 0000000..9634ca4 --- /dev/null +++ b/0017-.gitignore-build-artefacts.patch @@ -0,0 +1,28 @@ +From 90a310f57710abb3f5e41c431d26e77fef63ad2b Mon Sep 17 00:00:00 2001 +From: Pavel Boldin +Date: Thu, 13 Sep 2018 12:00:02 +0300 +Subject: [PATCH 17/89] .gitignore build artefacts + +Signed-off-by: Pavel Boldin +--- + .gitignore | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/.gitignore b/.gitignore +index 5761abc..065f093 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -1 +1,10 @@ + *.o ++*.d ++src/kpatch_gensrc ++src/kpatch_make ++src/kpatch_strip ++src/libcare-cc ++src/libcare-client ++src/libcare-ctl ++src/libcare-stresstest ++tags +-- +2.23.0 + diff --git a/0018-kpatch_storage-put-an-end-to-description-string-load.patch b/0018-kpatch_storage-put-an-end-to-description-string-load.patch new file mode 100644 index 0000000..3bf9df5 --- /dev/null +++ b/0018-kpatch_storage-put-an-end-to-description-string-load.patch @@ -0,0 +1,29 @@ +From 3954dd43124e87c788352ccc528cc79e8fca6f6d Mon Sep 17 00:00:00 2001 +From: YiFan +Date: Tue, 13 Aug 2019 11:02:36 +0800 +Subject: [PATCH 18/89] kpatch_storage: put an end to description string loaded + +Description string should be ended up with a '\0'. +--- + src/kpatch_storage.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/kpatch_storage.c b/src/kpatch_storage.c +index a466460..2375559 100644 +--- a/src/kpatch_storage.c ++++ b/src/kpatch_storage.c +@@ -377,8 +377,10 @@ char *storage_get_description(kpatch_storage_t *storage, + if (rv == -1) + goto err_free; + +- if (rv == 0) ++ if (rv == 0) { ++ desc[sz] = '\0'; + break; ++ } + + sz += rv; + } +-- +2.23.0 + diff --git a/0019-Fix-README-files.patch b/0019-Fix-README-files.patch new file mode 100644 index 0000000..54db1ac --- /dev/null +++ b/0019-Fix-README-files.patch @@ -0,0 +1,51 @@ +From 43e10a5ef1db3c56a872e3a61edaf224b51822f0 Mon Sep 17 00:00:00 2001 +From: Pavel Artsishevsky +Date: Tue, 18 Feb 2020 15:21:26 +0300 +Subject: [PATCH 19/89] Fix README files + +- Update patch build instructions for GHOST sample; +- Add Travis CI status image to main readme file. +--- + README.rst | 3 +++ + samples/ghost/README.rst | 11 +++++++++++ + 2 files changed, 14 insertions(+) + +diff --git a/README.rst b/README.rst +index 927325d..932b7c5 100644 +--- a/README.rst ++++ b/README.rst +@@ -1,6 +1,9 @@ + LibCare -- Patch Userspace Code on Live Processes + ================================================= + ++.. image:: https://travis-ci.org/cloudlinux/libcare.svg?branch=master ++ :target: https://travis-ci.org/cloudlinux/libcare ++ + Welcome to LibCare --- Live Patch Updates for Userspace Processes and Libraries. + + LibCare delivers live patches to any of your Linux executables or libraries at +diff --git a/samples/ghost/README.rst b/samples/ghost/README.rst +index da97926..e274b54 100644 +--- a/samples/ghost/README.rst ++++ b/samples/ghost/README.rst +@@ -32,6 +32,17 @@ Now, from inside the container let's install vulnerable version of glibc: + glibc-headers-2.17-55.el7 glibc-common-2.17-55.el7 + ... + ++Also we have to downgrade elfutils since newer versions of ``eu-unstrip`` ++fail to work with glibc utilities: ++ ++.. code:: console ++ ++ [root@... /]# yum downgrade -y --enablerepo=C7.0.1406-base \ ++ elfutils-devel-0.158-3.el7.x86_64 elfutils-0.158-3.el7.x86_64 \ ++ elfutils-libs-0.158-3.el7.x86_64 elfutils-libelf-0.158-3.el7.x86_64 \ ++ elfutils-libelf-devel-0.158-3.el7.x86_64 ++ ... ++ + Build the ``libcare`` tools: + + .. code:: console +-- +2.23.0 + diff --git a/0020-include-Create-include-directory-for-header-files.patch b/0020-include-Create-include-directory-for-header-files.patch new file mode 100644 index 0000000..7890a97 --- /dev/null +++ b/0020-include-Create-include-directory-for-header-files.patch @@ -0,0 +1,129 @@ +From ca5872b354b7987ce2dfd6f5268771268ad006d0 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Sat, 10 Oct 2020 16:46:38 +0800 +Subject: [PATCH 20/89] include: Create include directory for header files + +Move all header files from src directory into newly created include directory + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/{ => include}/kpatch_common.h | 0 + src/{ => include}/kpatch_coro.h | 0 + src/{ => include}/kpatch_dbgfilter.h | 0 + src/{ => include}/kpatch_elf.h | 0 + src/{ => include}/kpatch_elf_objinfo.h | 0 + src/{ => include}/kpatch_file.h | 0 + src/{ => include}/kpatch_flags.h | 0 + src/{ => include}/kpatch_io.h | 0 + src/{ => include}/kpatch_log.h | 0 + src/{ => include}/kpatch_parse.h | 0 + src/{ => include}/kpatch_patch.h | 0 + src/{ => include}/kpatch_process.h | 0 + src/{ => include}/kpatch_ptrace.h | 0 + src/{ => include}/kpatch_storage.h | 0 + src/{ => include}/kpatch_str.h | 0 + src/{ => include}/kpatch_user.h | 0 + src/{ => include}/list.h | 0 + src/{ => include}/rbtree.h | 0 + src/{ => include}/util.h | 0 + 19 files changed, 0 insertions(+), 0 deletions(-) + rename src/{ => include}/kpatch_common.h (100%) + rename src/{ => include}/kpatch_coro.h (100%) + rename src/{ => include}/kpatch_dbgfilter.h (100%) + rename src/{ => include}/kpatch_elf.h (100%) + rename src/{ => include}/kpatch_elf_objinfo.h (100%) + rename src/{ => include}/kpatch_file.h (100%) + rename src/{ => include}/kpatch_flags.h (100%) + rename src/{ => include}/kpatch_io.h (100%) + rename src/{ => include}/kpatch_log.h (100%) + rename src/{ => include}/kpatch_parse.h (100%) + rename src/{ => include}/kpatch_patch.h (100%) + rename src/{ => include}/kpatch_process.h (100%) + rename src/{ => include}/kpatch_ptrace.h (100%) + rename src/{ => include}/kpatch_storage.h (100%) + rename src/{ => include}/kpatch_str.h (100%) + rename src/{ => include}/kpatch_user.h (100%) + rename src/{ => include}/list.h (100%) + rename src/{ => include}/rbtree.h (100%) + rename src/{ => include}/util.h (100%) + +diff --git a/src/kpatch_common.h b/src/include/kpatch_common.h +similarity index 100% +rename from src/kpatch_common.h +rename to src/include/kpatch_common.h +diff --git a/src/kpatch_coro.h b/src/include/kpatch_coro.h +similarity index 100% +rename from src/kpatch_coro.h +rename to src/include/kpatch_coro.h +diff --git a/src/kpatch_dbgfilter.h b/src/include/kpatch_dbgfilter.h +similarity index 100% +rename from src/kpatch_dbgfilter.h +rename to src/include/kpatch_dbgfilter.h +diff --git a/src/kpatch_elf.h b/src/include/kpatch_elf.h +similarity index 100% +rename from src/kpatch_elf.h +rename to src/include/kpatch_elf.h +diff --git a/src/kpatch_elf_objinfo.h b/src/include/kpatch_elf_objinfo.h +similarity index 100% +rename from src/kpatch_elf_objinfo.h +rename to src/include/kpatch_elf_objinfo.h +diff --git a/src/kpatch_file.h b/src/include/kpatch_file.h +similarity index 100% +rename from src/kpatch_file.h +rename to src/include/kpatch_file.h +diff --git a/src/kpatch_flags.h b/src/include/kpatch_flags.h +similarity index 100% +rename from src/kpatch_flags.h +rename to src/include/kpatch_flags.h +diff --git a/src/kpatch_io.h b/src/include/kpatch_io.h +similarity index 100% +rename from src/kpatch_io.h +rename to src/include/kpatch_io.h +diff --git a/src/kpatch_log.h b/src/include/kpatch_log.h +similarity index 100% +rename from src/kpatch_log.h +rename to src/include/kpatch_log.h +diff --git a/src/kpatch_parse.h b/src/include/kpatch_parse.h +similarity index 100% +rename from src/kpatch_parse.h +rename to src/include/kpatch_parse.h +diff --git a/src/kpatch_patch.h b/src/include/kpatch_patch.h +similarity index 100% +rename from src/kpatch_patch.h +rename to src/include/kpatch_patch.h +diff --git a/src/kpatch_process.h b/src/include/kpatch_process.h +similarity index 100% +rename from src/kpatch_process.h +rename to src/include/kpatch_process.h +diff --git a/src/kpatch_ptrace.h b/src/include/kpatch_ptrace.h +similarity index 100% +rename from src/kpatch_ptrace.h +rename to src/include/kpatch_ptrace.h +diff --git a/src/kpatch_storage.h b/src/include/kpatch_storage.h +similarity index 100% +rename from src/kpatch_storage.h +rename to src/include/kpatch_storage.h +diff --git a/src/kpatch_str.h b/src/include/kpatch_str.h +similarity index 100% +rename from src/kpatch_str.h +rename to src/include/kpatch_str.h +diff --git a/src/kpatch_user.h b/src/include/kpatch_user.h +similarity index 100% +rename from src/kpatch_user.h +rename to src/include/kpatch_user.h +diff --git a/src/list.h b/src/include/list.h +similarity index 100% +rename from src/list.h +rename to src/include/list.h +diff --git a/src/rbtree.h b/src/include/rbtree.h +similarity index 100% +rename from src/rbtree.h +rename to src/include/rbtree.h +diff --git a/src/util.h b/src/include/util.h +similarity index 100% +rename from src/util.h +rename to src/include/util.h +-- +2.23.0 + diff --git a/0021-src-Update-header-file-position.patch b/0021-src-Update-header-file-position.patch new file mode 100644 index 0000000..f15ef13 --- /dev/null +++ b/0021-src-Update-header-file-position.patch @@ -0,0 +1,373 @@ +From 64f163e9bd26b0af65f12e7cc4205da0d787e7fc Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Sat, 10 Oct 2020 16:50:15 +0800 +Subject: [PATCH 21/89] src: Update header file position + +Update header file positon with include prefix to source file in src directory. + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/kpatch_common.c | 6 +++--- + src/kpatch_coro.c | 12 ++++++------ + src/kpatch_dbgfilter.c | 6 +++--- + src/kpatch_elf.c | 14 +++++++------- + src/kpatch_elf_objinfo.c | 6 +++--- + src/kpatch_gensrc.c | 8 ++++---- + src/kpatch_io.c | 6 +++--- + src/kpatch_log.c | 2 +- + src/kpatch_make.c | 2 +- + src/kpatch_parse.c | 6 +++--- + src/kpatch_patch.c | 20 ++++++++++---------- + src/kpatch_process.c | 14 +++++++------- + src/kpatch_ptrace.c | 8 ++++---- + src/kpatch_storage.c | 14 +++++++------- + src/kpatch_strip.c | 8 ++++---- + src/kpatch_user.c | 18 +++++++++--------- + src/rbtree.c | 2 +- + 17 files changed, 76 insertions(+), 76 deletions(-) + +diff --git a/src/kpatch_common.c b/src/kpatch_common.c +index 99bc0b3..95d4a54 100644 +--- a/src/kpatch_common.c ++++ b/src/kpatch_common.c +@@ -6,9 +6,9 @@ + #include + #include + +-#include "kpatch_file.h" +-#include "kpatch_common.h" +-#include "kpatch_log.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_log.h" + + int kpatch_openat_file(int atfd, const char *fname, struct kp_file *kpatch) + { +diff --git a/src/kpatch_coro.c b/src/kpatch_coro.c +index 45d4a0b..02d421b 100644 +--- a/src/kpatch_coro.c ++++ b/src/kpatch_coro.c +@@ -8,12 +8,12 @@ + + #include + +-#include "kpatch_user.h" +-#include "kpatch_coro.h" +-#include "kpatch_common.h" +-#include "kpatch_elf.h" +-#include "kpatch_ptrace.h" +-#include "kpatch_log.h" ++#include "include/kpatch_user.h" ++#include "include/kpatch_coro.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_ptrace.h" ++#include "include/kpatch_log.h" + + /* Indicates that the next CORO flavours should be tried */ + #define CORO_SEARCH_NEXT (1<<31) +diff --git a/src/kpatch_dbgfilter.c b/src/kpatch_dbgfilter.c +index 73a9bc3..d385c18 100644 +--- a/src/kpatch_dbgfilter.c ++++ b/src/kpatch_dbgfilter.c +@@ -1,9 +1,9 @@ + #include + #include + +-#include "kpatch_parse.h" +-#include "kpatch_str.h" +-#include "kpatch_dbgfilter.h" ++#include "include/kpatch_parse.h" ++#include "include/kpatch_str.h" ++#include "include/kpatch_dbgfilter.h" + + static int is_cold_hot(char *s) + { +diff --git a/src/kpatch_elf.c b/src/kpatch_elf.c +index e42642b..b1dfed0 100644 +--- a/src/kpatch_elf.c ++++ b/src/kpatch_elf.c +@@ -7,13 +7,13 @@ + + #include + +-#include "kpatch_common.h" +-#include "kpatch_user.h" +-#include "kpatch_process.h" +-#include "kpatch_elf.h" +-#include "kpatch_file.h" +-#include "kpatch_ptrace.h" +-#include "kpatch_log.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_user.h" ++#include "include/kpatch_process.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_ptrace.h" ++#include "include/kpatch_log.h" + + static int + elf_object_peek_phdr(struct object_file *o) +diff --git a/src/kpatch_elf_objinfo.c b/src/kpatch_elf_objinfo.c +index 65b7144..c967c37 100644 +--- a/src/kpatch_elf_objinfo.c ++++ b/src/kpatch_elf_objinfo.c +@@ -4,9 +4,9 @@ + + #include + +-#include "kpatch_common.h" +-#include "kpatch_elf_objinfo.h" +-#include "kpatch_log.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf_objinfo.h" ++#include "include/kpatch_log.h" + + const char *kpatch_objinfo_strptr(kpatch_objinfo *oi, int type, size_t nameidx) + { +diff --git a/src/kpatch_gensrc.c b/src/kpatch_gensrc.c +index a15fa2c..a16b652 100644 +--- a/src/kpatch_gensrc.c ++++ b/src/kpatch_gensrc.c +@@ -4,10 +4,10 @@ + #include + #include + +-#include "kpatch_log.h" +-#include "kpatch_parse.h" +-#include "kpatch_dbgfilter.h" +-#include "kpatch_flags.h" ++#include "include/kpatch_log.h" ++#include "include/kpatch_parse.h" ++#include "include/kpatch_dbgfilter.h" ++#include "include/kpatch_flags.h" + + #define OS_RHEL5 1 + #define OS_RHEL6 2 +diff --git a/src/kpatch_io.c b/src/kpatch_io.c +index 663bed0..7bdc298 100644 +--- a/src/kpatch_io.c ++++ b/src/kpatch_io.c +@@ -4,9 +4,9 @@ + #include + #include + +-#include "kpatch_log.h" +-#include "kpatch_io.h" +-#include "kpatch_str.h" ++#include "include/kpatch_log.h" ++#include "include/kpatch_io.h" ++#include "include/kpatch_str.h" + + void *kp_realloc(void *p, int oldsz, int newsz) + { +diff --git a/src/kpatch_log.c b/src/kpatch_log.c +index de80f7b..6e48de4 100644 +--- a/src/kpatch_log.c ++++ b/src/kpatch_log.c +@@ -4,7 +4,7 @@ + #include + #include + +-#include "kpatch_log.h" ++#include "include/kpatch_log.h" + + int log_level = LOG_INFO; + int log_indent; +diff --git a/src/kpatch_make.c b/src/kpatch_make.c +index c584b06..6a173e6 100644 +--- a/src/kpatch_make.c ++++ b/src/kpatch_make.c +@@ -9,7 +9,7 @@ + #include + #include + +-#include "kpatch_file.h" ++#include "include/kpatch_file.h" + + #define ALIGN(x, align) ((x + align - 1) & (~(align - 1))) + +diff --git a/src/kpatch_parse.c b/src/kpatch_parse.c +index 358916b..dfb3109 100644 +--- a/src/kpatch_parse.c ++++ b/src/kpatch_parse.c +@@ -1,8 +1,8 @@ + #include + +-#include "kpatch_log.h" +-#include "kpatch_parse.h" +-#include "kpatch_flags.h" ++#include "include/kpatch_log.h" ++#include "include/kpatch_parse.h" ++#include "include/kpatch_flags.h" + + char *cline(struct kp_file *f, int l) + { +diff --git a/src/kpatch_patch.c b/src/kpatch_patch.c +index e32c702..21a160a 100644 +--- a/src/kpatch_patch.c ++++ b/src/kpatch_patch.c +@@ -10,16 +10,16 @@ + #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" ++#include "include/kpatch_patch.h" ++#include "include/kpatch_user.h" ++#include "include/kpatch_storage.h" ++#include "include/kpatch_process.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_ptrace.h" ++#include "include/list.h" ++#include "include/kpatch_log.h" + + + static inline int +diff --git a/src/kpatch_process.c b/src/kpatch_process.c +index 5c0374a..3f7f2f6 100644 +--- a/src/kpatch_process.c ++++ b/src/kpatch_process.c +@@ -23,13 +23,13 @@ + + #include + +-#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" ++#include "include/kpatch_process.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_ptrace.h" ++#include "include/list.h" ++#include "include/kpatch_log.h" + + /* + * Locks process by opening /proc//maps +diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c +index a5f61b3..8910aa8 100644 +--- a/src/kpatch_ptrace.c ++++ b/src/kpatch_ptrace.c +@@ -15,10 +15,10 @@ + #include + #include + +-#include "kpatch_process.h" +-#include "kpatch_common.h" +-#include "kpatch_ptrace.h" +-#include "kpatch_log.h" ++#include "include/kpatch_process.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_ptrace.h" ++#include "include/kpatch_log.h" + + #include + +diff --git a/src/kpatch_storage.c b/src/kpatch_storage.c +index 2375559..9165cbf 100644 +--- a/src/kpatch_storage.c ++++ b/src/kpatch_storage.c +@@ -10,13 +10,13 @@ + #include + #include + +-#include "kpatch_storage.h" +-#include "kpatch_file.h" +-#include "kpatch_common.h" +-#include "kpatch_elf.h" +-#include "kpatch_ptrace.h" +-#include "list.h" +-#include "kpatch_log.h" ++#include "include/kpatch_storage.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_ptrace.h" ++#include "include/list.h" ++#include "include/kpatch_log.h" + + + /***************************************************************************** +diff --git a/src/kpatch_strip.c b/src/kpatch_strip.c +index b4de32d..6ad56a3 100644 +--- a/src/kpatch_strip.c ++++ b/src/kpatch_strip.c +@@ -7,13 +7,13 @@ + #include + #include + #include +-#include "kpatch_file.h" +-#include "kpatch_common.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_common.h" + + #include +-#include "kpatch_elf_objinfo.h" ++#include "include/kpatch_elf_objinfo.h" + +-#include "kpatch_log.h" ++#include "include/kpatch_log.h" + + #define ALIGN(off,sz) (((off)+(sz)-1)&~((sz)-1)) + +diff --git a/src/kpatch_user.c b/src/kpatch_user.c +index e6649b0..11e3408 100644 +--- a/src/kpatch_user.c ++++ b/src/kpatch_user.c +@@ -16,15 +16,15 @@ + #include + #include + +-#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 "list.h" +-#include "kpatch_log.h" ++#include "include/kpatch_user.h" ++#include "include/kpatch_storage.h" ++#include "include/kpatch_patch.h" ++#include "include/kpatch_process.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf.h" ++#include "include/list.h" ++#include "include/kpatch_log.h" + + /* Global variables */ + static char storage_dir[PATH_MAX] = "/var/lib/libcare"; +diff --git a/src/rbtree.c b/src/rbtree.c +index fee5844..6aff1d1 100644 +--- a/src/rbtree.c ++++ b/src/rbtree.c +@@ -21,7 +21,7 @@ + linux/lib/rbtree.c + */ + +-#include "rbtree.h" ++#include "include/rbtree.h" + + /* + * red-black trees properties: http://en.wikipedia.org/wiki/Rbtree +-- +2.23.0 + diff --git a/0022-arch-Create-arch-directory-to-support-multi-arch.patch b/0022-arch-Create-arch-directory-to-support-multi-arch.patch new file mode 100644 index 0000000..5399258 --- /dev/null +++ b/0022-arch-Create-arch-directory-to-support-multi-arch.patch @@ -0,0 +1,73 @@ +From b0d5f7f4e15bd19e7dc055b3f24171a3b14aa445 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Sat, 10 Oct 2020 16:59:16 +0800 +Subject: [PATCH 22/89] arch: Create arch directory to support multi-arch + +Create directory arch/aarch64 and arch/x86 hold arch-related source files. +Create arch-related source files in libcare. +Files list below are architecture dependent: + arch_coro.c + arch_elf.c + arch_parse.c + arch_patch.c + arch_ptrace.c + arch_process.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_coro.c | 0 + src/arch/aarch64/arch_elf.c | 0 + src/arch/aarch64/arch_parse.c | 0 + src/arch/aarch64/arch_patch.c | 0 + src/arch/aarch64/arch_ptrace.c | 0 + src/arch/x86/arch_coro.c | 0 + src/arch/x86/arch_elf.c | 0 + src/arch/x86/arch_parse.c | 0 + src/arch/x86/arch_patch.c | 0 + src/arch/x86/arch_ptrace.c | 0 + 10 files changed, 0 insertions(+), 0 deletions(-) + create mode 100644 src/arch/aarch64/arch_coro.c + create mode 100644 src/arch/aarch64/arch_elf.c + create mode 100644 src/arch/aarch64/arch_parse.c + create mode 100644 src/arch/aarch64/arch_patch.c + create mode 100644 src/arch/aarch64/arch_ptrace.c + create mode 100644 src/arch/x86/arch_coro.c + create mode 100644 src/arch/x86/arch_elf.c + create mode 100644 src/arch/x86/arch_parse.c + create mode 100644 src/arch/x86/arch_patch.c + create mode 100644 src/arch/x86/arch_ptrace.c + +diff --git a/src/arch/aarch64/arch_coro.c b/src/arch/aarch64/arch_coro.c +new file mode 100644 +index 0000000..e69de29 +diff --git a/src/arch/aarch64/arch_elf.c b/src/arch/aarch64/arch_elf.c +new file mode 100644 +index 0000000..e69de29 +diff --git a/src/arch/aarch64/arch_parse.c b/src/arch/aarch64/arch_parse.c +new file mode 100644 +index 0000000..e69de29 +diff --git a/src/arch/aarch64/arch_patch.c b/src/arch/aarch64/arch_patch.c +new file mode 100644 +index 0000000..e69de29 +diff --git a/src/arch/aarch64/arch_ptrace.c b/src/arch/aarch64/arch_ptrace.c +new file mode 100644 +index 0000000..e69de29 +diff --git a/src/arch/x86/arch_coro.c b/src/arch/x86/arch_coro.c +new file mode 100644 +index 0000000..e69de29 +diff --git a/src/arch/x86/arch_elf.c b/src/arch/x86/arch_elf.c +new file mode 100644 +index 0000000..e69de29 +diff --git a/src/arch/x86/arch_parse.c b/src/arch/x86/arch_parse.c +new file mode 100644 +index 0000000..e69de29 +diff --git a/src/arch/x86/arch_patch.c b/src/arch/x86/arch_patch.c +new file mode 100644 +index 0000000..e69de29 +diff --git a/src/arch/x86/arch_ptrace.c b/src/arch/x86/arch_ptrace.c +new file mode 100644 +index 0000000..e69de29 +-- +2.23.0 + diff --git a/0023-config-configure-out-the-running-arch.patch b/0023-config-configure-out-the-running-arch.patch new file mode 100644 index 0000000..6e2941e --- /dev/null +++ b/0023-config-configure-out-the-running-arch.patch @@ -0,0 +1,26 @@ +From d9b5d88e94b8be0100b0fc2ee0054c78fb0737be Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Tue, 13 Oct 2020 09:36:24 +0800 +Subject: [PATCH 23/89] config: configure out the running arch + +To support multi-arch, we need to figure out which architecture we stands on. +So let's get arch type before running the compilation process. + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/config | 2 ++ + 1 file changed, 2 insertions(+) + create mode 100755 src/config + +diff --git a/src/config b/src/config +new file mode 100755 +index 0000000..6c44cf2 +--- /dev/null ++++ b/src/config +@@ -0,0 +1,2 @@ ++#!/bin/bash ++uname -m > arch.desc +-- +2.23.0 + diff --git a/0024-Makefile-Adapt-Makefile-for-different-architectures.patch b/0024-Makefile-Adapt-Makefile-for-different-architectures.patch new file mode 100644 index 0000000..73cf5ad --- /dev/null +++ b/0024-Makefile-Adapt-Makefile-for-different-architectures.patch @@ -0,0 +1,72 @@ +From d12c8e57c24649e6c8c74e6dc72f02f54e82aea7 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Tue, 13 Oct 2020 14:55:53 +0800 +Subject: [PATCH 24/89] Makefile: Adapt Makefile for different architectures + +First take the arch information from config file. Then compile the +right file accroding to the architecture and find right header file +postion from src/include. + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/Makefile | 21 +++++++++++++++------ + 1 file changed, 15 insertions(+), 6 deletions(-) + +diff --git a/src/Makefile b/src/Makefile +index 58e942a..9108a02 100644 +--- a/src/Makefile ++++ b/src/Makefile +@@ -7,8 +7,10 @@ TARGETS = kpatch_gensrc \ + libcare-stresstest + DEBUG = yes # comment out this line if not debug + ++SRC_PATH := $(shell pwd) ++ + CC = gcc +-CFLAGS_MISC = -Wall -g -O2 -D_GNU_SOURCE ++CFLAGS_MISC = -Wall -g -O2 -D_GNU_SOURCE -I $(SRC_PATH) + + cc-option = $(shell if $(CC) $(CFLAGS_MISC) $(1) -S -o /dev/null -xc /dev/null \ + > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) +@@ -18,6 +20,13 @@ CFLAGS_WARN += $(call cc-option,-Wno-builtin-macro-redefined) + CFLAGS_WARN += $(call cc-option,-Wno-deprecated-declarations) + CFLAGS = $(CFLAGS_MISC) $(CFLAGS_WARN) + ++ARCH := $(shell cat arch.desc) ++ifeq ($(ARCH),aarch64) ++VPATH = arch/aarch64 ++else ++VPATH = arch/x86 ++endif ++ + ifdef DEBUG + CFLAGS += -DDEBUG -O0 -g + endif +@@ -30,18 +39,18 @@ all: $(TARGETS) + + .SUFFIXES: + +-kpatch_gensrc: kpatch_gensrc.o kpatch_dbgfilter.o kpatch_parse.o kpatch_io.o rbtree.o kpatch_log.o ++kpatch_gensrc: kpatch_gensrc.o kpatch_dbgfilter.o kpatch_parse.o kpatch_io.o rbtree.o kpatch_log.o arch_parse.o + kpatch_make: kpatch_make.o + + LIBUNWIND_LIBS := $(shell pkg-config --libs libunwind libunwind-ptrace) + + +-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: kpatch_user.o kpatch_storage.o kpatch_patch.o kpatch_elf.o kpatch_ptrace.o kpatch_coro.o arch_patch.o arch_elf.o arch_ptrace.o arch_coro.o ++libcare-ctl: kpatch_process.o kpatch_common.o rbtree.o kpatch_log.o arch_process.o + libcare-ctl: LDLIBS += -lelf -lrt $(LIBUNWIND_LIBS) + +-libcare-stresstest: kpatch_user-stresstest.o kpatch_storage.o kpatch_patch.c kpatch_elf.o kpatch_ptrace.o kpatch_coro.o +-libcare-stresstest: kpatch_process.o kpatch_common.o rbtree.o kpatch_log.o ++libcare-stresstest: kpatch_user-stresstest.o kpatch_storage.o kpatch_patch.o kpatch_elf.o kpatch_ptrace.o kpatch_coro.o arch_patch.o arch_elf.o arch_ptrace.o arch_coro.o ++libcare-stresstest: kpatch_process.o kpatch_common.o rbtree.o kpatch_log.o arch_process.o + libcare-stresstest: LDLIBS += -lelf -lrt $(LIBUNWIND_LIBS) + + libcare-client: libcare-client.o +-- +2.23.0 + diff --git a/0025-kpatch_parse-Update-asm_directives-for-aarch64.patch b/0025-kpatch_parse-Update-asm_directives-for-aarch64.patch new file mode 100644 index 0000000..3f411bc --- /dev/null +++ b/0025-kpatch_parse-Update-asm_directives-for-aarch64.patch @@ -0,0 +1,31 @@ +From 48a7b69f85710e1db33db994e79fdcd568ceacda Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 10:53:28 +0800 +Subject: [PATCH 25/89] kpatch_parse: Update asm_directives for aarch64 + +Update asm_directives in kpatch_parse.c, because +the golbal identifier in x86 is ".globl" +which is different from the ".global"in aarch64. + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/kpatch_parse.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/kpatch_parse.c b/src/kpatch_parse.c +index dfb3109..44e8a60 100644 +--- a/src/kpatch_parse.c ++++ b/src/kpatch_parse.c +@@ -82,6 +82,8 @@ static struct { + { DIRECTIVE_SUBSECTION, ".subsection"}, + + { DIRECTIVE_GLOBL, ".globl"}, ++ { DIRECTIVE_GLOBL, ".global"}, ++ + { DIRECTIVE_LOCAL, ".local"}, + { DIRECTIVE_HIDDEN, ".hidden"}, + { DIRECTIVE_PROTECTED, ".protected"}, +-- +2.23.0 + diff --git a/0026-kpatch_parse-Split-function-parse_ctype.patch b/0026-kpatch_parse-Split-function-parse_ctype.patch new file mode 100644 index 0000000..8f6f377 --- /dev/null +++ b/0026-kpatch_parse-Split-function-parse_ctype.patch @@ -0,0 +1,154 @@ +From 13dafd93cf011d79f1f4baf2c9035faeb52f4945 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 13:31:41 +0800 +Subject: [PATCH 26/89] kpatch_parse: Split function parse_ctype + +The parse_ctype function is arch related, so let's make two +separate definations in arch/x86/arch_parse.c and arch/aarch64/arch_parse.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_parse.c | 34 ++++++++++++++++++++++++++++++++++ + src/arch/x86/arch_parse.c | 31 +++++++++++++++++++++++++++++++ + src/include/kpatch_parse.h | 2 ++ + src/kpatch_parse.c | 28 +--------------------------- + 4 files changed, 68 insertions(+), 27 deletions(-) + +diff --git a/src/arch/aarch64/arch_parse.c b/src/arch/aarch64/arch_parse.c +index e69de29..8eb88a9 100644 +--- a/src/arch/aarch64/arch_parse.c ++++ b/src/arch/aarch64/arch_parse.c +@@ -0,0 +1,34 @@ ++#include ++ ++#include "include/kpatch_log.h" ++#include "include/kpatch_parse.h" ++#include "include/kpatch_flags.h" ++ ++int parse_ctype(char *origs, bool with_checks) ++{ ++ char *s = origs; ++ int type; ++ kpstr_t t; ++ ++ s = skip_blanks(s); ++ if (s[0] == '#') ++ return DIRECTIVE_COMMENT; /* Single-line comment */ ++ ++ if (s[0] == '/' && s[1] == '/') ++ return DIRECTIVE_COMMENT; /* Arm disassembly support c style comment */ ++ ++ get_token(&s, &t); ++ type = find_ctype(&t); ++ ++ if (type >= 0) ++ return type; ++ ++ /* ++ * Asm labels starting from digits are local labels, they can be even created multiple times in the same function. ++ * So there is no reason to handle them and bother with renaming at all. It would create conflicts at our brains ++ * and require special tracking and matching... Brrrr.... */ ++ if (s && *s == ':') ++ return !isdigit(t.s[0]) ? DIRECTIVE_LABEL : DIRECTIVE_LOCAL_LABEL; ++ ++ return DIRECTIVE_OTHER; ++} +diff --git a/src/arch/x86/arch_parse.c b/src/arch/x86/arch_parse.c +index e69de29..5a67116 100644 +--- a/src/arch/x86/arch_parse.c ++++ b/src/arch/x86/arch_parse.c +@@ -0,0 +1,31 @@ ++#include ++ ++#include "include/kpatch_log.h" ++#include "include/kpatch_parse.h" ++#include "include/kpatch_flags.h" ++ ++int parse_ctype(char *origs, bool with_checks) ++{ ++ char *s = origs; ++ int type; ++ kpstr_t t; ++ ++ s = skip_blanks(s); ++ if (s[0] == '#') ++ return DIRECTIVE_COMMENT; /* Single-line comment */ ++ ++ get_token(&s, &t); ++ type = find_ctype(&t); ++ ++ if (type >= 0) ++ return type; ++ ++ /* ++ * Asm labels starting from digits are local labels, they can be even created multiple times in the same function. ++ * So there is no reason to handle them and bother with renaming at all. It would create conflicts at our brains ++ * and require special tracking and matching... Brrrr.... */ ++ if (s && *s == ':') ++ return !isdigit(t.s[0]) ? DIRECTIVE_LABEL : DIRECTIVE_LOCAL_LABEL; ++ ++ return DIRECTIVE_OTHER; ++} +diff --git a/src/include/kpatch_parse.h b/src/include/kpatch_parse.h +index 1012d5d..e1b7501 100644 +--- a/src/include/kpatch_parse.h ++++ b/src/include/kpatch_parse.h +@@ -51,6 +51,8 @@ void init_ctypes(struct kp_file *f); + int ctype(struct kp_file *f, int l); + int is_sect_cmd(struct kp_file *f, int l); + ++ ++int find_ctype(kpstr_t *t); + int parse_ctype(char *s, bool with_checks); + + /* ----------------------------------------- sections ----------------------------------------- */ +diff --git a/src/kpatch_parse.c b/src/kpatch_parse.c +index 44e8a60..4bafdb7 100644 +--- a/src/kpatch_parse.c ++++ b/src/kpatch_parse.c +@@ -109,7 +109,7 @@ static void get_type_args(char *s, kpstr_t *nm, kpstr_t *attr) + kpfatal("can't parse .type command"); + } + +-static int find_ctype(kpstr_t *t) ++int find_ctype(kpstr_t *t) + { + int i; + for (i = 0; i < (int)(sizeof(asm_directives)/sizeof(asm_directives[0])); i++) { +@@ -119,32 +119,6 @@ static int find_ctype(kpstr_t *t) + return -1; + } + +-int parse_ctype(char *origs, bool with_checks) +-{ +- char *s = origs; +- int type; +- kpstr_t t; +- +- s = skip_blanks(s); +- if (s[0] == '#') +- return DIRECTIVE_COMMENT; /* Single-line comment */ +- +- get_token(&s, &t); +- type = find_ctype(&t); +- +- if (type >= 0) +- return type; +- +- /* +- * Asm labels starting from digits are local labels, they can be even created multiple times in the same function. +- * So there is no reason to handle them and bother with renaming at all. It would create conflicts at our brains +- * and require special tracking and matching... Brrrr.... */ +- if (s && *s == ':') +- return !isdigit(t.s[0]) ? DIRECTIVE_LABEL : DIRECTIVE_LOCAL_LABEL; +- +- return DIRECTIVE_OTHER; +-} +- + int ctype(struct kp_file *f, int l) + { + if (l >= f->nr_lines) +-- +2.23.0 + diff --git a/0027-kpatch_parse-Split-function-init_multilines.patch b/0027-kpatch_parse-Split-function-init_multilines.patch new file mode 100644 index 0000000..e4d98b3 --- /dev/null +++ b/0027-kpatch_parse-Split-function-init_multilines.patch @@ -0,0 +1,249 @@ +From 0e0e7195fca05f706c1701484710fea9fc8b21f5 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 13:41:06 +0800 +Subject: [PATCH 27/89] kpatch_parse: Split function init_multilines + +The function init_multilines is arch related, so let's make +two definations in arch/x86/arch_parse.c and arch/aarch64/arch_parse.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_parse.c | 67 +++++++++++++++++++++++++++++++++++ + src/arch/x86/arch_parse.c | 65 +++++++++++++++++++++++++++++++++ + src/kpatch_parse.c | 65 --------------------------------- + 3 files changed, 132 insertions(+), 65 deletions(-) + +diff --git a/src/arch/aarch64/arch_parse.c b/src/arch/aarch64/arch_parse.c +index 8eb88a9..1233e03 100644 +--- a/src/arch/aarch64/arch_parse.c ++++ b/src/arch/aarch64/arch_parse.c +@@ -4,6 +4,73 @@ + #include "include/kpatch_parse.h" + #include "include/kpatch_flags.h" + ++/* break manually crafted multiple statements separated by ; to separate lines */ ++void init_multilines(struct kp_file *f) ++{ ++ int i, nr, sz = 64, slen, first_token; ++ char **lines = NULL, *s, *se; ++ int *lines_num = NULL; ++ kpstr_t t; ++ ++ nr = 0; ++ for (i = 0; i < f->nr_lines; i++) { ++ if (nr + 1000 >= sz || !lines) { ++ sz *= 2; ++ lines = kp_realloc(lines, (sz/2) * sizeof(char *), sz * sizeof(char *)); ++ lines_num = kp_realloc(lines_num, (sz/2) * sizeof(int), sz * sizeof(int)); ++ } ++ ++ s = f->lines[i]; ++ if (strpbrk(s, ";:") != NULL) { ++ while (s && *s) { ++ se = s; ++ slen = strlen(s); ++ first_token = 1; ++ while (se) { ++ get_token(&se, &t); ++ if (t.l == 1 && t.s[0] == '#') ++ goto done; ++ if (t.l == 2 && t.s[0] == '/' && t.s[1] == '/') ++ goto done; ++ if (t.l == 1 && t.s[0] == ';') { ++ slen = t.s - s; ++ break; ++ } ++ /* first token with ':' after is ++ * the label, separate it unless ++ * it is done already (next non-blank ++ * is '\0') ++ */ ++ if (first_token && se && ++ se[0] == ':' && ++ se[1] != '\0') { ++ slen = se - s + 1; ++ se++; ++ break; ++ } ++ first_token = 0; ++ } ++ lines[nr] = strndup(s, slen); ++ s = se; ++ lines_num[nr] = i; ++ nr++; ++ if (nr >= sz) ++ kpfatal("oops, not prepared to handle >1000 asm statements in single line"); ++ } ++ free(f->lines[i]); ++ } else { ++done: ++ lines[nr] = s; ++ lines_num[nr] = i; ++ nr++; ++ } ++ } ++ free(f->lines); ++ f->lines = lines; ++ f->lines_num = lines_num; ++ f->nr_lines = nr; ++} ++ + int parse_ctype(char *origs, bool with_checks) + { + char *s = origs; +diff --git a/src/arch/x86/arch_parse.c b/src/arch/x86/arch_parse.c +index 5a67116..92ff483 100644 +--- a/src/arch/x86/arch_parse.c ++++ b/src/arch/x86/arch_parse.c +@@ -4,6 +4,71 @@ + #include "include/kpatch_parse.h" + #include "include/kpatch_flags.h" + ++/* break manually crafted multiple statements separated by ; to separate lines */ ++void init_multilines(struct kp_file *f) ++{ ++ int i, nr, sz = 64, slen, first_token; ++ char **lines = NULL, *s, *se; ++ int *lines_num = NULL; ++ kpstr_t t; ++ ++ nr = 0; ++ for (i = 0; i < f->nr_lines; i++) { ++ if (nr + 1000 >= sz || !lines) { ++ sz *= 2; ++ lines = kp_realloc(lines, (sz/2) * sizeof(char *), sz * sizeof(char *)); ++ lines_num = kp_realloc(lines_num, (sz/2) * sizeof(int), sz * sizeof(int)); ++ } ++ ++ s = f->lines[i]; ++ if (strpbrk(s, ";:") != NULL) { ++ while (s && *s) { ++ se = s; ++ slen = strlen(s); ++ first_token = 1; ++ while (se) { ++ get_token(&se, &t); ++ if (t.l == 1 && t.s[0] == '#') ++ goto done; ++ if (t.l == 1 && t.s[0] == ';') { ++ slen = t.s - s; ++ break; ++ } ++ /* first token with ':' after is ++ * the label, separate it unless ++ * it is done already (next non-blank ++ * is '\0') ++ */ ++ if (first_token && se && ++ se[0] == ':' && ++ se[1] != '\0') { ++ slen = se - s + 1; ++ se++; ++ break; ++ } ++ first_token = 0; ++ } ++ lines[nr] = strndup(s, slen); ++ s = se; ++ lines_num[nr] = i; ++ nr++; ++ if (nr >= sz) ++ kpfatal("oops, not prepared to handle >1000 asm statements in single line"); ++ } ++ free(f->lines[i]); ++ } else { ++done: ++ lines[nr] = s; ++ lines_num[nr] = i; ++ nr++; ++ } ++ } ++ free(f->lines); ++ f->lines = lines; ++ f->lines_num = lines_num; ++ f->nr_lines = nr; ++} ++ + int parse_ctype(char *origs, bool with_checks) + { + char *s = origs; +diff --git a/src/kpatch_parse.c b/src/kpatch_parse.c +index 4bafdb7..857dbf3 100644 +--- a/src/kpatch_parse.c ++++ b/src/kpatch_parse.c +@@ -136,71 +136,6 @@ int is_sect_cmd(struct kp_file *f, int l) + t == DIRECTIVE_PREVIOUS || t == DIRECTIVE_SUBSECTION; + } + +-/* break manually crafted multiple statements separated by ; to separate lines */ +-void init_multilines(struct kp_file *f) +-{ +- int i, nr, sz = 64, slen, first_token; +- char **lines = NULL, *s, *se; +- int *lines_num = NULL; +- kpstr_t t; +- +- nr = 0; +- for (i = 0; i < f->nr_lines; i++) { +- if (nr + 1000 >= sz || !lines) { +- sz *= 2; +- lines = kp_realloc(lines, (sz/2) * sizeof(char *), sz * sizeof(char *)); +- lines_num = kp_realloc(lines_num, (sz/2) * sizeof(int), sz * sizeof(int)); +- } +- +- s = f->lines[i]; +- if (strpbrk(s, ";:") != NULL) { +- while (s && *s) { +- se = s; +- slen = strlen(s); +- first_token = 1; +- while (se) { +- get_token(&se, &t); +- if (t.l == 1 && t.s[0] == '#') +- goto done; +- if (t.l == 1 && t.s[0] == ';') { +- slen = t.s - s; +- break; +- } +- /* first token with ':' after is +- * the label, separate it unless +- * it is done already (next non-blank +- * is '\0') +- */ +- if (first_token && se && +- se[0] == ':' && +- se[1] != '\0') { +- slen = se - s + 1; +- se++; +- break; +- } +- first_token = 0; +- } +- lines[nr] = strndup(s, slen); +- s = se; +- lines_num[nr] = i; +- nr++; +- if (nr >= sz) +- kpfatal("oops, not prepared to handle >1000 asm statements in single line"); +- } +- free(f->lines[i]); +- } else { +-done: +- lines[nr] = s; +- lines_num[nr] = i; +- nr++; +- } +- } +- free(f->lines); +- f->lines = lines; +- f->lines_num = lines_num; +- f->nr_lines = nr; +-} +- + void init_ctypes(struct kp_file *f) + { + int i; +-- +2.23.0 + diff --git a/0028-kpatch_parse-Split-function-is_variable_start.patch b/0028-kpatch_parse-Split-function-is_variable_start.patch new file mode 100644 index 0000000..92d549b --- /dev/null +++ b/0028-kpatch_parse-Split-function-is_variable_start.patch @@ -0,0 +1,282 @@ +From a3fce6e82d24afe186a46461b9cf931f2f023f36 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 13:48:18 +0800 +Subject: [PATCH 28/89] kpatch_parse: Split function is_variable_start + +The function is_variable_start is arch related, since different arch may +use different assembly directives to describe variables. So let's make +two definations in arch/x86/arch_parse.c and arch/aarch64/arch_parse.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_parse.c | 69 ++++++++++++++++++++++++++++++++++ + src/arch/x86/arch_parse.c | 69 ++++++++++++++++++++++++++++++++++ + src/include/kpatch_parse.h | 1 + + src/kpatch_parse.c | 71 +---------------------------------- + 4 files changed, 140 insertions(+), 70 deletions(-) + +diff --git a/src/arch/aarch64/arch_parse.c b/src/arch/aarch64/arch_parse.c +index 1233e03..abbc38c 100644 +--- a/src/arch/aarch64/arch_parse.c ++++ b/src/arch/aarch64/arch_parse.c +@@ -4,6 +4,75 @@ + #include "include/kpatch_parse.h" + #include "include/kpatch_flags.h" + ++int is_variable_start(struct kp_file *f, int l, int *e, int *pglobl, kpstr_t *nm) ++{ ++ char *s; ++ int l0 = l, globl = 0; ++ kpstr_t nm2, attr; ++ ++ kpstrset(nm, "", 0); ++ for ( ; cline(f, l); l++) { ++ ++ /* first verify that all the commands we met has the same symbol name... just to be safe! */ ++ s = cline(f, l); ++ if (*s == '\0' && l != l0) ++ continue; ++ switch (ctype(f, l)) { ++ case DIRECTIVE_TYPE: ++ case DIRECTIVE_GLOBL: ++ case DIRECTIVE_LOCAL: ++ get_token(&s, &nm2); ++ case DIRECTIVE_LABEL: ++ get_token(&s, &nm2); ++ if (nm->l && kpstrcmp(nm, &nm2)) /* some other symbol met... stop */ ++ return 0; ++ *nm = nm2; ++ break; ++ } ++ ++ switch (ctype(f, l)) { ++ case DIRECTIVE_TEXT: ++ case DIRECTIVE_DATA: ++ case DIRECTIVE_BSS: ++ case DIRECTIVE_SECTION: ++ case DIRECTIVE_PUSHSECTION: ++ case DIRECTIVE_POPSECTION: ++ case DIRECTIVE_PREVIOUS: ++ case DIRECTIVE_SUBSECTION: ++ break; ++ case DIRECTIVE_TYPE: ++ get_type_args(cline(f, l), &nm2, &attr); ++ if (kpstrcmpz(&attr, "%object") && kpstrcmpz(&attr, "%tls_object")) ++ return 0; ++ break; ++ case DIRECTIVE_GLOBL: ++ globl = 1; ++ break; ++ case DIRECTIVE_ALIGN: ++ break; ++ case DIRECTIVE_COMMENT: ++ case DIRECTIVE_SIZE: ++ /* can't start with .size */ ++ if (l0 == l) ++ return 0; ++ break; ++ case DIRECTIVE_LABEL: ++ if (!is_data_sect(csect(f, l))) ++ return 0; ++ /* fall throught */ ++ case DIRECTIVE_LOCAL: ++ if (e) ++ *e = l + 1; ++ if (pglobl) ++ *pglobl = globl; ++ return 1; ++ default: ++ return 0; ++ } ++ } ++ return 0; ++} ++ + /* break manually crafted multiple statements separated by ; to separate lines */ + void init_multilines(struct kp_file *f) + { +diff --git a/src/arch/x86/arch_parse.c b/src/arch/x86/arch_parse.c +index 92ff483..ca57507 100644 +--- a/src/arch/x86/arch_parse.c ++++ b/src/arch/x86/arch_parse.c +@@ -4,6 +4,75 @@ + #include "include/kpatch_parse.h" + #include "include/kpatch_flags.h" + ++int is_variable_start(struct kp_file *f, int l, int *e, int *pglobl, kpstr_t *nm) ++{ ++ char *s; ++ int l0 = l, globl = 0; ++ kpstr_t nm2, attr; ++ ++ kpstrset(nm, "", 0); ++ for ( ; cline(f, l); l++) { ++ ++ /* first verify that all the commands we met has the same symbol name... just to be safe! */ ++ s = cline(f, l); ++ if (*s == '\0' && l != l0) ++ continue; ++ switch (ctype(f, l)) { ++ case DIRECTIVE_TYPE: ++ case DIRECTIVE_GLOBL: ++ case DIRECTIVE_LOCAL: ++ get_token(&s, &nm2); ++ case DIRECTIVE_LABEL: ++ get_token(&s, &nm2); ++ if (nm->l && kpstrcmp(nm, &nm2)) /* some other symbol met... stop */ ++ return 0; ++ *nm = nm2; ++ break; ++ } ++ ++ switch (ctype(f, l)) { ++ case DIRECTIVE_TEXT: ++ case DIRECTIVE_DATA: ++ case DIRECTIVE_BSS: ++ case DIRECTIVE_SECTION: ++ case DIRECTIVE_PUSHSECTION: ++ case DIRECTIVE_POPSECTION: ++ case DIRECTIVE_PREVIOUS: ++ case DIRECTIVE_SUBSECTION: ++ break; ++ case DIRECTIVE_TYPE: ++ get_type_args(cline(f, l), &nm2, &attr); ++ if (kpstrcmpz(&attr, "@object")) ++ return 0; ++ break; ++ case DIRECTIVE_GLOBL: ++ globl = 1; ++ break; ++ case DIRECTIVE_ALIGN: ++ break; ++ case DIRECTIVE_COMMENT: ++ case DIRECTIVE_SIZE: ++ /* can't start with .size */ ++ if (l0 == l) ++ return 0; ++ break; ++ case DIRECTIVE_LABEL: ++ if (!is_data_sect(csect(f, l))) ++ return 0; ++ /* fall throught */ ++ case DIRECTIVE_LOCAL: ++ if (e) ++ *e = l + 1; ++ if (pglobl) ++ *pglobl = globl; ++ return 1; ++ default: ++ return 0; ++ } ++ } ++ return 0; ++} ++ + /* break manually crafted multiple statements separated by ; to separate lines */ + void init_multilines(struct kp_file *f) + { +diff --git a/src/include/kpatch_parse.h b/src/include/kpatch_parse.h +index e1b7501..0f41509 100644 +--- a/src/include/kpatch_parse.h ++++ b/src/include/kpatch_parse.h +@@ -109,6 +109,7 @@ void __get_token(char **str, kpstr_t *x, const char *delim); + int is_function_start(struct kp_file *f, int l, kpstr_t *nm); + int is_function_end(struct kp_file *f, int l, kpstr_t *nm); + ++void get_type_args(char *s, kpstr_t *nm, kpstr_t *attr); + int is_variable_start(struct kp_file *f, int l, int *e, int *globl, kpstr_t *nm); + int is_data_def(char *s, int type); + +diff --git a/src/kpatch_parse.c b/src/kpatch_parse.c +index 857dbf3..a3be7c0 100644 +--- a/src/kpatch_parse.c ++++ b/src/kpatch_parse.c +@@ -93,7 +93,7 @@ static struct { + }; + + /* parse arguments of .type command */ +-static void get_type_args(char *s, kpstr_t *nm, kpstr_t *attr) ++void get_type_args(char *s, kpstr_t *nm, kpstr_t *attr) + { + kpstr_t t, t2; + +@@ -674,75 +674,6 @@ int is_function_end(struct kp_file *f, int l, kpstr_t *nm) + return 1; + } + +-int is_variable_start(struct kp_file *f, int l, int *e, int *pglobl, kpstr_t *nm) +-{ +- char *s; +- int l0 = l, globl = 0; +- kpstr_t nm2, attr; +- +- kpstrset(nm, "", 0); +- for ( ; cline(f, l); l++) { +- +- /* first verify that all the commands we met has the same symbol name... just to be safe! */ +- s = cline(f, l); +- if (*s == '\0' && l != l0) +- continue; +- switch (ctype(f, l)) { +- case DIRECTIVE_TYPE: +- case DIRECTIVE_GLOBL: +- case DIRECTIVE_LOCAL: +- get_token(&s, &nm2); +- case DIRECTIVE_LABEL: +- get_token(&s, &nm2); +- if (nm->l && kpstrcmp(nm, &nm2)) /* some other symbol met... stop */ +- return 0; +- *nm = nm2; +- break; +- } +- +- switch (ctype(f, l)) { +- case DIRECTIVE_TEXT: +- case DIRECTIVE_DATA: +- case DIRECTIVE_BSS: +- case DIRECTIVE_SECTION: +- case DIRECTIVE_PUSHSECTION: +- case DIRECTIVE_POPSECTION: +- case DIRECTIVE_PREVIOUS: +- case DIRECTIVE_SUBSECTION: +- break; +- case DIRECTIVE_TYPE: +- get_type_args(cline(f, l), &nm2, &attr); +- if (kpstrcmpz(&attr, "@object")) +- return 0; +- break; +- case DIRECTIVE_GLOBL: +- globl = 1; +- break; +- case DIRECTIVE_ALIGN: +- break; +- case DIRECTIVE_COMMENT: +- case DIRECTIVE_SIZE: +- /* can't start with .size */ +- if (l0 == l) +- return 0; +- break; +- case DIRECTIVE_LABEL: +- if (!is_data_sect(csect(f, l))) +- return 0; +- /* fall throught */ +- case DIRECTIVE_LOCAL: +- if (e) +- *e = l + 1; +- if (pglobl) +- *pglobl = globl; +- return 1; +- default: +- return 0; +- } +- } +- return 0; +-} +- + int is_data_def(char *s, int type) + { + kpstr_t t; +-- +2.23.0 + diff --git a/0029-kpatch_parse-Split-function-is_data_def.patch b/0029-kpatch_parse-Split-function-is_data_def.patch new file mode 100644 index 0000000..cd71280 --- /dev/null +++ b/0029-kpatch_parse-Split-function-is_data_def.patch @@ -0,0 +1,180 @@ +From eaff972f8edd58491eeca17f4b7553cceb1fb5d4 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 13:53:01 +0800 +Subject: [PATCH 29/89] kpatch_parse: Split function is_data_def + +The function is_data_def is arch related, so let's make two +separate definations in arch/x86/arch_parse.c and arch/aarch64/arch_parse.c + +Signed-off-by: Jiajie Li +--- + src/arch/aarch64/arch_parse.c | 64 +++++++++++++++++++++++++++++++++++ + src/arch/x86/arch_parse.c | 34 +++++++++++++++++++ + src/kpatch_parse.c | 33 ------------------ + 3 files changed, 98 insertions(+), 33 deletions(-) + +diff --git a/src/arch/aarch64/arch_parse.c b/src/arch/aarch64/arch_parse.c +index abbc38c..fea09f6 100644 +--- a/src/arch/aarch64/arch_parse.c ++++ b/src/arch/aarch64/arch_parse.c +@@ -4,6 +4,70 @@ + #include "include/kpatch_parse.h" + #include "include/kpatch_flags.h" + ++int is_data_def(char *s, int type) ++{ ++ kpstr_t t; ++ ++ get_token(&s, &t); ++ if ( ++ /* strings */ ++ !kpstrcmpz(&t, ".ascii") || ++ !kpstrcmpz(&t, ".asciz") || ++ !kpstrcmpz(&t, ".string") || ++ !kpstrcmpz(&t, ".string8") || ++ !kpstrcmpz(&t, ".string16") || ++ !kpstrcmpz(&t, ".string32") || ++ !kpstrcmpz(&t, ".string64") || ++ /* numeric */ ++ !kpstrcmpz(&t, ".byte") || ++ !kpstrcmpz(&t, ".1byte") || ++ !kpstrcmpz(&t, ".dc.b") || ++ ++ !kpstrcmpz(&t, ".hword") || ++ !kpstrcmpz(&t, ".short") || ++ !kpstrcmpz(&t, ".2byte") || ++ !kpstrcmpz(&t, ".dc") || ++ !kpstrcmpz(&t, ".dc.w") || ++ !kpstrcmpz(&t, ".value") || ++ !kpstrcmpz(&t, ".octa") || ++ ++ !kpstrcmpz(&t, ".word") || ++ !kpstrcmpz(&t, ".4byte") || ++ !kpstrcmpz(&t, ".long") || ++ !kpstrcmpz(&t, ".int") || ++ !kpstrcmpz(&t, ".dc.l") || ++ !kpstrcmpz(&t, ".dc.a") ||// (AArch32 only) ++ ++ !kpstrcmpz(&t, ".quad") || ++ !kpstrcmpz(&t, ".8byte") || ++ !kpstrcmpz(&t, ".xword") ||//(AArch64 only) ++ !kpstrcmpz(&t, ".dc.a") ||//(AArch64 only) ++ ++ !kpstrcmpz(&t, ".short") || ++ !kpstrcmpz(&t, ".int") || ++ !kpstrcmpz(&t, ".long") || ++ !kpstrcmpz(&t, ".quad") || ++ /* float */ ++ !kpstrcmpz(&t, ".double") || ++ !kpstrcmpz(&t, ".dc.d") || ++ !kpstrcmpz(&t, ".float") || ++ !kpstrcmpz(&t, ".single") || ++ !kpstrcmpz(&t, ".dc.s") || ++ /* other */ ++ !kpstrcmpz(&t, ".value") || ++ !kpstrcmpz(&t, ".comm") || ++ !kpstrcmpz(&t, ".zero") || ++ !kpstrcmpz(&t, ".fill") || ++ !kpstrcmpz(&t, ".space") || ++ !kpstrcmpz(&t, ".skip") || ++ /* dwarf types */ ++ !kpstrcmpz(&t, ".uleb128") || ++ !kpstrcmpz(&t, ".sleb128") ++ ) ++ return 1; ++ return 0; ++} ++ + int is_variable_start(struct kp_file *f, int l, int *e, int *pglobl, kpstr_t *nm) + { + char *s; +diff --git a/src/arch/x86/arch_parse.c b/src/arch/x86/arch_parse.c +index ca57507..81edaf8 100644 +--- a/src/arch/x86/arch_parse.c ++++ b/src/arch/x86/arch_parse.c +@@ -4,6 +4,40 @@ + #include "include/kpatch_parse.h" + #include "include/kpatch_flags.h" + ++int is_data_def(char *s, int type) ++{ ++ kpstr_t t; ++ ++ get_token(&s, &t); ++ if ( ++ /* strings */ ++ !kpstrcmpz(&t, ".ascii") || ++ !kpstrcmpz(&t, ".asciz") || ++ !kpstrcmpz(&t, ".string") || ++ /* numeric */ ++ !kpstrcmpz(&t, ".byte") || ++ !kpstrcmpz(&t, ".word") || ++ !kpstrcmpz(&t, ".short") || ++ !kpstrcmpz(&t, ".int") || ++ !kpstrcmpz(&t, ".long") || ++ !kpstrcmpz(&t, ".quad") || ++ /* float */ ++ !kpstrcmpz(&t, ".double") || ++ !kpstrcmpz(&t, ".float") || ++ !kpstrcmpz(&t, ".single") || ++ /* other */ ++ !kpstrcmpz(&t, ".value") || ++ !kpstrcmpz(&t, ".comm") || ++ !kpstrcmpz(&t, ".zero") || ++ /* dwarf types */ ++ !kpstrcmpz(&t, ".uleb128") || ++ !kpstrcmpz(&t, ".sleb128") || ++ !kpstrcmpz(&t, ".4byte") ++ ) ++ return 1; ++ return 0; ++} ++ + int is_variable_start(struct kp_file *f, int l, int *e, int *pglobl, kpstr_t *nm) + { + char *s; +diff --git a/src/kpatch_parse.c b/src/kpatch_parse.c +index a3be7c0..f486b55 100644 +--- a/src/kpatch_parse.c ++++ b/src/kpatch_parse.c +@@ -674,37 +674,4 @@ int is_function_end(struct kp_file *f, int l, kpstr_t *nm) + return 1; + } + +-int is_data_def(char *s, int type) +-{ +- kpstr_t t; +- +- get_token(&s, &t); +- if ( +- /* strings */ +- !kpstrcmpz(&t, ".ascii") || +- !kpstrcmpz(&t, ".asciz") || +- !kpstrcmpz(&t, ".string") || +- /* numeric */ +- !kpstrcmpz(&t, ".byte") || +- !kpstrcmpz(&t, ".word") || +- !kpstrcmpz(&t, ".short") || +- !kpstrcmpz(&t, ".int") || +- !kpstrcmpz(&t, ".long") || +- !kpstrcmpz(&t, ".quad") || +- /* float */ +- !kpstrcmpz(&t, ".double") || +- !kpstrcmpz(&t, ".float") || +- !kpstrcmpz(&t, ".single") || +- /* other */ +- !kpstrcmpz(&t, ".value") || +- !kpstrcmpz(&t, ".comm") || +- !kpstrcmpz(&t, ".zero") || +- /* dwarf types */ +- !kpstrcmpz(&t, ".uleb128") || +- !kpstrcmpz(&t, ".sleb128") || +- !kpstrcmpz(&t, ".4byte") +- ) +- return 1; +- return 0; +-} + +-- +2.23.0 + diff --git a/0030-kpatch_parse-Split-function-is_function_start.patch b/0030-kpatch_parse-Split-function-is_function_start.patch new file mode 100644 index 0000000..94fd088 --- /dev/null +++ b/0030-kpatch_parse-Split-function-is_function_start.patch @@ -0,0 +1,149 @@ +From 3ecebe9ea858d5502af5f5cd79141e4546ae3fe8 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 13:58:58 +0800 +Subject: [PATCH 30/89] kpatch_parse: Split function is_function_start + +The function is_function_start is arch related, so make two +separate definations in arch/x86/arch_parse.c and arch/aarch64/arch_parse.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_parse.c | 32 ++++++++++++++++++++++++++++++++ + src/arch/x86/arch_parse.c | 32 ++++++++++++++++++++++++++++++++ + src/kpatch_parse.c | 33 --------------------------------- + 3 files changed, 64 insertions(+), 33 deletions(-) + +diff --git a/src/arch/aarch64/arch_parse.c b/src/arch/aarch64/arch_parse.c +index fea09f6..e0f0c8b 100644 +--- a/src/arch/aarch64/arch_parse.c ++++ b/src/arch/aarch64/arch_parse.c +@@ -4,6 +4,38 @@ + #include "include/kpatch_parse.h" + #include "include/kpatch_flags.h" + ++int is_function_start(struct kp_file *f, int l, kpstr_t *nm) ++{ ++ char *s; ++ kpstr_t nm2, attr; ++ int l0 = l, func = 0; ++ ++ kpstrset(nm, "", 0); ++ for (; l < f->nr_lines; l++) { ++ if (l != l0 && cline(f, l)[0] == '\0') ++ continue; ++ if ((is_sect_cmd(f, l) && is_code_sect(csect(f, l))) || ++ ctype(f, l) == DIRECTIVE_ALIGN) ++ continue; ++ get_type_args(cline(f, l), &nm2, &attr); ++ if ((ctype(f, l) == DIRECTIVE_WEAK && l0 != l) || ++ ctype(f, l) == DIRECTIVE_GLOBL || ctype(f, l) == DIRECTIVE_HIDDEN || ++ ctype(f, l) == DIRECTIVE_PROTECTED || ctype(f, l) == DIRECTIVE_INTERNAL || ++ (ctype(f, l) == DIRECTIVE_TYPE && !kpstrcmpz(&attr, "%function"))) { ++ s = cline(f, l); ++ get_token(&s, &nm2); /* skip command */ ++ get_token(&s, &nm2); ++ if (nm->l && kpstrcmp(nm, &nm2)) /* verify name matches in all .weak/.globl/.type commands */ ++ return 0; ++ *nm = nm2; ++ func = func ? 1 : ctype(f, l) == DIRECTIVE_TYPE; ++ continue; ++ } ++ break; ++ } ++ return func; ++} ++ + int is_data_def(char *s, int type) + { + kpstr_t t; +diff --git a/src/arch/x86/arch_parse.c b/src/arch/x86/arch_parse.c +index 81edaf8..c51c49b 100644 +--- a/src/arch/x86/arch_parse.c ++++ b/src/arch/x86/arch_parse.c +@@ -4,6 +4,38 @@ + #include "include/kpatch_parse.h" + #include "include/kpatch_flags.h" + ++int is_function_start(struct kp_file *f, int l, kpstr_t *nm) ++{ ++ char *s; ++ kpstr_t nm2, attr; ++ int l0 = l, func = 0; ++ ++ kpstrset(nm, "", 0); ++ for (; l < f->nr_lines; l++) { ++ if (l != l0 && cline(f, l)[0] == '\0') ++ continue; ++ if ((is_sect_cmd(f, l) && is_code_sect(csect(f, l))) || ++ ctype(f, l) == DIRECTIVE_ALIGN) ++ continue; ++ get_type_args(cline(f, l), &nm2, &attr); ++ if ((ctype(f, l) == DIRECTIVE_WEAK && l0 != l) || ++ ctype(f, l) == DIRECTIVE_GLOBL || ctype(f, l) == DIRECTIVE_HIDDEN || ++ ctype(f, l) == DIRECTIVE_PROTECTED || ctype(f, l) == DIRECTIVE_INTERNAL || ++ (ctype(f, l) == DIRECTIVE_TYPE && !kpstrcmpz(&attr, "@function"))) { ++ s = cline(f, l); ++ get_token(&s, &nm2); /* skip command */ ++ get_token(&s, &nm2); ++ if (nm->l && kpstrcmp(nm, &nm2)) /* verify name matches in all .weak/.globl/.type commands */ ++ return 0; ++ *nm = nm2; ++ func = func ? 1 : ctype(f, l) == DIRECTIVE_TYPE; ++ continue; ++ } ++ break; ++ } ++ return func; ++} ++ + int is_data_def(char *s, int type) + { + kpstr_t t; +diff --git a/src/kpatch_parse.c b/src/kpatch_parse.c +index f486b55..3df658d 100644 +--- a/src/kpatch_parse.c ++++ b/src/kpatch_parse.c +@@ -625,39 +625,6 @@ void init_sections(struct kp_file *f) + } + + /* ----------------------------------------- code block boundaries detection ---------------------------------------- */ +- +-int is_function_start(struct kp_file *f, int l, kpstr_t *nm) +-{ +- char *s; +- kpstr_t nm2, attr; +- int l0 = l, func = 0; +- +- kpstrset(nm, "", 0); +- for (; l < f->nr_lines; l++) { +- if (l != l0 && cline(f, l)[0] == '\0') +- continue; +- if ((is_sect_cmd(f, l) && is_code_sect(csect(f, l))) || +- ctype(f, l) == DIRECTIVE_ALIGN) +- continue; +- get_type_args(cline(f, l), &nm2, &attr); +- if ((ctype(f, l) == DIRECTIVE_WEAK && l0 != l) || +- ctype(f, l) == DIRECTIVE_GLOBL || ctype(f, l) == DIRECTIVE_HIDDEN || +- ctype(f, l) == DIRECTIVE_PROTECTED || ctype(f, l) == DIRECTIVE_INTERNAL || +- (ctype(f, l) == DIRECTIVE_TYPE && !kpstrcmpz(&attr, "@function"))) { +- s = cline(f, l); +- get_token(&s, &nm2); /* skip command */ +- get_token(&s, &nm2); +- if (nm->l && kpstrcmp(nm, &nm2)) /* verify name matches in all .weak/.globl/.type commands */ +- return 0; +- *nm = nm2; +- func = func ? 1 : ctype(f, l) == DIRECTIVE_TYPE; +- continue; +- } +- break; +- } +- return func; +-} +- + int is_function_end(struct kp_file *f, int l, kpstr_t *nm) + { + /* Functions should always end by .size directive. Previously used to detect .LFe labels, but they are not generated w/o frame pointers */ +-- +2.23.0 + diff --git a/0031-kpatch_common.h-Factor-out-PAGE_SIZE-marco.patch b/0031-kpatch_common.h-Factor-out-PAGE_SIZE-marco.patch new file mode 100644 index 0000000..69b760e --- /dev/null +++ b/0031-kpatch_common.h-Factor-out-PAGE_SIZE-marco.patch @@ -0,0 +1,43 @@ +From 0430a8354e988a1a72896844d45b1a5c83743d74 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 14:02:50 +0800 +Subject: [PATCH 31/89] kpatch_common.h: Factor out PAGE_SIZE marco + +Since page size may be different on OS configuration. Let's make a +change it to get PAGE_SIZE dynamicly accquired from syscall. + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/include/kpatch_common.h | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/src/include/kpatch_common.h b/src/include/kpatch_common.h +index c160250..775ea14 100644 +--- a/src/include/kpatch_common.h ++++ b/src/include/kpatch_common.h +@@ -41,4 +41,21 @@ int kpatch_close_file(struct kp_file *kpatch); + # define R_X86_64_GOTPCRELX 0x29 + #endif + ++static inline int page_shift(int n) { ++ int res = -1; ++ ++ while(n) { ++ res++; ++ n >>= 1; ++ } ++ ++ return res; ++} ++ ++#ifndef PAGE_SIZE ++#define PAGE_SIZE getpagesize() ++#define PAGE_MASK (~(PAGE_SIZE-1)) ++#define PAGE_SHIFT page_shift(PAGE_SIZE) ++#endif ++ + #endif +-- +2.23.0 + diff --git a/0032-kpatch_coro-Split-function-_UCORO_access_reg.patch b/0032-kpatch_coro-Split-function-_UCORO_access_reg.patch new file mode 100644 index 0000000..0004231 --- /dev/null +++ b/0032-kpatch_coro-Split-function-_UCORO_access_reg.patch @@ -0,0 +1,196 @@ +From ae9e01aefed4105c808301e783d29ddd349dc0f6 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 14:22:45 +0800 +Subject: [PATCH 32/89] kpatch_coro: Split function _UCORO_access_reg + +The function _UCORO_access_reg is arch related, so make two +separate definations in arch/x86/arch_coro.c and arch/aarch64/arch_coro.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_coro.c | 39 ++++++++++++++++++++++++++++++++ + src/arch/x86/arch_coro.c | 43 ++++++++++++++++++++++++++++++++++++ + src/include/kpatch_coro.h | 11 +++++++++ + src/kpatch_coro.c | 37 ------------------------------- + 4 files changed, 93 insertions(+), 37 deletions(-) + +diff --git a/src/arch/aarch64/arch_coro.c b/src/arch/aarch64/arch_coro.c +index e69de29..e6fe3d0 100644 +--- a/src/arch/aarch64/arch_coro.c ++++ b/src/arch/aarch64/arch_coro.c +@@ -0,0 +1,39 @@ ++#include ++#include ++#include ++#include ++#include ++ ++#include "include/kpatch_user.h" ++#include "include/kpatch_coro.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_ptrace.h" ++#include "include/kpatch_log.h" ++ ++int _UCORO_access_reg(unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, ++ int write, void *arg) ++{ ++ struct UCORO_info *info = (struct UCORO_info *)arg; ++ unsigned long *regs = (unsigned long *)info->coro->env[0].__jmpbuf; ++ ++ if (write) { ++ kperr("_UCORO_access_reg: write is not implemeneted (%d)\n", reg); ++ return -UNW_EINVAL; ++ } ++ switch (reg) { ++ case UNW_AARCH64_X9: ++ *val = regs[JB_RBX]; break; ++ case UNW_AARCH64_X29: ++ *val = regs[JB_RBP]; break; ++ case UNW_AARCH64_X12...UNW_AARCH64_X15: ++ *val = regs[reg - UNW_AARCH64_X12 + JB_R12]; break; ++ case UNW_AARCH64_SP: ++ *val = regs[JB_RSP]; break; ++ case UNW_AARCH64_PC: ++ *val = regs[JB_RIP]; break; ++ default: ++ return _UPT_access_reg(as, reg, val, write, arg); ++ } ++ return 0; ++} +diff --git a/src/arch/x86/arch_coro.c b/src/arch/x86/arch_coro.c +index e69de29..ce889df 100644 +--- a/src/arch/x86/arch_coro.c ++++ b/src/arch/x86/arch_coro.c +@@ -0,0 +1,43 @@ ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#include ++ ++#include "include/kpatch_user.h" ++#include "include/kpatch_coro.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_ptrace.h" ++#include "include/kpatch_log.h" ++ ++int _UCORO_access_reg(unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, ++ int write, void *arg) ++{ ++ struct UCORO_info *info = (struct UCORO_info *)arg; ++ unsigned long *regs = (unsigned long *)info->coro->env[0].__jmpbuf; ++ ++ if (write) { ++ kperr("_UCORO_access_reg: write is not implemeneted (%d)\n", reg); ++ return -UNW_EINVAL; ++ } ++ switch (reg) { ++ case UNW_X86_64_RBX: ++ *val = regs[JB_RBX]; break; ++ case UNW_X86_64_RBP: ++ *val = regs[JB_RBP]; break; ++ case UNW_X86_64_R12...UNW_X86_64_R15: ++ *val = regs[reg - UNW_X86_64_R12 + JB_R12]; break; ++ case UNW_X86_64_RSP: ++ *val = regs[JB_RSP]; break; ++ case UNW_X86_64_RIP: ++ *val = regs[JB_RIP]; break; ++ default: ++ return _UPT_access_reg(as, reg, val, write, arg); ++ } ++ return 0; ++} +diff --git a/src/include/kpatch_coro.h b/src/include/kpatch_coro.h +index 1588b5e..760b1db 100644 +--- a/src/include/kpatch_coro.h ++++ b/src/include/kpatch_coro.h +@@ -19,6 +19,17 @@ struct kpatch_coro { + void *_UCORO_create(struct kpatch_coro *coro, pid_t pid); + void _UCORO_destroy(void *arg); + ++ ++struct UCORO_info { ++ union { ++ void *upt; ++ char dummy[256]; ++ }; ++ struct kpatch_coro *coro; ++}; ++int _UCORO_access_reg(unw_addr_space_t as, unw_regnum_t reg, ++ unw_word_t *val, int write, void *arg); ++ + int kpatch_coroutines_init(struct kpatch_process *proc); + int kpatch_coroutines_find(struct kpatch_process *proc); + void kpatch_coroutines_free(struct kpatch_process *proc); +diff --git a/src/kpatch_coro.c b/src/kpatch_coro.c +index 02d421b..83d04ce 100644 +--- a/src/kpatch_coro.c ++++ b/src/kpatch_coro.c +@@ -6,8 +6,6 @@ + + #include + +-#include +- + #include "include/kpatch_user.h" + #include "include/kpatch_coro.h" + #include "include/kpatch_common.h" +@@ -505,13 +503,6 @@ static struct kpatch_coro_ops kpatch_coro_flavours[] = { + * + * That's why I had to do this hack + */ +-struct UCORO_info { +- union { +- void *upt; +- char dummy[256]; +- }; +- struct kpatch_coro *coro; +-}; + + void *_UCORO_create(struct kpatch_coro *coro, pid_t pid) + { +@@ -538,34 +529,6 @@ void _UCORO_destroy(void *arg) + _UPT_destroy(info); + } + +-static int +-_UCORO_access_reg(unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, +- int write, void *arg) +-{ +- struct UCORO_info *info = (struct UCORO_info *)arg; +- unsigned long *regs = (unsigned long *)info->coro->env[0].__jmpbuf; +- +- if (write) { +- kperr("_UCORO_access_reg: write is not implemeneted (%d)\n", reg); +- return -UNW_EINVAL; +- } +- switch (reg) { +- case UNW_X86_64_RBX: +- *val = regs[JB_RBX]; break; +- case UNW_X86_64_RBP: +- *val = regs[JB_RBP]; break; +- case UNW_X86_64_R12...UNW_X86_64_R15: +- *val = regs[reg - UNW_X86_64_R12 + JB_R12]; break; +- case UNW_X86_64_RSP: +- *val = regs[JB_RSP]; break; +- case UNW_X86_64_RIP: +- *val = regs[JB_RIP]; break; +- default: +- return _UPT_access_reg(as, reg, val, write, arg); +- } +- return 0; +-} +- + static unw_accessors_t _UCORO_accessors = { + _UPT_find_proc_info, + _UPT_put_unwind_info, +-- +2.23.0 + diff --git a/0033-kpatch_coro-Split-function-get_ptr_guard.patch b/0033-kpatch_coro-Split-function-get_ptr_guard.patch new file mode 100644 index 0000000..cdb2244 --- /dev/null +++ b/0033-kpatch_coro-Split-function-get_ptr_guard.patch @@ -0,0 +1,148 @@ +From 612e06f2fc95029c13cfdb684014259fb49f18fe Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 14:32:51 +0800 +Subject: [PATCH 33/89] kpatch_coro: Split function get_ptr_guard + +The function get_ptr_guard is arch related, so make two +separate definations in arch/x86/arch_coro.c and arch/aarch64/arch_coro.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_coro.c | 24 ++++++++++++++++++++++++ + src/arch/x86/arch_coro.c | 23 +++++++++++++++++++++++ + src/include/kpatch_coro.h | 5 +++++ + src/kpatch_coro.c | 25 ------------------------- + 4 files changed, 52 insertions(+), 25 deletions(-) + +diff --git a/src/arch/aarch64/arch_coro.c b/src/arch/aarch64/arch_coro.c +index e6fe3d0..b93581e 100644 +--- a/src/arch/aarch64/arch_coro.c ++++ b/src/arch/aarch64/arch_coro.c +@@ -11,6 +11,30 @@ + #include "include/kpatch_ptrace.h" + #include "include/kpatch_log.h" + ++int get_ptr_guard(struct kpatch_process *proc, ++ unsigned long *ptr_guard) ++{ ++ int ret; ++ unsigned long tls = 0; ++ ++ /* ++ ret = kpatch_arch_prctl_remote(proc2pctx(proc), ARCH_GET_FS, &tls); ++ if (ret < 0) { ++ kpdebug("FAIL. Can't get TLS base value\n"); ++ return -1; ++ }*/ ++ ret = kpatch_process_mem_read(proc, ++ tls + GLIBC_TLS_PTR_GUARD, ++ ptr_guard, ++ sizeof(*ptr_guard)); ++ if (ret < 0) { ++ kpdebug("FAIL. Can't get pointer guard value\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ + int _UCORO_access_reg(unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, + int write, void *arg) + { +diff --git a/src/arch/x86/arch_coro.c b/src/arch/x86/arch_coro.c +index ce889df..86bf12f 100644 +--- a/src/arch/x86/arch_coro.c ++++ b/src/arch/x86/arch_coro.c +@@ -15,6 +15,29 @@ + #include "include/kpatch_ptrace.h" + #include "include/kpatch_log.h" + ++int get_ptr_guard(struct kpatch_process *proc, ++ unsigned long *ptr_guard) ++{ ++ int ret; ++ unsigned long tls; ++ ++ ret = kpatch_arch_prctl_remote(proc2pctx(proc), ARCH_GET_FS, &tls); ++ if (ret < 0) { ++ kpdebug("FAIL. Can't get TLS base value\n"); ++ return -1; ++ } ++ ret = kpatch_process_mem_read(proc, ++ tls + GLIBC_TLS_PTR_GUARD, ++ ptr_guard, ++ sizeof(*ptr_guard)); ++ if (ret < 0) { ++ kpdebug("FAIL. Can't get pointer guard value\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ + int _UCORO_access_reg(unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, + int write, void *arg) + { +diff --git a/src/include/kpatch_coro.h b/src/include/kpatch_coro.h +index 760b1db..272855e 100644 +--- a/src/include/kpatch_coro.h ++++ b/src/include/kpatch_coro.h +@@ -30,6 +30,11 @@ struct UCORO_info { + int _UCORO_access_reg(unw_addr_space_t as, unw_regnum_t reg, + unw_word_t *val, int write, void *arg); + ++#define GLIBC_TLS_PTR_GUARD 0x30 ++int get_ptr_guard(struct kpatch_process *proc, ++ unsigned long *ptr_guard); ++ ++ + int kpatch_coroutines_init(struct kpatch_process *proc); + int kpatch_coroutines_find(struct kpatch_process *proc); + void kpatch_coroutines_free(struct kpatch_process *proc); +diff --git a/src/kpatch_coro.c b/src/kpatch_coro.c +index 83d04ce..ea4050f 100644 +--- a/src/kpatch_coro.c ++++ b/src/kpatch_coro.c +@@ -105,8 +105,6 @@ kpatch_coro_free(struct kpatch_coro *c) + #define JB_RSP 6 + #define JB_RIP 7 + +-#define GLIBC_TLS_PTR_GUARD 0x30 +- + #define STACK_OFFSET_UC_LINK (2 * sizeof(long)) + #define STACK_OFFSET_START_CONTEXT (3 * sizeof(long)) + #define STACK_OFFSET_UC_LINK_PTR (4 * sizeof(long)) +@@ -191,29 +189,6 @@ static int is_test_target(struct kpatch_process *proc, + return strcmp(proc->comm, procname) == 0; + } + +-static int get_ptr_guard(struct kpatch_process *proc, +- unsigned long *ptr_guard) +-{ +- int ret; +- unsigned long tls; +- +- ret = kpatch_arch_prctl_remote(proc2pctx(proc), ARCH_GET_FS, &tls); +- if (ret < 0) { +- kpdebug("FAIL. Can't get TLS base value\n"); +- return -1; +- } +- ret = kpatch_process_mem_read(proc, +- tls + GLIBC_TLS_PTR_GUARD, +- ptr_guard, +- sizeof(*ptr_guard)); +- if (ret < 0) { +- kpdebug("FAIL. Can't get pointer guard value\n"); +- return -1; +- } +- +- return 0; +-} +- + int is_centos7_qemu(struct kpatch_process *proc) + { + struct utsname uts; +-- +2.23.0 + diff --git a/0034-kpatch_coro-Split-function-locate_start_context_symb.patch b/0034-kpatch_coro-Split-function-locate_start_context_symb.patch new file mode 100644 index 0000000..feea29a --- /dev/null +++ b/0034-kpatch_coro-Split-function-locate_start_context_symb.patch @@ -0,0 +1,307 @@ +From 9ac8822b66bb06a463a29ec86088cfe8adc1e6d4 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 14:45:03 +0800 +Subject: [PATCH 34/89] kpatch_coro: Split function locate_start_context_symbol + +The function locate_start_context_symbol is arch related, so let's +make two separate definations in arch/x86/arch_coro.c and +arch/aarch64/arch_coro.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_coro.c | 63 ++++++++++++++++++++++++++ + src/arch/x86/arch_coro.c | 67 +++++++++++++++++++++++++++ + src/include/kpatch_coro.h | 21 +++++++++ + src/kpatch_coro.c | 87 ------------------------------------ + 4 files changed, 151 insertions(+), 87 deletions(-) + +diff --git a/src/arch/aarch64/arch_coro.c b/src/arch/aarch64/arch_coro.c +index b93581e..f485cf9 100644 +--- a/src/arch/aarch64/arch_coro.c ++++ b/src/arch/aarch64/arch_coro.c +@@ -11,6 +11,69 @@ + #include "include/kpatch_ptrace.h" + #include "include/kpatch_log.h" + ++asm ( ++ "makecontext_call:\n" ++ "mov x29, sp\n" ++ "and x29,x29,#-16\n" ++ "sub x29, x29,#0x400\n" ++ "ldr x9,[x29,#-128]\n" ++ "str x9,[fp,#0x10]\n" ++ //"str #128,[fp.#0x20]\n" ++ "mov x0,fp\n" ++ "mov x1,#0x100\n" ++ "mov x2,#0\n" ++ "svc #0\n" ++ "brk #0\n" ++ "makecontext_call_end:" ++ ); ++ ++extern unsigned char makecontext_call, makecontext_call_end; ++ ++int ++locate_start_context_symbol(struct kpatch_process *proc, ++ unsigned long *pstart_context) ++{ ++ struct object_file *olibc; ++ struct user_regs_struct regs; ++ int rv; ++ unsigned long makecontext; ++ ++ olibc = kpatch_process_get_obj_by_regex(proc, "^libc-.*\\.so"); ++ if (olibc == NULL) { ++ kpdebug("FAIL. Can't find libc\n"); ++ return -1; ++ } ++ ++ rv = kpatch_resolve_undefined_single_dynamic(olibc, ++ "makecontext", ++ &makecontext); ++ makecontext = vaddr2addr(olibc, makecontext); ++ if (rv < 0 || makecontext == 0) { ++ kpdebug("FAIL. Can't find makecontext\n"); ++ return -1; ++ } ++ ++ regs.regs[8] = makecontext; ++ rv = kpatch_execute_remote(proc2pctx(proc), ++ &makecontext_call, ++ &makecontext_call_end - &makecontext_call, ++ ®s); ++ if (rv < 0) { ++ kpdebug("FAIL. Can't execute makecontext\n"); ++ return -1; ++ } ++ ++ rv = kpatch_process_mem_read(proc, ++ regs.regs[29]- STACK_OFFSET_START_CONTEXT, ++ pstart_context, ++ sizeof(*pstart_context)); ++ if (rv < 0) { ++ kpdebug("FAIL. Can't peek __start_context address\n"); ++ return -1; ++ } ++ return rv; ++} ++ + int get_ptr_guard(struct kpatch_process *proc, + unsigned long *ptr_guard) + { +diff --git a/src/arch/x86/arch_coro.c b/src/arch/x86/arch_coro.c +index 86bf12f..27c834b 100644 +--- a/src/arch/x86/arch_coro.c ++++ b/src/arch/x86/arch_coro.c +@@ -15,6 +15,73 @@ + #include "include/kpatch_ptrace.h" + #include "include/kpatch_log.h" + ++asm ("makecontext_call:\n" ++ "mov %rsp, %rbp\n" ++ "and $-16, %rbp\n" ++ /* ucontext_t is 0x3a8 bytes */ ++ "sub $0x400, %rbp\n" ++ /* TODO interpolate these from the calculations above */ ++ ++ /* set uc_stack.ss_sp and uc_stack.ss_size */ ++ /* TODO magic -128 is used below as well */ ++ "lea -128(%rbp), %rbx\n" ++ "movq %rbx, 0x10(%rbp)\n" ++ "movq $128, 0x20(%rbp)\n" ++ "mov %rbp, %rdi\n" ++ "mov $0x100, %rsi\n" ++ "xor %rdx, %rdx\n" ++ /* call `makecontext` */ ++ "call *%rax\n" ++ "int3\n" ++ "makecontext_call_end:"); ++ ++extern unsigned char makecontext_call, makecontext_call_end; ++ ++int ++locate_start_context_symbol(struct kpatch_process *proc, ++ unsigned long *pstart_context) ++{ ++ struct object_file *olibc; ++ struct user_regs_struct regs; ++ int rv; ++ unsigned long makecontext; ++ ++ olibc = kpatch_process_get_obj_by_regex(proc, "^libc-.*\\.so"); ++ if (olibc == NULL) { ++ kpdebug("FAIL. Can't find libc\n"); ++ return -1; ++ } ++ ++ rv = kpatch_resolve_undefined_single_dynamic(olibc, ++ "makecontext", ++ &makecontext); ++ makecontext = vaddr2addr(olibc, makecontext); ++ if (rv < 0 || makecontext == 0) { ++ kpdebug("FAIL. Can't find makecontext\n"); ++ return -1; ++ } ++ ++ regs.rax = makecontext; ++ rv = kpatch_execute_remote(proc2pctx(proc), ++ &makecontext_call, ++ &makecontext_call_end - &makecontext_call, ++ ®s); ++ if (rv < 0) { ++ kpdebug("FAIL. Can't execute makecontext\n"); ++ return -1; ++ } ++ ++ rv = kpatch_process_mem_read(proc, ++ regs.rbp - STACK_OFFSET_START_CONTEXT, ++ pstart_context, ++ sizeof(*pstart_context)); ++ if (rv < 0) { ++ kpdebug("FAIL. Can't peek __start_context address\n"); ++ return -1; ++ } ++ return rv; ++} ++ + int get_ptr_guard(struct kpatch_process *proc, + unsigned long *ptr_guard) + { +diff --git a/src/include/kpatch_coro.h b/src/include/kpatch_coro.h +index 272855e..0b3a9a1 100644 +--- a/src/include/kpatch_coro.h ++++ b/src/include/kpatch_coro.h +@@ -30,10 +30,31 @@ struct UCORO_info { + int _UCORO_access_reg(unw_addr_space_t as, unw_regnum_t reg, + unw_word_t *val, int write, void *arg); + ++#define PTR_DEMANGLE(ptr, key) ((((ptr) >> 0x11) | ((ptr) << 47)) ^ key) ++#define JB_RBX 0 ++#define JB_RBP 1 ++#define JB_R12 2 ++#define JB_R13 3 ++#define JB_R14 4 ++#define JB_R15 5 ++#define JB_RSP 6 ++#define JB_RIP 7 ++ ++#define STACK_OFFSET_UC_LINK (2 * sizeof(long)) ++#define STACK_OFFSET_START_CONTEXT (3 * sizeof(long)) ++#define STACK_OFFSET_UC_LINK_PTR (4 * sizeof(long)) ++#define STACK_OFFSET_COROUTINE_UCONTEXT (7 * sizeof(long)) ++#define STACK_OFFSET_COROUTINE (8 * sizeof(long)) ++ ++#define UCONTEXT_OFFSET_JMPBUF 0x38 ++ + #define GLIBC_TLS_PTR_GUARD 0x30 + int get_ptr_guard(struct kpatch_process *proc, + unsigned long *ptr_guard); + ++int locate_start_context_symbol(struct kpatch_process *proc, ++ unsigned long *pstart_context); ++ + + int kpatch_coroutines_init(struct kpatch_process *proc); + int kpatch_coroutines_find(struct kpatch_process *proc); +diff --git a/src/kpatch_coro.c b/src/kpatch_coro.c +index ea4050f..8457800 100644 +--- a/src/kpatch_coro.c ++++ b/src/kpatch_coro.c +@@ -95,93 +95,6 @@ kpatch_coro_free(struct kpatch_coro *c) + * some kind of persistency (to allow kernelcare updates). This + * service also can listen to netlink events about new processes. + */ +-#define PTR_DEMANGLE(ptr, key) ((((ptr) >> 0x11) | ((ptr) << 47)) ^ key) +-#define JB_RBX 0 +-#define JB_RBP 1 +-#define JB_R12 2 +-#define JB_R13 3 +-#define JB_R14 4 +-#define JB_R15 5 +-#define JB_RSP 6 +-#define JB_RIP 7 +- +-#define STACK_OFFSET_UC_LINK (2 * sizeof(long)) +-#define STACK_OFFSET_START_CONTEXT (3 * sizeof(long)) +-#define STACK_OFFSET_UC_LINK_PTR (4 * sizeof(long)) +-#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) +- +-asm ("makecontext_call:\n" +- "mov %rsp, %rbp\n" +- "and $-16, %rbp\n" +- /* ucontext_t is 0x3a8 bytes */ +- "sub $0x400, %rbp\n" +- /* TODO interpolate these from the calculations above */ +- +- /* set uc_stack.ss_sp and uc_stack.ss_size */ +- /* TODO magic -128 is used below as well */ +- "lea -128(%rbp), %rbx\n" +- "movq %rbx, 0x10(%rbp)\n" +- "movq $128, 0x20(%rbp)\n" +- "mov %rbp, %rdi\n" +- "mov $0x100, %rsi\n" +- "xor %rdx, %rdx\n" +- /* call `makecontext` */ +- "call *%rax\n" +- "int3\n" +- "makecontext_call_end:"); +- +-extern unsigned char makecontext_call, makecontext_call_end; +- +-static int +-locate_start_context_symbol(struct kpatch_process *proc, +- unsigned long *pstart_context) +-{ +- struct object_file *olibc; +- struct user_regs_struct regs; +- int rv; +- unsigned long makecontext; +- +- olibc = kpatch_process_get_obj_by_regex(proc, "^libc-.*\\.so"); +- if (olibc == NULL) { +- kpdebug("FAIL. Can't find libc\n"); +- return -1; +- } +- +- rv = kpatch_resolve_undefined_single_dynamic(olibc, +- "makecontext", +- &makecontext); +- makecontext = vaddr2addr(olibc, makecontext); +- if (rv < 0 || makecontext == 0) { +- kpdebug("FAIL. Can't find makecontext\n"); +- return -1; +- } +- +- regs.rax = makecontext; +- rv = kpatch_execute_remote(proc2pctx(proc), +- &makecontext_call, +- &makecontext_call_end - &makecontext_call, +- ®s); +- if (rv < 0) { +- kpdebug("FAIL. Can't execute makecontext\n"); +- return -1; +- } +- +- rv = kpatch_process_mem_read(proc, +- regs.rbp - STACK_OFFSET_START_CONTEXT, +- pstart_context, +- sizeof(*pstart_context)); +- if (rv < 0) { +- kpdebug("FAIL. Can't peek __start_context address\n"); +- return -1; +- } +- return rv; +-} + + static int is_test_target(struct kpatch_process *proc, + const char *procname) +-- +2.23.0 + diff --git a/0035-kpatch_patch-Split-function-patch_apply_hunk.patch b/0035-kpatch_patch-Split-function-patch_apply_hunk.patch new file mode 100644 index 0000000..8a57997 --- /dev/null +++ b/0035-kpatch_patch-Split-function-patch_apply_hunk.patch @@ -0,0 +1,237 @@ +From 4f165eacd6d1d64cc43a58dd54e35017663d99e2 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 14:58:18 +0800 +Subject: [PATCH 35/89] kpatch_patch: Split function patch_apply_hunk + +The function patch_apply_hunk is arch related, so make two +separate definations in arch/x86/arch_patch.c and arch/aarch64/arch_patch.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_patch.c | 68 +++++++++++++++++++++++++++++++++++ + src/arch/x86/arch_patch.c | 66 ++++++++++++++++++++++++++++++++++ + src/include/kpatch_patch.h | 2 ++ + src/kpatch_patch.c | 44 ++--------------------- + 4 files changed, 139 insertions(+), 41 deletions(-) + +diff --git a/src/arch/aarch64/arch_patch.c b/src/arch/aarch64/arch_patch.c +index e69de29..9102621 100644 +--- a/src/arch/aarch64/arch_patch.c ++++ b/src/arch/aarch64/arch_patch.c +@@ -0,0 +1,68 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "include/kpatch_patch.h" ++#include "include/kpatch_user.h" ++#include "include/kpatch_storage.h" ++#include "include/kpatch_process.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_ptrace.h" ++#include "include/list.h" ++#include "include/kpatch_log.h" ++ ++ ++/***************************************************************************** ++ * Patch application subroutines ++ ****************************************************************************/ ++/* ++ * This flag is local, i.e. it is never stored to the ++ * patch applied to patient's memory. ++ */ ++int PATCH_APPLIED = (1 << 31); ++int HUNK_SIZE = 4; ++ ++int patch_apply_hunk(struct object_file *o, size_t nhunk) ++{ ++ int ret; ++ unsigned char code[] = {0x00, 0x00, 0x00, 0x00}; /* ins: b 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) = (unsigned int)(info->saddr - info->daddr) / 4; ++ code[3] &= 0x3; ++ code[3] |= 0x14; ++ ++ 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; ++} +diff --git a/src/arch/x86/arch_patch.c b/src/arch/x86/arch_patch.c +index e69de29..a6e794d 100644 +--- a/src/arch/x86/arch_patch.c ++++ b/src/arch/x86/arch_patch.c +@@ -0,0 +1,66 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "include/kpatch_patch.h" ++#include "include/kpatch_user.h" ++#include "include/kpatch_storage.h" ++#include "include/kpatch_process.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_ptrace.h" ++#include "include/list.h" ++#include "include/kpatch_log.h" ++ ++/***************************************************************************** ++ * Patch application subroutines ++ ****************************************************************************/ ++/* ++ * This flag is local, i.e. it is never stored to the ++ * patch applied to patient's memory. ++ */ ++int PATCH_APPLIED = (1 << 31); ++int HUNK_SIZE = 5; ++ ++int patch_apply_hunk(struct object_file *o, size_t nhunk) ++{ ++ int ret; ++ char code[] = {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; ++} ++ +diff --git a/src/include/kpatch_patch.h b/src/include/kpatch_patch.h +index 44806ab..fa96b08 100644 +--- a/src/include/kpatch_patch.h ++++ b/src/include/kpatch_patch.h +@@ -25,4 +25,6 @@ struct unpatch_data { + int process_patch(int pid, void *_data); + int process_unpatch(int pid, void *_data); + ++int patch_apply_hunk(struct object_file *o, size_t nhunk); ++ + #endif +diff --git a/src/kpatch_patch.c b/src/kpatch_patch.c +index 21a160a..4a1d149 100644 +--- a/src/kpatch_patch.c ++++ b/src/kpatch_patch.c +@@ -270,47 +270,6 @@ patch_ensure_safety(struct object_file *o, + /***************************************************************************** + * 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) +@@ -328,6 +287,9 @@ duplicate_kp_file(struct object_file *o) + return 0; + } + ++extern int PATCH_APPLIED; ++extern int HUNK_SIZE; ++ + static int + object_apply_patch(struct object_file *o) + { +-- +2.23.0 + diff --git a/0036-kpatch_elf-Split-function-kpatch_add_jmp_entry.patch b/0036-kpatch_elf-Split-function-kpatch_add_jmp_entry.patch new file mode 100644 index 0000000..68bc71a --- /dev/null +++ b/0036-kpatch_elf-Split-function-kpatch_add_jmp_entry.patch @@ -0,0 +1,151 @@ +From 3352c27078a63b5bfc6ff4df639489fdabfd4dbe Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 15:11:05 +0800 +Subject: [PATCH 36/89] kpatch_elf: Split function kpatch_add_jmp_entry + +The function kpatch_add_jmp_entry is arch related. To support multi-arch +let's rename it with kpatch_arch_add_jmp_entry, and make the defination in +arch/x86/arch_elf.c and arch/aarch64/arch_elf.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_elf.c | 35 +++++++++++++++++++++++++++++++++++ + src/arch/x86/arch_elf.c | 35 +++++++++++++++++++++++++++++++++++ + src/include/kpatch_elf.h | 2 ++ + src/kpatch_elf.c | 21 +-------------------- + 4 files changed, 73 insertions(+), 20 deletions(-) + +diff --git a/src/arch/aarch64/arch_elf.c b/src/arch/aarch64/arch_elf.c +index e69de29..b977489 100644 +--- a/src/arch/aarch64/arch_elf.c ++++ b/src/arch/aarch64/arch_elf.c +@@ -0,0 +1,35 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "include/kpatch_common.h" ++#include "include/kpatch_user.h" ++#include "include/kpatch_process.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_ptrace.h" ++#include "include/kpatch_log.h" ++ ++#define JMP_TABLE_JUMP 0xd61f022058000051 /* ldr x17 #8; br x17 */ ++unsigned long kpatch_arch_add_jmp_entry(struct object_file *o, unsigned long addr) ++{ ++ struct kpatch_jmp_table_entry entry = {JMP_TABLE_JUMP, addr}; ++ int e; ++ ++ if (o->jmp_table == NULL) { ++ kpfatalerror("JMP TABLE not found\n"); ++ return 0; ++ } ++ ++ if (o->jmp_table->cur_entry >= o->jmp_table->max_entry) ++ return 0; ++ e = o->jmp_table->cur_entry++; ++ o->jmp_table->entries[e] = entry; ++ return (unsigned long)(o->kpta + o->kpfile.patch->jmp_offset + \ ++ ((void *)&o->jmp_table->entries[e] - (void *)o->jmp_table)); ++} +diff --git a/src/arch/x86/arch_elf.c b/src/arch/x86/arch_elf.c +index e69de29..ef5564e 100644 +--- a/src/arch/x86/arch_elf.c ++++ b/src/arch/x86/arch_elf.c +@@ -0,0 +1,35 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "include/kpatch_common.h" ++#include "include/kpatch_user.h" ++#include "include/kpatch_process.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_ptrace.h" ++#include "include/kpatch_log.h" ++ ++#define JMP_TABLE_JUMP 0x90900000000225ff /* jmp [rip+2]; nop; nop */ ++unsigned long kpatch_arch_add_jmp_entry(struct object_file *o, unsigned long addr) ++{ ++ struct kpatch_jmp_table_entry entry = {JMP_TABLE_JUMP, addr}; ++ int e; ++ ++ if (o->jmp_table == NULL) { ++ kpfatalerror("JMP TABLE not found\n"); ++ return 0; ++ } ++ ++ if (o->jmp_table->cur_entry >= o->jmp_table->max_entry) ++ return 0; ++ e = o->jmp_table->cur_entry++; ++ o->jmp_table->entries[e] = entry; ++ return (unsigned long)(o->kpta + o->kpfile.patch->jmp_offset + \ ++ ((void *)&o->jmp_table->entries[e] - (void *)o->jmp_table)); ++} +diff --git a/src/include/kpatch_elf.h b/src/include/kpatch_elf.h +index 8c0a4a4..7e5d8c3 100644 +--- a/src/include/kpatch_elf.h ++++ b/src/include/kpatch_elf.h +@@ -43,4 +43,6 @@ struct kpatch_jmp_table { + struct kpatch_jmp_table_entry entries[0]; + }; + ++unsigned long kpatch_arch_add_jmp_entry(struct object_file *o, unsigned long addr); ++ + #endif +diff --git a/src/kpatch_elf.c b/src/kpatch_elf.c +index b1dfed0..21ba496 100644 +--- a/src/kpatch_elf.c ++++ b/src/kpatch_elf.c +@@ -686,25 +686,6 @@ kpatch_resolve_undefined(struct object_file *obj, + return addr; + } + +-#define JMP_TABLE_JUMP 0x90900000000225ff /* jmp [rip+2]; nop; nop */ +-static unsigned long kpatch_add_jmp_entry(struct object_file *o, unsigned long addr) +-{ +- struct kpatch_jmp_table_entry entry = {JMP_TABLE_JUMP, addr}; +- int e; +- +- if (o->jmp_table == NULL) { +- kpfatalerror("JMP TABLE not found\n"); +- return 0; +- } +- +- if (o->jmp_table->cur_entry >= o->jmp_table->max_entry) +- return 0; +- e = o->jmp_table->cur_entry++; +- o->jmp_table->entries[e] = entry; +- return (unsigned long)(o->kpta + o->kpfile.patch->jmp_offset + \ +- ((void *)&o->jmp_table->entries[e] - (void *)o->jmp_table)); +-} +- + static inline int + symbol_resolve(struct object_file *o, + GElf_Shdr *shdr, +@@ -737,7 +718,7 @@ symbol_resolve(struct object_file *o, + } + /* OK, we overuse st_size to store original offset */ + s->st_size = uaddr; +- s->st_value = kpatch_add_jmp_entry(o, uaddr); ++ s->st_value = kpatch_arch_add_jmp_entry(o, uaddr); + + kpdebug("symbol '%s' = 0x%lx\n", + symname, uaddr); +-- +2.23.0 + diff --git a/0037-kpatch_elf-Split-function-kpatch_apply_relocate_add.patch b/0037-kpatch_elf-Split-function-kpatch_apply_relocate_add.patch new file mode 100644 index 0000000..66916c3 --- /dev/null +++ b/0037-kpatch_elf-Split-function-kpatch_apply_relocate_add.patch @@ -0,0 +1,414 @@ +From 69837926282fc65d41be15390d9a125da97adc54 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 15:37:08 +0800 +Subject: [PATCH 37/89] kpatch_elf: Split function kpatch_apply_relocate_add + +The function kpatch_apply_relocate_add is arch related. To support multi-arch +let's rename it with kpatch_arch_apply_relocate_add, and make the defination +in arch/x86/arch_elf.c and arch/aarch64/arch_elf.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_elf.c | 100 ++++++++++++++++++++++++++++++++++++ + src/arch/x86/arch_elf.c | 89 ++++++++++++++++++++++++++++++++ + src/include/bitops.h | 27 ++++++++++ + src/include/kpatch_elf.h | 5 ++ + src/kpatch_elf.c | 94 ++------------------------------- + 5 files changed, 224 insertions(+), 91 deletions(-) + create mode 100644 src/include/bitops.h + +diff --git a/src/arch/aarch64/arch_elf.c b/src/arch/aarch64/arch_elf.c +index b977489..deacb6f 100644 +--- a/src/arch/aarch64/arch_elf.c ++++ b/src/arch/aarch64/arch_elf.c +@@ -14,6 +14,106 @@ + #include "include/kpatch_file.h" + #include "include/kpatch_ptrace.h" + #include "include/kpatch_log.h" ++#include "include/bitops.h" ++ ++static int kpatch_arch_apply_relocate(GElf_Rela *r, GElf_Sym *s, ++ void *loc, void *loc2, unsigned long val) ++{ ++ switch (GELF_R_TYPE(r->r_info)) { ++ case R_AARCH64_ABS64: ++ *(unsigned long *)loc = val; ++ kpdebug("R_AARCH64_ABS64: loc=0x%x, val =0x%lx\n",*(unsigned int*)loc,val); ++ break; ++ case R_AARCH64_ADD_ABS_LO12_NC: { ++ //ADD ins ++ kpdebug("R_AARCH64_ADD_ABS_LO12_NC: val=0x%lx\n", val); ++ val = val & 0xfff; ++ uint32_t mask = 0xfff << 10; ++ *(unsigned int*)loc &= ~mask; ++ or_32(loc, (val & 0xfff) << 10); ++ kpdebug("R_AARCH64_ADD_ABS_LO12_NC: loc=0x%x, val =0x%lx\n",*(unsigned int*)loc,val); ++ break; ++ } ++ case R_AARCH64_CALL26: { ++ // TODO bl ins ++ kpdebug("R_AARCH64_CALL26: val=0x%lx\n", val); ++ val -= (unsigned long)loc2; ++ uint32_t mask = 0x03FFFFFF;; ++ *(unsigned int*)loc &= ~mask; ++ or_32(loc, (val >> 2) & mask); ++ kpdebug("R_AARCH64_CALL26: loc=0x%x, val =0x%lx\n",*(unsigned int*)loc, val); ++ break; ++ } ++ case R_AARCH64_ADR_PREL_PG_HI21: { ++ // TODO ADRP ins ++ kpdebug("RR_AARCH64_ADR_PREL_PG_HI21: val=0x%lx\n", val); ++ val = (val >> 12) - ((unsigned long)loc2 >> 12); ++ kpdebug("val=0x%lx\n",val); ++ uint32_t immLo = (val & 0x3) << 29; ++ uint32_t immHi = (val & 0x1FFFFC) << 3; ++ uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3); ++ *(unsigned int*)loc = (*(unsigned int*)loc & ~mask) | immLo | immHi; ++ //*(unsigned int*)loc &= 0x7fffffff; ++ kpdebug("lo=0x%x hi=0x%x\n",immLo,immHi); ++ kpdebug("R_AARCH64_ADR_PREL_PG_HI21: loc=0x%x, val=0x%lx\n", *(unsigned int *)loc, val); ++ break; ++ } ++ default: ++ kperr("unknown relocation type: %lx\n", r->r_info); ++ return -1; ++ } ++ return 0; ++} ++ ++int kpatch_arch_apply_relocate_add(struct object_file *o, GElf_Shdr *relsec) ++{ ++ struct kpatch_file *kp = o->kpfile.patch; ++ GElf_Ehdr *ehdr = (void *)kp + kp->kpatch_offset; ++ GElf_Shdr *shdr = (void *)ehdr + ehdr->e_shoff, *symhdr; ++ GElf_Rela *relocs = (void *)ehdr + relsec->sh_offset; ++ GElf_Shdr *tshdr = shdr + relsec->sh_info; ++ void *t = (void *)ehdr + shdr[relsec->sh_info].sh_offset; ++ void *tshdr2 = (void *)shdr[relsec->sh_info].sh_addr; ++ int i, is_kpatch_info; ++ const char *scnname; ++ ++ for (i = 1; i < ehdr->e_shnum; i++) { ++ if (shdr[i].sh_type == SHT_SYMTAB) ++ symhdr = &shdr[i]; ++ } ++ ++ scnname = secname(ehdr, shdr + relsec->sh_info); ++ kpdebug("applying relocations to '%s'\n", scnname); ++ is_kpatch_info = strcmp(scnname, ".kpatch.info") == 0; ++ ++ for (i = 0; i < relsec->sh_size / sizeof(*relocs); i++) { ++ GElf_Rela *r = relocs + i; ++ GElf_Sym *s; ++ unsigned long val; ++ void *loc, *loc2; ++ ++ if (r->r_offset < 0 || r->r_offset >= tshdr->sh_size) ++ kpfatalerror("Relocation offset for section '%s'" ++ " is at 0x%lx beyond the section size 0x%lx\n", ++ scnname, r->r_offset, tshdr->sh_size); ++ ++ /* Location in our address space */ ++ loc = t + r->r_offset; ++ /* Location in target process address space (for relative addressing) */ ++ loc2 = tshdr2 + r->r_offset; ++ s = (GElf_Sym *)((void *)ehdr + symhdr->sh_offset) + GELF_R_SYM(r->r_info); ++ val = s->st_value + r->r_addend; ++ ++ if (is_kpatch_info && is_undef_symbol(s)) { ++ val = s->st_size; ++ } ++ ++ kpatch_arch_apply_relocate(r, s, loc, loc2, val); ++ } ++ ++ return 0; ++} ++ + + #define JMP_TABLE_JUMP 0xd61f022058000051 /* ldr x17 #8; br x17 */ + unsigned long kpatch_arch_add_jmp_entry(struct object_file *o, unsigned long addr) +diff --git a/src/arch/x86/arch_elf.c b/src/arch/x86/arch_elf.c +index ef5564e..52de117 100644 +--- a/src/arch/x86/arch_elf.c ++++ b/src/arch/x86/arch_elf.c +@@ -15,6 +15,95 @@ + #include "include/kpatch_ptrace.h" + #include "include/kpatch_log.h" + ++int kpatch_arch_apply_relocate_add(struct object_file *o, GElf_Shdr *relsec) ++{ ++ struct kpatch_file *kp = o->kpfile.patch; ++ GElf_Ehdr *ehdr = (void *)kp + kp->kpatch_offset; ++ GElf_Shdr *shdr = (void *)ehdr + ehdr->e_shoff, *symhdr; ++ GElf_Rela *relocs = (void *)ehdr + relsec->sh_offset; ++ GElf_Shdr *tshdr = shdr + relsec->sh_info; ++ void *t = (void *)ehdr + shdr[relsec->sh_info].sh_offset; ++ void *tshdr2 = (void *)shdr[relsec->sh_info].sh_addr; ++ int i, is_kpatch_info; ++ const char *scnname; ++ ++ for (i = 1; i < ehdr->e_shnum; i++) { ++ if (shdr[i].sh_type == SHT_SYMTAB) ++ symhdr = &shdr[i]; ++ } ++ ++ scnname = secname(ehdr, shdr + relsec->sh_info); ++ kpdebug("applying relocations to '%s'\n", scnname); ++ is_kpatch_info = strcmp(scnname, ".kpatch.info") == 0; ++ ++ for (i = 0; i < relsec->sh_size / sizeof(*relocs); i++) { ++ GElf_Rela *r = relocs + i; ++ GElf_Sym *s; ++ unsigned long val; ++ void *loc, *loc2; ++ ++ if (r->r_offset < 0 || r->r_offset >= tshdr->sh_size) ++ kpfatalerror("Relocation offset for section '%s'" ++ " is at 0x%lx beyond the section size 0x%lx\n", ++ scnname, r->r_offset, tshdr->sh_size); ++ ++ /* Location in our address space */ ++ loc = t + r->r_offset; ++ /* Location in target process address space (for relative addressing) */ ++ loc2 = tshdr2 + r->r_offset; ++ s = (GElf_Sym *)((void *)ehdr + symhdr->sh_offset) + GELF_R_SYM(r->r_info); ++ val = s->st_value + r->r_addend; ++ ++ if (is_kpatch_info && is_undef_symbol(s)) { ++ val = s->st_size; ++ } ++ ++ switch (GELF_R_TYPE(r->r_info)) { ++ case R_X86_64_NONE: ++ break; ++ case R_X86_64_64: ++ *(unsigned long *)loc = val; ++ break; ++ case R_X86_64_32: ++ *(unsigned int *)loc = val; ++ break; ++ case R_X86_64_32S: ++ *(signed int *)loc = val; ++ break; ++ case R_X86_64_GOTTPOFF: ++ case R_X86_64_GOTPCREL: ++ case R_X86_64_REX_GOTPCRELX: ++ case R_X86_64_GOTPCRELX: ++ if (is_undef_symbol(s)) { ++ /* This is an undefined symbol, ++ * use jmp table as the GOT */ ++ val += sizeof(unsigned long); ++ } else if (GELF_ST_TYPE(s->st_info) == STT_TLS) { ++ /* This is GOTTPOFF that already points ++ * to an appropriate GOT entry in the ++ * patient's memory. ++ */ ++ val = r->r_addend + o->load_offset - 4; ++ } ++ /* FALLTHROUGH */ ++ case R_X86_64_PC32: ++ val -= (unsigned long)loc2; ++ *(unsigned int *)loc = val; ++ break; ++ case R_X86_64_TPOFF64: ++ case R_X86_64_TPOFF32: ++ kperr("TPOFF32/TPOFF64 should not be present\n"); ++ break; ++ default: ++ kperr("unknown relocation type: %lx\n", r->r_info); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++ + #define JMP_TABLE_JUMP 0x90900000000225ff /* jmp [rip+2]; nop; nop */ + unsigned long kpatch_arch_add_jmp_entry(struct object_file *o, unsigned long addr) + { +diff --git a/src/include/bitops.h b/src/include/bitops.h +new file mode 100644 +index 0000000..aab1679 +--- /dev/null ++++ b/src/include/bitops.h +@@ -0,0 +1,27 @@ ++#ifndef BITOPS_H ++#define BITOPS_H ++ ++#define BITS_PER_BYTE CHAR_BIT ++#define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) ++ ++#define BIT(nr) (1UL << (nr)) ++#define BIT_ULL(nr) (1ULL << (nr)) ++#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) ++#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) ++#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) ++ ++static inline void or_32(void *addr, unsigned int val) ++{ ++ *(unsigned int*) addr = *(unsigned int*)addr | val; ++} ++ ++static inline void or_64(void *addr, unsigned long val) ++{ ++ *(unsigned long*) addr = *(unsigned long*)addr | val; ++} ++ ++static inline void and_32(void *addr, unsigned int val) ++{ ++ *(unsigned int*) addr = *(unsigned int*)addr & val; ++} ++#endif +diff --git a/src/include/kpatch_elf.h b/src/include/kpatch_elf.h +index 7e5d8c3..74efe04 100644 +--- a/src/include/kpatch_elf.h ++++ b/src/include/kpatch_elf.h +@@ -1,6 +1,7 @@ + #ifndef __KPATCH_ELF__ + #define __KPATCH_ELF__ + ++#include + #include "kpatch_process.h" + + const char *kpatch_get_buildid(struct object_file *o); +@@ -45,4 +46,8 @@ struct kpatch_jmp_table { + + unsigned long kpatch_arch_add_jmp_entry(struct object_file *o, unsigned long addr); + ++char *secname(GElf_Ehdr *ehdr, GElf_Shdr *s); ++int is_undef_symbol(const Elf64_Sym *sym); ++int kpatch_arch_apply_relocate_add(struct object_file *o, GElf_Shdr *relsec); ++ + #endif +diff --git a/src/kpatch_elf.c b/src/kpatch_elf.c +index 21ba496..5506292 100644 +--- a/src/kpatch_elf.c ++++ b/src/kpatch_elf.c +@@ -410,7 +410,7 @@ out: + return rv; + } + +-static char *secname(GElf_Ehdr *ehdr, GElf_Shdr *s) ++char *secname(GElf_Ehdr *ehdr, GElf_Shdr *s) + { + GElf_Shdr *shdr = (void *)ehdr + ehdr->e_shoff; + char *str = (void *)ehdr + shdr[ehdr->e_shstrndx].sh_offset; +@@ -450,7 +450,7 @@ struct kpatch_jmp_table *kpatch_new_jmp_table(int entries) + return jtbl; + } + +-static inline int ++inline int + is_undef_symbol(const Elf64_Sym *sym) + { + return sym->st_shndx == SHN_UNDEF || sym->st_shndx >= SHN_LORESERVE; +@@ -805,94 +805,6 @@ int kpatch_resolve(struct object_file *o) + return 0; + } + +-static int kpatch_apply_relocate_add(struct object_file *o, GElf_Shdr *relsec) +-{ +- struct kpatch_file *kp = o->kpfile.patch; +- GElf_Ehdr *ehdr = (void *)kp + kp->kpatch_offset; +- GElf_Shdr *shdr = (void *)ehdr + ehdr->e_shoff, *symhdr; +- GElf_Rela *relocs = (void *)ehdr + relsec->sh_offset; +- GElf_Shdr *tshdr = shdr + relsec->sh_info; +- void *t = (void *)ehdr + shdr[relsec->sh_info].sh_offset; +- void *tshdr2 = (void *)shdr[relsec->sh_info].sh_addr; +- int i, is_kpatch_info; +- const char *scnname; +- +- for (i = 1; i < ehdr->e_shnum; i++) { +- if (shdr[i].sh_type == SHT_SYMTAB) +- symhdr = &shdr[i]; +- } +- +- scnname = secname(ehdr, shdr + relsec->sh_info); +- kpdebug("applying relocations to '%s'\n", scnname); +- is_kpatch_info = strcmp(scnname, ".kpatch.info") == 0; +- +- for (i = 0; i < relsec->sh_size / sizeof(*relocs); i++) { +- GElf_Rela *r = relocs + i; +- GElf_Sym *s; +- unsigned long val; +- void *loc, *loc2; +- +- if (r->r_offset < 0 || r->r_offset >= tshdr->sh_size) +- kpfatalerror("Relocation offset for section '%s'" +- " is at 0x%lx beyond the section size 0x%lx\n", +- scnname, r->r_offset, tshdr->sh_size); +- +- /* Location in our address space */ +- loc = t + r->r_offset; +- /* Location in target process address space (for relative addressing) */ +- loc2 = tshdr2 + r->r_offset; +- s = (GElf_Sym *)((void *)ehdr + symhdr->sh_offset) + GELF_R_SYM(r->r_info); +- val = s->st_value + r->r_addend; +- +- if (is_kpatch_info && is_undef_symbol(s)) { +- val = s->st_size; +- } +- +- switch (GELF_R_TYPE(r->r_info)) { +- case R_X86_64_NONE: +- break; +- case R_X86_64_64: +- *(unsigned long *)loc = val; +- break; +- case R_X86_64_32: +- *(unsigned int *)loc = val; +- break; +- case R_X86_64_32S: +- *(signed int *)loc = val; +- break; +- case R_X86_64_GOTTPOFF: +- case R_X86_64_GOTPCREL: +- case R_X86_64_REX_GOTPCRELX: +- case R_X86_64_GOTPCRELX: +- if (is_undef_symbol(s)) { +- /* This is an undefined symbol, +- * use jmp table as the GOT */ +- val += sizeof(unsigned long); +- } else if (GELF_ST_TYPE(s->st_info) == STT_TLS) { +- /* This is GOTTPOFF that already points +- * to an appropriate GOT entry in the +- * patient's memory. +- */ +- val = r->r_addend + o->load_offset - 4; +- } +- /* FALLTHROUGH */ +- case R_X86_64_PC32: +- val -= (unsigned long)loc2; +- *(unsigned int *)loc = val; +- break; +- case R_X86_64_TPOFF64: +- case R_X86_64_TPOFF32: +- kperr("TPOFF32/TPOFF64 should not be present\n"); +- break; +- default: +- kperr("unknown relocation type: %lx\n", r->r_info); +- return -1; +- } +- } +- +- return 0; +-} +- + int kpatch_relocate(struct object_file *o) + { + GElf_Ehdr *ehdr; +@@ -907,7 +819,7 @@ int kpatch_relocate(struct object_file *o) + GElf_Shdr *s = shdr + i; + + if (s->sh_type == SHT_RELA) +- ret = kpatch_apply_relocate_add(o, s); ++ ret = kpatch_arch_apply_relocate_add(o, s); + else if (shdr->sh_type == SHT_REL) { + kperr("TODO: handle SHT_REL\n"); + return -1; +-- +2.23.0 + diff --git a/0038-kpatch_process-Split-function-object_find_patch_regi.patch b/0038-kpatch_process-Split-function-object_find_patch_regi.patch new file mode 100644 index 0000000..9e830b7 --- /dev/null +++ b/0038-kpatch_process-Split-function-object_find_patch_regi.patch @@ -0,0 +1,416 @@ +From 80c479726361710a9ac4f328687796a183cf780f Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 15:56:42 +0800 +Subject: [PATCH 38/89] kpatch_process: Split function object_find_patch_region + +The function object_find_patch_region is arch related. Since process +virtual address layout may be different between x86 and aarch64, +let's make two separate definations in arch/x86/arch_process.c and +arch/aarch64/arch_process.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch.desc | 1 + + src/arch/aarch64/arch_process.c | 120 ++++++++++++++++++++++++++++++++ + src/arch/x86/arch_process.c | 108 ++++++++++++++++++++++++++++ + src/include/kpatch_process.h | 9 +++ + src/kpatch_process.c | 87 ++--------------------- + 5 files changed, 242 insertions(+), 83 deletions(-) + create mode 100644 src/arch.desc + create mode 100644 src/arch/aarch64/arch_process.c + create mode 100644 src/arch/x86/arch_process.c + +diff --git a/src/arch.desc b/src/arch.desc +new file mode 100644 +index 0000000..9647742 +--- /dev/null ++++ b/src/arch.desc +@@ -0,0 +1 @@ ++aarch64 +diff --git a/src/arch/aarch64/arch_process.c b/src/arch/aarch64/arch_process.c +new file mode 100644 +index 0000000..3a64d77 +--- /dev/null ++++ b/src/arch/aarch64/arch_process.c +@@ -0,0 +1,120 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "include/kpatch_process.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_ptrace.h" ++#include "include/list.h" ++#include "include/kpatch_log.h" ++ ++/* ++ * Find region for a patch. Take object's `previous_hole` as a left candidate ++ * and the next hole as a right candidate. Pace through them until there is ++ * enough space in the hole for the patch. ++ * ++ * Since holes can be much larger than 2GiB take extra caution to allocate ++ * patch region inside the (-2GiB, +2GiB) range from the original object. ++ */ ++unsigned long ++object_find_patch_region(struct object_file *obj, ++ size_t memsize, ++ struct vm_hole **hole) ++{ ++ struct list_head *head = &obj->proc->vmaholes; ++ struct vm_hole *left_hole = obj->previous_hole, ++ *right_hole = next_hole(left_hole, head); ++ unsigned long max_distance = 0x80000000; ++ struct obj_vm_area *sovma; ++ ++ unsigned long obj_start, obj_end; ++ unsigned long region_start = 0, region_end = 0; ++ ++ kpdebug("Looking for patch region for '%s'...\n", obj->name); ++ ++ sovma = list_first_entry(&obj->vma, struct obj_vm_area, list); ++ obj_start = sovma->inmem.start; ++ sovma = list_entry(obj->vma.prev, struct obj_vm_area, list); ++ obj_end = sovma->inmem.end; ++ ++ ++ max_distance -= memsize; ++ ++ /* TODO carefully check for the holes laying between obj_start and ++ * obj_end, i.e. just after the executable segment of an executable ++ */ ++ while (left_hole != NULL && right_hole != NULL) { ++ if (right_hole != NULL && ++ right_hole->start - obj_start > max_distance) ++ right_hole = NULL; ++ else if (hole_size(right_hole) > memsize) { ++ region_start = right_hole->start; ++ region_end = ++ (right_hole->end - obj_start) <= max_distance ? ++ right_hole->end - memsize : ++ obj_start + max_distance; ++ *hole = right_hole; ++ break; ++ } else ++ right_hole = next_hole(right_hole, head); ++ ++ if (left_hole != NULL && ++ obj_end - left_hole->end > max_distance) ++ left_hole = NULL; ++ else if (hole_size(left_hole) > memsize) { ++ region_start = ++ (left_hole->start - obj_end) <= max_distance ? ++ left_hole->start : obj_end > max_distance ? ++ obj_end - max_distance : 0; ++ region_end = left_hole->end - memsize; ++ *hole = left_hole; ++ break; ++ } else ++ left_hole = prev_hole(left_hole, head); ++ } ++ ++ if (region_start == region_end) { ++ kperr("can't find suitable region for patch on '%s'\n", ++ obj->name); ++ return -1UL; ++ } ++ ++ /* ++ * On aarch64, virtual address of text and data segments may be continuous, ++ * gap between data segment and process heap may be huge. Need to have ++ * region_end fixed. Here goes the trick: ++ * The branch instruction jump size is in the range of +/-128MB. ++ * So we need to put limitation to the region_end. ++ */ ++ region_end = region_start + (0x1<<25); ++ region_start = random_from_range(region_start >> PAGE_SHIFT, ++ region_end >> PAGE_SHIFT); ++ region_start <<= PAGE_SHIFT; ++ kpdebug("Found patch region for '%s' at %lx\n", obj->name, region_start); ++ ++ return region_start; ++} ++ +diff --git a/src/arch/x86/arch_process.c b/src/arch/x86/arch_process.c +new file mode 100644 +index 0000000..ba66134 +--- /dev/null ++++ b/src/arch/x86/arch_process.c +@@ -0,0 +1,108 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "include/kpatch_process.h" ++#include "include/kpatch_file.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_elf.h" ++#include "include/kpatch_ptrace.h" ++#include "include/list.h" ++#include "include/kpatch_log.h" ++ ++/* ++ * Find region for a patch. Take object's `previous_hole` as a left candidate ++ * and the next hole as a right candidate. Pace through them until there is ++ * enough space in the hole for the patch. ++ * ++ * Since holes can be much larger than 2GiB take extra caution to allocate ++ * patch region inside the (-2GiB, +2GiB) range from the original object. ++ */ ++unsigned long object_find_patch_region(struct object_file *obj, ++ size_t memsize, ++ struct vm_hole **hole) ++{ ++ struct list_head *head = &obj->proc->vmaholes; ++ struct vm_hole *left_hole = obj->previous_hole, ++ *right_hole = next_hole(left_hole, head); ++ unsigned long max_distance = 0x80000000; ++ struct obj_vm_area *sovma; ++ ++ unsigned long obj_start, obj_end; ++ unsigned long region_start = 0, region_end = 0; ++ ++ kpdebug("Looking for patch region for '%s'...\n", obj->name); ++ ++ sovma = list_first_entry(&obj->vma, struct obj_vm_area, list); ++ obj_start = sovma->inmem.start; ++ sovma = list_entry(obj->vma.prev, struct obj_vm_area, list); ++ obj_end = sovma->inmem.end; ++ ++ ++ max_distance -= memsize; ++ ++ /* TODO carefully check for the holes laying between obj_start and ++ * obj_end, i.e. just after the executable segment of an executable ++ */ ++ while (left_hole != NULL && right_hole != NULL) { ++ if (right_hole != NULL && ++ right_hole->start - obj_start > max_distance) ++ right_hole = NULL; ++ else if (hole_size(right_hole) > memsize) { ++ region_start = right_hole->start; ++ region_end = ++ (right_hole->end - obj_start) <= max_distance ? ++ right_hole->end - memsize : ++ obj_start + max_distance; ++ *hole = right_hole; ++ break; ++ } else ++ right_hole = next_hole(right_hole, head); ++ ++ if (left_hole != NULL && ++ obj_end - left_hole->end > max_distance) ++ left_hole = NULL; ++ else if (hole_size(left_hole) > memsize) { ++ region_start = ++ (left_hole->start - obj_end) <= max_distance ? ++ left_hole->start : obj_end > max_distance ? ++ obj_end - max_distance : 0; ++ region_end = left_hole->end - memsize; ++ *hole = left_hole; ++ break; ++ } else ++ left_hole = prev_hole(left_hole, head); ++ } ++ ++ if (region_start == region_end) { ++ kperr("can't find suitable region for patch on '%s'\n", ++ obj->name); ++ return -1UL; ++ } ++ ++ region_start += PAGE_SIZE; ++ kpdebug("Found patch region for '%s' at %lx\n", obj->name, region_start); ++ ++ return region_start; ++} +diff --git a/src/include/kpatch_process.h b/src/include/kpatch_process.h +index abbb1af..b96a6da 100644 +--- a/src/include/kpatch_process.h ++++ b/src/include/kpatch_process.h +@@ -211,4 +211,13 @@ is_kernel_object_name(char *name) + return 0; + } + ++struct vm_hole *next_hole(struct vm_hole *hole, struct list_head *head); ++struct vm_hole *prev_hole(struct vm_hole *hole, struct list_head *head); ++unsigned long hole_size(struct vm_hole *hole); ++ ++unsigned long random_from_range(unsigned long min, unsigned long max); ++unsigned long object_find_patch_region(struct object_file *obj, ++ size_t memsize, ++ struct vm_hole **hole); ++ + #endif /* ifndef __KPATCH_PROCESS__ */ +diff --git a/src/kpatch_process.c b/src/kpatch_process.c +index 3f7f2f6..9561962 100644 +--- a/src/kpatch_process.c ++++ b/src/kpatch_process.c +@@ -965,7 +965,7 @@ vm_hole_split(struct vm_hole *hole, + return 0; + } + +-static inline struct vm_hole * ++inline struct vm_hole * + next_hole(struct vm_hole *hole, struct list_head *head) + { + if (hole == NULL || hole->list.next == head) +@@ -974,7 +974,7 @@ next_hole(struct vm_hole *hole, struct list_head *head) + return list_entry(hole->list.next, struct vm_hole, list); + } + +-static inline struct vm_hole * ++inline struct vm_hole * + prev_hole(struct vm_hole *hole, struct list_head *head) + { + if (hole == NULL || hole->list.prev == head) +@@ -983,7 +983,7 @@ prev_hole(struct vm_hole *hole, struct list_head *head) + return list_entry(hole->list.prev, struct vm_hole, list); + } + +-static inline unsigned long ++inline unsigned long + hole_size(struct vm_hole *hole) + { + if (hole == NULL) +@@ -991,92 +991,13 @@ hole_size(struct vm_hole *hole) + return hole->end - hole->start; + } + +-static unsigned long ++unsigned long + random_from_range(unsigned long min, unsigned long max) + { + /* TODO this is not uniform nor safe */ + return min + random() % (max - min); + } + +-/* +- * Find region for a patch. Take object's `previous_hole` as a left candidate +- * and the next hole as a right candidate. Pace through them until there is +- * enough space in the hole for the patch. +- * +- * Since holes can be much larger than 2GiB take extra caution to allocate +- * patch region inside the (-2GiB, +2GiB) range from the original object. +- */ +-static unsigned long +-object_find_patch_region(struct object_file *obj, +- size_t memsize, +- struct vm_hole **hole) +-{ +- struct list_head *head = &obj->proc->vmaholes; +- struct vm_hole *left_hole = obj->previous_hole, +- *right_hole = next_hole(left_hole, head); +- unsigned long max_distance = 0x80000000; +- struct obj_vm_area *sovma; +- +- unsigned long obj_start, obj_end; +- unsigned long region_start = 0, region_end = 0; +- +- kpdebug("Looking for patch region for '%s'...\n", obj->name); +- +- sovma = list_first_entry(&obj->vma, struct obj_vm_area, list); +- obj_start = sovma->inmem.start; +- sovma = list_entry(obj->vma.prev, struct obj_vm_area, list); +- obj_end = sovma->inmem.end; +- +- +- max_distance -= memsize; +- +- /* TODO carefully check for the holes laying between obj_start and +- * obj_end, i.e. just after the executable segment of an executable +- */ +- while (left_hole != NULL && right_hole != NULL) { +- if (right_hole != NULL && +- right_hole->start - obj_start > max_distance) +- right_hole = NULL; +- else if (hole_size(right_hole) > memsize) { +- region_start = right_hole->start; +- region_end = +- (right_hole->end - obj_start) <= max_distance ? +- right_hole->end - memsize : +- obj_start + max_distance; +- *hole = right_hole; +- break; +- } else +- right_hole = next_hole(right_hole, head); +- +- if (left_hole != NULL && +- obj_end - left_hole->end > max_distance) +- left_hole = NULL; +- else if (hole_size(left_hole) > memsize) { +- region_start = +- (left_hole->start - obj_end) <= max_distance ? +- left_hole->start : obj_end > max_distance ? +- obj_end - max_distance : 0; +- region_end = left_hole->end - memsize; +- *hole = left_hole; +- break; +- } else +- left_hole = prev_hole(left_hole, head); +- } +- +- if (region_start == region_end) { +- kperr("can't find suitable region for patch on '%s'\n", +- obj->name); +- return -1UL; +- } +- +- region_start = random_from_range(region_start >> PAGE_SHIFT, +- region_end >> PAGE_SHIFT); +- region_start <<= PAGE_SHIFT; +- kpdebug("Found patch region for '%s' at %lx\n", obj->name, region_start); +- +- return region_start; +-} +- + int + kpatch_object_allocate_patch(struct object_file *o, + size_t sz) +-- +2.23.0 + diff --git a/0039-kpatch_ptrace-Split-function-kpatch_ptrace_waitpid.patch b/0039-kpatch_ptrace-Split-function-kpatch_ptrace_waitpid.patch new file mode 100644 index 0000000..1ebd5e4 --- /dev/null +++ b/0039-kpatch_ptrace-Split-function-kpatch_ptrace_waitpid.patch @@ -0,0 +1,501 @@ +From 35b9c6934fc5c1e2ea4cf7e30b91b3b91e48074d Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 16:16:57 +0800 +Subject: [PATCH 39/89] kpatch_ptrace: Split function kpatch_ptrace_waitpid + +The function kpatch_ptrace_waitpid is arch related, let's +rename it with kpatch_arch_ptrace_waitpid, and make the +defination in arch/x86/arch_ptrace.c and arch/aarch64/arch_ptrace.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_ptrace.c | 145 +++++++++++++++++++++++++++++++++ + src/arch/x86/arch_ptrace.c | 140 +++++++++++++++++++++++++++++++ + src/include/kpatch_ptrace.h | 20 +++++ + src/kpatch_ptrace.c | 130 +---------------------------- + 4 files changed, 307 insertions(+), 128 deletions(-) + +diff --git a/src/arch/aarch64/arch_ptrace.c b/src/arch/aarch64/arch_ptrace.c +index e69de29..fb19e86 100644 +--- a/src/arch/aarch64/arch_ptrace.c ++++ b/src/arch/aarch64/arch_ptrace.c +@@ -0,0 +1,145 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "include/kpatch_process.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_ptrace.h" ++#include "include/kpatch_log.h" ++ ++#include ++ ++int ++kpatch_arch_ptrace_waitpid(kpatch_process_t *proc, ++ struct timespec *timeout, ++ const sigset_t *sigset) ++{ ++ struct kpatch_ptrace_ctx *pctx; ++ siginfo_t siginfo; ++ int ret, status; ++ pid_t pid; ++ struct user_regs_struct regs; ++ struct iovec regs_iov; ++ ++ regs_iov.iov_base = ®s; ++ regs_iov.iov_len = sizeof(regs); ++ ++ /* Immediately reap one attached thread */ ++ pid = waitpid(-1, &status, __WALL | WNOHANG); ++ ++ if (pid < 0) { ++ kplogerror("can't wait for tracees\n"); ++ return -1; ++ } ++ ++ /* There is none ready, wait for notification via signal */ ++ if (pid == 0) { ++ ret = sigtimedwait(sigset, &siginfo, timeout); ++ if (ret == -1 && errno == EAGAIN) { ++ /* We have timeouted */ ++ return -1; ++ } ++ ++ if (ret == -1 && errno == EINVAL) { ++ kperr("invalid timeout\n"); ++ return -1; ++ } ++ ++ /* We have got EINTR and must restart */ ++ if (ret == -1 && errno == EINTR) ++ return 0; ++ ++ /** ++ * Kernel stacks signals that follow too quickly. ++ * Deal with it by waiting for any child, not just ++ * one that is specified in signal ++ */ ++ pid = waitpid(-1, &status, __WALL | WNOHANG); ++ ++ if (pid == 0) { ++ kperr("missing waitpid for %d\n", siginfo.si_pid); ++ return 0; ++ } ++ ++ if (pid < 0) { ++ kplogerror("can't wait for tracee %d\n", siginfo.si_pid); ++ return -1; ++ } ++ } ++ ++ if (!WIFSTOPPED(status) && WIFSIGNALED(status)) { ++ /* Continue, resending the signal */ ++ ret = ptrace(PTRACE_CONT, pid, NULL, ++ (void *)(uintptr_t)WTERMSIG(status)); ++ if (ret < 0) { ++ kplogerror("can't start tracee %d\n", pid); ++ return -1; ++ } ++ return 0; ++ } ++ ++ if (WIFEXITED(status)) { ++ pctx = kpatch_ptrace_find_thread(proc, pid, 0UL); ++ if (pctx == NULL) { ++ kperr("got unexpected child '%d' exit\n", pid); ++ } else { ++ /* It's dead */ ++ pctx->pid = pctx->running = 0; ++ } ++ return 1; ++ } ++ ++ ret = ptrace(PTRACE_GETREGSET, pid, (void *)NT_PRSTATUS, (void *)®s_iov); ++ if (ret < 0) { ++ kplogerror("can't get regs %d\n", pid); ++ return -1; ++ } ++ ++ pctx = kpatch_ptrace_find_thread(proc, pid, regs.pc); ++ ++ if (pctx == NULL) { ++ /* We either don't know anything about this thread or ++ * even worse -- we stopped it in the wrong place. ++ * Bail out. ++ */ ++ pctx = kpatch_ptrace_find_thread(proc, pid, 0); ++ if (pctx != NULL) ++ pctx->running = 0; ++ ++ /* TODO: fix the latter by SINGLESTEPping such a thread with ++ * the original instruction in place */ ++ kperr("the thread ran out: %d, pc= %llx, expected = %lx\n", pid, ++ regs.pc, pctx->execute_until); ++ errno = ESRCH; ++ return -1; ++ } ++ ++ pctx->running = 0; ++ ++ /* Restore thread registers, pctx is now valid */ ++ kpdebug("Got thread %d at %llx\n", pctx->pid, ++ regs.pc - BREAK_INSN_LENGTH); ++ ++ regs.pc = pctx->execute_until; ++ ++ ret = ptrace(PTRACE_SETREGSET, pctx->pid, (void*)NT_PRSTATUS, (void*)®s_iov); ++ if (ret < 0) { ++ kplogerror("can't set regs - %d\n", pctx->pid); ++ return -1; ++ } ++ ++ return 1; ++} +diff --git a/src/arch/x86/arch_ptrace.c b/src/arch/x86/arch_ptrace.c +index e69de29..6e943fd 100644 +--- a/src/arch/x86/arch_ptrace.c ++++ b/src/arch/x86/arch_ptrace.c +@@ -0,0 +1,140 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "include/kpatch_process.h" ++#include "include/kpatch_common.h" ++#include "include/kpatch_ptrace.h" ++#include "include/kpatch_log.h" ++ ++#include ++ ++int kpatch_arch_ptrace_waitpid(kpatch_process_t *proc, ++ struct timespec *timeout, ++ const sigset_t *sigset) ++{ ++ struct kpatch_ptrace_ctx *pctx; ++ siginfo_t siginfo; ++ int ret, status; ++ pid_t pid; ++ struct user_regs_struct regs; ++ ++ /* Immediately reap one attached thread */ ++ pid = waitpid(-1, &status, __WALL | WNOHANG); ++ ++ if (pid < 0) { ++ kplogerror("can't wait for tracees\n"); ++ return -1; ++ } ++ ++ /* There is none ready, wait for notification via signal */ ++ if (pid == 0) { ++ ret = sigtimedwait(sigset, &siginfo, timeout); ++ if (ret == -1 && errno == EAGAIN) { ++ /* We have timeouted */ ++ return -1; ++ } ++ ++ if (ret == -1 && errno == EINVAL) { ++ kperr("invalid timeout\n"); ++ return -1; ++ } ++ ++ /* We have got EINTR and must restart */ ++ if (ret == -1 && errno == EINTR) ++ return 0; ++ ++ /** ++ * Kernel stacks signals that follow too quickly. ++ * Deal with it by waiting for any child, not just ++ * one that is specified in signal ++ */ ++ pid = waitpid(-1, &status, __WALL | WNOHANG); ++ ++ if (pid == 0) { ++ kperr("missing waitpid for %d\n", siginfo.si_pid); ++ return 0; ++ } ++ ++ if (pid < 0) { ++ kplogerror("can't wait for tracee %d\n", siginfo.si_pid); ++ return -1; ++ } ++ } ++ ++ if (!WIFSTOPPED(status) && WIFSIGNALED(status)) { ++ /* Continue, resending the signal */ ++ ret = ptrace(PTRACE_CONT, pid, NULL, ++ (void *)(uintptr_t)WTERMSIG(status)); ++ if (ret < 0) { ++ kplogerror("can't start tracee %d\n", pid); ++ return -1; ++ } ++ return 0; ++ } ++ ++ if (WIFEXITED(status)) { ++ pctx = kpatch_ptrace_find_thread(proc, pid, 0UL); ++ if (pctx == NULL) { ++ kperr("got unexpected child '%d' exit\n", pid); ++ } else { ++ /* It's dead */ ++ pctx->pid = pctx->running = 0; ++ } ++ return 1; ++ } ++ ++ ret = ptrace(PTRACE_GETREGS, pid, NULL, ®s); ++ if (ret < 0) { ++ kplogerror("can't get regs %d\n", pid); ++ return -1; ++ } ++ ++ pctx = kpatch_ptrace_find_thread(proc, pid, regs.rip); ++ ++ if (pctx == NULL) { ++ /* We either don't know anything about this thread or ++ * even worse -- we stopped it in the wrong place. ++ * Bail out. ++ */ ++ pctx = kpatch_ptrace_find_thread(proc, pid, 0); ++ if (pctx != NULL) ++ pctx->running = 0; ++ ++ /* TODO: fix the latter by SINGLESTEPping such a thread with ++ * the original instruction in place */ ++ kperr("the thread ran out: %d, rip = %llx, expected = %lx\n", pid, ++ regs.rip, pctx->execute_until); ++ errno = ESRCH; ++ return -1; ++ } ++ ++ pctx->running = 0; ++ ++ /* Restore thread registers, pctx is now valid */ ++ kpdebug("Got thread %d at %llx\n", pctx->pid, ++ regs.rip - BREAK_INSN_LENGTH); ++ ++ regs.rip = pctx->execute_until; ++ ++ ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, ®s); ++ if (ret < 0) { ++ kplogerror("can't set regs - %d\n", pctx->pid); ++ return -1; ++ } ++ ++ return 1; ++} +diff --git a/src/include/kpatch_ptrace.h b/src/include/kpatch_ptrace.h +index 7557e1f..1c7d33e 100644 +--- a/src/include/kpatch_ptrace.h ++++ b/src/include/kpatch_ptrace.h +@@ -102,4 +102,24 @@ kpatch_process_memcpy(kpatch_process_t *proc, + unsigned long dst, + unsigned long src, + size_t size); ++ ++#define BREAK_INSN_LENGTH 1 ++#define BREAK_INSN {0xcc} ++ ++#define SEC_TO_MSEC 1000 ++#define MSEC_TO_NSEC 1000000 ++ ++#define for_each_thread(proc, pctx) \ ++ list_for_each_entry(pctx, &proc->ptrace.pctxs, list) ++ ++struct kpatch_ptrace_ctx * ++kpatch_ptrace_find_thread(kpatch_process_t *proc, ++ pid_t pid, ++ unsigned long rip); ++ ++int ++kpatch_arch_ptrace_waitpid(kpatch_process_t *proc, ++ struct timespec *timeout, ++ const sigset_t *sigset); ++ + #endif +diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c +index 8910aa8..3c57288 100644 +--- a/src/kpatch_ptrace.c ++++ b/src/kpatch_ptrace.c +@@ -180,16 +180,8 @@ int kpatch_ptrace_get_entry_point(struct kpatch_ptrace_ctx *pctx, + return entry[0] == AT_ENTRY ? 0 : -1; + } + +-#define BREAK_INSN_LENGTH 1 +-#define BREAK_INSN {0xcc} + +-#define SEC_TO_MSEC 1000 +-#define MSEC_TO_NSEC 1000000 +- +-#define for_each_thread(proc, pctx) \ +- list_for_each_entry(pctx, &proc->ptrace.pctxs, list) +- +-static struct kpatch_ptrace_ctx * ++struct kpatch_ptrace_ctx * + kpatch_ptrace_find_thread(kpatch_process_t *proc, + pid_t pid, + unsigned long rip) +@@ -213,124 +205,6 @@ kpatch_ptrace_find_thread(kpatch_process_t *proc, + return NULL; + } + +-static inline int +-kpatch_ptrace_waitpid(kpatch_process_t *proc, +- struct timespec *timeout, +- const sigset_t *sigset) +-{ +- struct kpatch_ptrace_ctx *pctx; +- siginfo_t siginfo; +- int ret, status; +- pid_t pid; +- struct user_regs_struct regs; +- +- /* Immediately reap one attached thread */ +- pid = waitpid(-1, &status, __WALL | WNOHANG); +- +- if (pid < 0) { +- kplogerror("can't wait for tracees\n"); +- return -1; +- } +- +- /* There is none ready, wait for notification via signal */ +- if (pid == 0) { +- ret = sigtimedwait(sigset, &siginfo, timeout); +- if (ret == -1 && errno == EAGAIN) { +- /* We have timeouted */ +- return -1; +- } +- +- if (ret == -1 && errno == EINVAL) { +- kperr("invalid timeout\n"); +- return -1; +- } +- +- /* We have got EINTR and must restart */ +- if (ret == -1 && errno == EINTR) +- return 0; +- +- /** +- * Kernel stacks signals that follow too quickly. +- * Deal with it by waiting for any child, not just +- * one that is specified in signal +- */ +- pid = waitpid(-1, &status, __WALL | WNOHANG); +- +- if (pid == 0) { +- kperr("missing waitpid for %d\n", siginfo.si_pid); +- return 0; +- } +- +- if (pid < 0) { +- kplogerror("can't wait for tracee %d\n", siginfo.si_pid); +- return -1; +- } +- } +- +- if (!WIFSTOPPED(status) && WIFSIGNALED(status)) { +- /* Continue, resending the signal */ +- ret = ptrace(PTRACE_CONT, pid, NULL, +- (void *)(uintptr_t)WTERMSIG(status)); +- if (ret < 0) { +- kplogerror("can't start tracee %d\n", pid); +- return -1; +- } +- return 0; +- } +- +- if (WIFEXITED(status)) { +- pctx = kpatch_ptrace_find_thread(proc, pid, 0UL); +- if (pctx == NULL) { +- kperr("got unexpected child '%d' exit\n", pid); +- } else { +- /* It's dead */ +- pctx->pid = pctx->running = 0; +- } +- return 1; +- } +- +- ret = ptrace(PTRACE_GETREGS, pid, NULL, ®s); +- if (ret < 0) { +- kplogerror("can't get regs %d\n", pid); +- return -1; +- } +- +- pctx = kpatch_ptrace_find_thread(proc, pid, regs.rip); +- +- if (pctx == NULL) { +- /* We either don't know anything about this thread or +- * even worse -- we stopped it in the wrong place. +- * Bail out. +- */ +- pctx = kpatch_ptrace_find_thread(proc, pid, 0); +- if (pctx != NULL) +- pctx->running = 0; +- +- /* TODO: fix the latter by SINGLESTEPping such a thread with +- * the original instruction in place */ +- kperr("the thread ran out: %d, rip = %llx, expected = %lx\n", pid, +- regs.rip, pctx->execute_until); +- errno = ESRCH; +- return -1; +- } +- +- pctx->running = 0; +- +- /* Restore thread registers, pctx is now valid */ +- kpdebug("Got thread %d at %llx\n", pctx->pid, +- regs.rip - BREAK_INSN_LENGTH); +- +- regs.rip = pctx->execute_until; +- +- ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, ®s); +- if (ret < 0) { +- kplogerror("can't set regs - %d\n", pctx->pid); +- return -1; +- } +- +- return 1; +-} +- + struct breakpoint { + unsigned long addr; + unsigned char orig_code[BREAK_INSN_LENGTH]; +@@ -441,7 +315,7 @@ kpatch_ptrace_execute_until(kpatch_process_t *proc, + break; + } + +- rv = kpatch_ptrace_waitpid(proc, &timeout, &sigset); ++ rv = kpatch_arch_ptrace_waitpid(proc, &timeout, &sigset); + if (rv < 0) + break; + +-- +2.23.0 + diff --git a/0040-kpatch_ptrace-Split-function-copy_regs.patch b/0040-kpatch_ptrace-Split-function-copy_regs.patch new file mode 100644 index 0000000..5884a74 --- /dev/null +++ b/0040-kpatch_ptrace-Split-function-copy_regs.patch @@ -0,0 +1,137 @@ +From 8c0199836e7944569dbbeb5e571d791b8e466275 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 16:24:09 +0800 +Subject: [PATCH 40/89] kpatch_ptrace: Split function copy_regs + +The function copy_regs is arch related, so make two separate +definations in arch/x86/arch_ptrace.c and arch/aarch64/arch_ptrace.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_ptrace.c | 28 ++++++++++++++++++++++++++++ + src/arch/x86/arch_ptrace.c | 22 ++++++++++++++++++++++ + src/include/kpatch_ptrace.h | 3 +++ + src/kpatch_ptrace.c | 22 ---------------------- + 4 files changed, 53 insertions(+), 22 deletions(-) + +diff --git a/src/arch/aarch64/arch_ptrace.c b/src/arch/aarch64/arch_ptrace.c +index fb19e86..0366d4f 100644 +--- a/src/arch/aarch64/arch_ptrace.c ++++ b/src/arch/aarch64/arch_ptrace.c +@@ -22,6 +22,34 @@ + + #include + ++void copy_regs(struct user_regs_struct *dst, ++ struct user_regs_struct *src) ++{ ++#define COPY_REG(x) dst->x = src->x ++ COPY_REG(regs[0]); ++ COPY_REG(regs[1]); ++ COPY_REG(regs[2]); ++ COPY_REG(regs[3]); ++ COPY_REG(regs[4]); ++ COPY_REG(regs[5]); ++ COPY_REG(regs[8]); ++ COPY_REG(regs[29]); ++ ++ COPY_REG(regs[9]); ++ COPY_REG(regs[10]); ++ COPY_REG(regs[11]); ++ COPY_REG(regs[12]); ++ COPY_REG(regs[13]); ++ COPY_REG(regs[14]); ++ COPY_REG(regs[15]); ++ COPY_REG(regs[16]); ++ COPY_REG(regs[17]); ++ COPY_REG(regs[18]); ++ COPY_REG(regs[19]); ++ COPY_REG(regs[20]); ++#undef COPY_REG ++} ++ + int + kpatch_arch_ptrace_waitpid(kpatch_process_t *proc, + struct timespec *timeout, +diff --git a/src/arch/x86/arch_ptrace.c b/src/arch/x86/arch_ptrace.c +index 6e943fd..fa23757 100644 +--- a/src/arch/x86/arch_ptrace.c ++++ b/src/arch/x86/arch_ptrace.c +@@ -22,6 +22,28 @@ + + #include + ++void copy_regs(struct user_regs_struct *dst, ++ struct user_regs_struct *src) ++{ ++#define COPY_REG(x) dst->x = src->x ++ COPY_REG(r15); ++ COPY_REG(r14); ++ COPY_REG(r13); ++ COPY_REG(r12); ++ COPY_REG(rbp); ++ COPY_REG(rbx); ++ COPY_REG(r11); ++ COPY_REG(r10); ++ COPY_REG(r9); ++ COPY_REG(r8); ++ COPY_REG(rax); ++ COPY_REG(rcx); ++ COPY_REG(rdx); ++ COPY_REG(rsi); ++ COPY_REG(rdi); ++#undef COPY_REG ++} ++ + int kpatch_arch_ptrace_waitpid(kpatch_process_t *proc, + struct timespec *timeout, + const sigset_t *sigset) +diff --git a/src/include/kpatch_ptrace.h b/src/include/kpatch_ptrace.h +index 1c7d33e..e434d68 100644 +--- a/src/include/kpatch_ptrace.h ++++ b/src/include/kpatch_ptrace.h +@@ -122,4 +122,7 @@ kpatch_arch_ptrace_waitpid(kpatch_process_t *proc, + struct timespec *timeout, + const sigset_t *sigset); + ++void copy_regs(struct user_regs_struct *dst, ++ struct user_regs_struct *src); ++ + #endif +diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c +index 3c57288..180bbaa 100644 +--- a/src/kpatch_ptrace.c ++++ b/src/kpatch_ptrace.c +@@ -413,28 +413,6 @@ poke_back: + return ret; + } + +-static void copy_regs(struct user_regs_struct *dst, +- struct user_regs_struct *src) +-{ +-#define COPY_REG(x) dst->x = src->x +- COPY_REG(r15); +- COPY_REG(r14); +- COPY_REG(r13); +- COPY_REG(r12); +- COPY_REG(rbp); +- COPY_REG(rbx); +- COPY_REG(r11); +- COPY_REG(r10); +- COPY_REG(r9); +- COPY_REG(r8); +- COPY_REG(rax); +- COPY_REG(rcx); +- COPY_REG(rdx); +- COPY_REG(rsi); +- COPY_REG(rdi); +-#undef COPY_REG +-} +- + static + int + kpatch_execute_remote_func(struct kpatch_ptrace_ctx *pctx, +-- +2.23.0 + diff --git a/0041-kpatch_ptrace-Split-function-kpatch_execute_remote_f.patch b/0041-kpatch_ptrace-Split-function-kpatch_execute_remote_f.patch new file mode 100644 index 0000000..ae1b197 --- /dev/null +++ b/0041-kpatch_ptrace-Split-function-kpatch_execute_remote_f.patch @@ -0,0 +1,328 @@ +From e312d7a74924d6e3880fa27adb4bcd04c8c25983 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 16:30:58 +0800 +Subject: [PATCH 41/89] kpatch_ptrace: Split function + kpatch_execute_remote_func + +The function kpatch_execute_remote_func is arch related, first +rename it with kpatch_arch_execute_remote_func, and the make separate +definations in arch/x86/arch_ptrace.c and arch/aarch64/arch_ptrace.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_ptrace.c | 87 ++++++++++++++++++++++++++++++++++ + src/arch/x86/arch_ptrace.c | 79 ++++++++++++++++++++++++++++++ + src/include/kpatch_ptrace.h | 9 ++++ + src/kpatch_ptrace.c | 82 +------------------------------- + 4 files changed, 176 insertions(+), 81 deletions(-) + +diff --git a/src/arch/aarch64/arch_ptrace.c b/src/arch/aarch64/arch_ptrace.c +index 0366d4f..821b4e8 100644 +--- a/src/arch/aarch64/arch_ptrace.c ++++ b/src/arch/aarch64/arch_ptrace.c +@@ -22,6 +22,93 @@ + + #include + ++int ++kpatch_arch_execute_remote_func(struct kpatch_ptrace_ctx *pctx, ++ const unsigned char *code, ++ size_t codelen, ++ struct user_regs_struct *pregs, ++ int (*func)(struct kpatch_ptrace_ctx *pctx, ++ void *data), ++ void *data) ++{ ++ struct user_regs_struct orig_regs, regs; ++ struct iovec orig_regs_iov, regs_iov; ++ ++ orig_regs_iov.iov_base = &orig_regs; ++ orig_regs_iov.iov_len = sizeof(orig_regs); ++ regs_iov.iov_base = ®s; ++ regs_iov.iov_len = sizeof(regs); ++ ++ unsigned char orig_code[codelen]; ++ int ret; ++ kpatch_process_t *proc = pctx->proc; ++ unsigned long libc_base = proc->libc_base; ++ ++ ++ ret = ptrace(PTRACE_GETREGSET, pctx->pid, (void*)NT_PRSTATUS, (void*)&orig_regs_iov); ++ if (ret < 0) { ++ kplogerror("can't get regs - %d\n", pctx->pid); ++ return -1; ++ } ++ ret = kpatch_process_mem_read( ++ proc, ++ libc_base, ++ (unsigned long *)orig_code, ++ codelen); ++ if (ret < 0) { ++ kplogerror("can't peek original code - %d\n", pctx->pid); ++ return -1; ++ } ++ ret = kpatch_process_mem_write( ++ proc, ++ (unsigned long *)code, ++ libc_base, ++ codelen); ++ if (ret < 0) { ++ kplogerror("can't poke syscall code - %d\n", pctx->pid); ++ goto poke_back; ++ } ++ ++ regs = orig_regs; ++ regs.pc = libc_base; ++ ++ copy_regs(®s, pregs); ++ ++ ret = ptrace(PTRACE_SETREGSET, pctx->pid, (void*)NT_PRSTATUS, (void*)®s_iov); ++ if (ret < 0) { ++ kplogerror("can't set regs - %d\n", pctx->pid); ++ goto poke_back; ++ } ++ ++ ret = func(pctx, data); ++ if (ret < 0) { ++ kplogerror("failed call to func\n"); ++ goto poke_back; ++ } ++ ++ ret = ptrace(PTRACE_GETREGSET, pctx->pid, (void*)NT_PRSTATUS, (void*)®s_iov); ++ if (ret < 0) { ++ kplogerror("can't get updated regs - %d\n", pctx->pid); ++ goto poke_back; ++ } ++ ++ ret = ptrace(PTRACE_SETREGSET, pctx->pid, (void*)NT_PRSTATUS, (void*)&orig_regs_iov); ++ if (ret < 0) { ++ kplogerror("can't restore regs - %d\n", pctx->pid); ++ goto poke_back; ++ } ++ ++ *pregs = regs; ++ ++poke_back: ++ kpatch_process_mem_write( ++ proc, ++ (unsigned long *)orig_code, ++ libc_base, ++ codelen); ++ return ret; ++} ++ + void copy_regs(struct user_regs_struct *dst, + struct user_regs_struct *src) + { +diff --git a/src/arch/x86/arch_ptrace.c b/src/arch/x86/arch_ptrace.c +index fa23757..9239f52 100644 +--- a/src/arch/x86/arch_ptrace.c ++++ b/src/arch/x86/arch_ptrace.c +@@ -22,6 +22,85 @@ + + #include + ++int ++kpatch_arch_execute_remote_func(struct kpatch_ptrace_ctx *pctx, ++ const unsigned char *code, ++ size_t codelen, ++ struct user_regs_struct *pregs, ++ int (*func)(struct kpatch_ptrace_ctx *pctx, ++ void *data), ++ void *data) ++{ ++ struct user_regs_struct orig_regs, regs; ++ unsigned char orig_code[codelen]; ++ int ret; ++ kpatch_process_t *proc = pctx->proc; ++ unsigned long libc_base = proc->libc_base; ++ ++ ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, &orig_regs); ++ if (ret < 0) { ++ kplogerror("can't get regs - %d\n", pctx->pid); ++ return -1; ++ } ++ ret = kpatch_process_mem_read( ++ proc, ++ libc_base, ++ (unsigned long *)orig_code, ++ codelen); ++ if (ret < 0) { ++ kplogerror("can't peek original code - %d\n", pctx->pid); ++ return -1; ++ } ++ ret = kpatch_process_mem_write( ++ proc, ++ (unsigned long *)code, ++ libc_base, ++ codelen); ++ if (ret < 0) { ++ kplogerror("can't poke syscall code - %d\n", pctx->pid); ++ goto poke_back; ++ } ++ ++ regs = orig_regs; ++ regs.rip = libc_base; ++ ++ copy_regs(®s, pregs); ++ ++ ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, ®s); ++ if (ret < 0) { ++ kplogerror("can't set regs - %d\n", pctx->pid); ++ goto poke_back; ++ } ++ ++ ret = func(pctx, data); ++ if (ret < 0) { ++ kplogerror("failed call to func\n"); ++ goto poke_back; ++ } ++ ++ ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, ®s); ++ if (ret < 0) { ++ kplogerror("can't get updated regs - %d\n", pctx->pid); ++ goto poke_back; ++ } ++ ++ ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, &orig_regs); ++ if (ret < 0) { ++ kplogerror("can't restore regs - %d\n", pctx->pid); ++ goto poke_back; ++ } ++ ++ *pregs = regs; ++ ++poke_back: ++ kpatch_process_mem_write( ++ proc, ++ (unsigned long *)orig_code, ++ libc_base, ++ codelen); ++ return ret; ++} ++ + void copy_regs(struct user_regs_struct *dst, + struct user_regs_struct *src) + { +diff --git a/src/include/kpatch_ptrace.h b/src/include/kpatch_ptrace.h +index e434d68..f35aabd 100644 +--- a/src/include/kpatch_ptrace.h ++++ b/src/include/kpatch_ptrace.h +@@ -125,4 +125,13 @@ kpatch_arch_ptrace_waitpid(kpatch_process_t *proc, + void copy_regs(struct user_regs_struct *dst, + struct user_regs_struct *src); + ++int ++kpatch_arch_execute_remote_func(struct kpatch_ptrace_ctx *pctx, ++ const unsigned char *code, ++ size_t codelen, ++ struct user_regs_struct *pregs, ++ int (*func)(struct kpatch_ptrace_ctx *pctx, ++ void *data), ++ void *data); ++ + #endif +diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c +index 180bbaa..9056815 100644 +--- a/src/kpatch_ptrace.c ++++ b/src/kpatch_ptrace.c +@@ -413,86 +413,6 @@ poke_back: + return ret; + } + +-static +-int +-kpatch_execute_remote_func(struct kpatch_ptrace_ctx *pctx, +- const unsigned char *code, +- size_t codelen, +- struct user_regs_struct *pregs, +- int (*func)(struct kpatch_ptrace_ctx *pctx, +- void *data), +- void *data) +-{ +- struct user_regs_struct orig_regs, regs; +- unsigned char orig_code[codelen]; +- int ret; +- kpatch_process_t *proc = pctx->proc; +- unsigned long libc_base = proc->libc_base; +- +- ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, &orig_regs); +- if (ret < 0) { +- kplogerror("can't get regs - %d\n", pctx->pid); +- return -1; +- } +- ret = kpatch_process_mem_read( +- proc, +- libc_base, +- (unsigned long *)orig_code, +- codelen); +- if (ret < 0) { +- kplogerror("can't peek original code - %d\n", pctx->pid); +- return -1; +- } +- ret = kpatch_process_mem_write( +- proc, +- (unsigned long *)code, +- libc_base, +- codelen); +- if (ret < 0) { +- kplogerror("can't poke syscall code - %d\n", pctx->pid); +- goto poke_back; +- } +- +- regs = orig_regs; +- regs.rip = libc_base; +- +- copy_regs(®s, pregs); +- +- ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, ®s); +- if (ret < 0) { +- kplogerror("can't set regs - %d\n", pctx->pid); +- goto poke_back; +- } +- +- ret = func(pctx, data); +- if (ret < 0) { +- kplogerror("failed call to func\n"); +- goto poke_back; +- } +- +- ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, ®s); +- if (ret < 0) { +- kplogerror("can't get updated regs - %d\n", pctx->pid); +- goto poke_back; +- } +- +- ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, &orig_regs); +- if (ret < 0) { +- kplogerror("can't restore regs - %d\n", pctx->pid); +- goto poke_back; +- } +- +- *pregs = regs; +- +-poke_back: +- kpatch_process_mem_write( +- proc, +- (unsigned long *)orig_code, +- libc_base, +- codelen); +- return ret; +-} +- + static int + wait_for_stop(struct kpatch_ptrace_ctx *pctx, + void *data) +@@ -592,7 +512,7 @@ kpatch_execute_remote(struct kpatch_ptrace_ctx *pctx, + size_t codelen, + struct user_regs_struct *pregs) + { +- return kpatch_execute_remote_func(pctx, ++ return kpatch_arch_execute_remote_func(pctx, + code, + codelen, + pregs, +-- +2.23.0 + diff --git a/0042-kpatch_ptrace-Split-function-kpatch_ptrace_resolve_i.patch b/0042-kpatch_ptrace-Split-function-kpatch_ptrace_resolve_i.patch new file mode 100644 index 0000000..d591baa --- /dev/null +++ b/0042-kpatch_ptrace-Split-function-kpatch_ptrace_resolve_i.patch @@ -0,0 +1,146 @@ +From aeeded44db6e705717bd24f3ffbbe878af47833b Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 16:40:42 +0800 +Subject: [PATCH 42/89] kpatch_ptrace: Split function + kpatch_ptrace_resolve_ifunc + +The function kpatch_ptrace_resolve_ifunc is arch related, first +rename it with kpatch_arch_ptrace_resolve_ifunc, and then make +separate definations in arch/x86/arch_ptrace.c and arch/aarch64/arch_ptrace.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_ptrace.c | 20 ++++++++++++++++++++ + src/arch/x86/arch_ptrace.c | 21 +++++++++++++++++++++ + src/include/kpatch_ptrace.h | 2 +- + src/kpatch_elf.c | 4 ++-- + src/kpatch_ptrace.c | 21 --------------------- + 5 files changed, 44 insertions(+), 24 deletions(-) + +diff --git a/src/arch/aarch64/arch_ptrace.c b/src/arch/aarch64/arch_ptrace.c +index 821b4e8..4dee0e5 100644 +--- a/src/arch/aarch64/arch_ptrace.c ++++ b/src/arch/aarch64/arch_ptrace.c +@@ -22,6 +22,26 @@ + + #include + ++int kpatch_arch_ptrace_resolve_ifunc(struct kpatch_ptrace_ctx *pctx, ++ unsigned long *addr) ++{ ++ struct user_regs_struct regs; ++ ++ unsigned char callrax[] = { ++ 0x00, 0x01, 0x3f, 0xd6, // blr x8 ++ 0xa0, 0x00, 0x20, 0xd4, // brk #5 ++ }; ++ int ret; ++ kpdebug("Executing callrax %lx (pid %d)\n", *addr, pctx->pid); ++ regs.regs[8] = *addr; ++ ++ ret = kpatch_execute_remote(pctx, callrax, sizeof(callrax), ®s); ++ if (ret == 0) ++ *addr = regs.regs[0]; ++ ++ return ret; ++} ++ + int + kpatch_arch_execute_remote_func(struct kpatch_ptrace_ctx *pctx, + const unsigned char *code, +diff --git a/src/arch/x86/arch_ptrace.c b/src/arch/x86/arch_ptrace.c +index 9239f52..3d49638 100644 +--- a/src/arch/x86/arch_ptrace.c ++++ b/src/arch/x86/arch_ptrace.c +@@ -22,6 +22,27 @@ + + #include + ++int kpatch_arch_ptrace_resolve_ifunc(struct kpatch_ptrace_ctx *pctx, ++ unsigned long *addr) ++{ ++ struct user_regs_struct regs; ++ ++ unsigned char callrax[] = { ++ 0xff, 0xd0, /* call *%rax */ ++ 0xcc, /* int3 */ ++ }; ++ int ret; ++ ++ kpdebug("Executing callrax %lx (pid %d)\n", *addr, pctx->pid); ++ regs.rax = *addr; ++ ++ ret = kpatch_execute_remote(pctx, callrax, sizeof(callrax), ®s); ++ if (ret == 0) ++ *addr = regs.rax; ++ ++ return ret; ++} ++ + int + kpatch_arch_execute_remote_func(struct kpatch_ptrace_ctx *pctx, + const unsigned char *code, +diff --git a/src/include/kpatch_ptrace.h b/src/include/kpatch_ptrace.h +index f35aabd..19a1b2c 100644 +--- a/src/include/kpatch_ptrace.h ++++ b/src/include/kpatch_ptrace.h +@@ -70,7 +70,7 @@ int kpatch_execute_remote(struct kpatch_ptrace_ctx *pctx, + size_t codelen, + struct user_regs_struct *pregs); + +-int kpatch_ptrace_resolve_ifunc(struct kpatch_ptrace_ctx *pctx, ++int kpatch_arch_ptrace_resolve_ifunc(struct kpatch_ptrace_ctx *pctx, + unsigned long *addr); + unsigned long + kpatch_mmap_remote(struct kpatch_ptrace_ctx *pctx, +diff --git a/src/kpatch_elf.c b/src/kpatch_elf.c +index 5506292..d443001 100644 +--- a/src/kpatch_elf.c ++++ b/src/kpatch_elf.c +@@ -677,8 +677,8 @@ kpatch_resolve_undefined(struct object_file *obj, + addr = vaddr2addr(o, addr); + + if (type == STT_GNU_IFUNC) +- if (kpatch_ptrace_resolve_ifunc(proc2pctx(obj->proc), &addr) < 0) +- kpfatalerror("kpatch_ptrace_resolve_ifunc failed\n"); ++ if (kpatch_arch_ptrace_resolve_ifunc(proc2pctx(obj->proc), &addr) < 0) ++ kpfatalerror("kpatch_arch_ptrace_resolve_ifunc failed\n"); + + break; + } +diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c +index 9056815..cd961e1 100644 +--- a/src/kpatch_ptrace.c ++++ b/src/kpatch_ptrace.c +@@ -672,27 +672,6 @@ static int kpatch_syscall_remote(struct kpatch_ptrace_ctx *pctx, int nr, + return ret; + } + +-int kpatch_ptrace_resolve_ifunc(struct kpatch_ptrace_ctx *pctx, +- unsigned long *addr) +-{ +- struct user_regs_struct regs; +- +- unsigned char callrax[] = { +- 0xff, 0xd0, /* call *%rax */ +- 0xcc, /* int3 */ +- }; +- int ret; +- +- kpdebug("Executing callrax %lx (pid %d)\n", *addr, pctx->pid); +- regs.rax = *addr; +- +- ret = kpatch_execute_remote(pctx, callrax, sizeof(callrax), ®s); +- if (ret == 0) +- *addr = regs.rax; +- +- return ret; +-} +- + #define MAX_ERRNO 4095 + unsigned long + kpatch_mmap_remote(struct kpatch_ptrace_ctx *pctx, +-- +2.23.0 + diff --git a/0043-kpatch_ptrace-Split-function-kpatch_arch_prctl_remot.patch b/0043-kpatch_ptrace-Split-function-kpatch_arch_prctl_remot.patch new file mode 100644 index 0000000..5a8e066 --- /dev/null +++ b/0043-kpatch_ptrace-Split-function-kpatch_arch_prctl_remot.patch @@ -0,0 +1,213 @@ +From aa39ba7326c13546f68b51d95bf55004437c3110 Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 16:49:15 +0800 +Subject: [PATCH 43/89] kpatch_ptrace: Split function kpatch_arch_prctl_remote + +The function kpatch_arch_prctl_remote is arch related, let's make two +separate definations in arch/x86/arch_ptrace.c and arch/aarch64/arch_ptrace.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_ptrace.c | 49 ++++++++++++++++++++++++++++++++++ + src/arch/x86/arch_ptrace.c | 45 +++++++++++++++++++++++++++++++ + src/include/kpatch_ptrace.h | 2 ++ + src/kpatch_ptrace.c | 46 ------------------------------- + 4 files changed, 96 insertions(+), 46 deletions(-) + +diff --git a/src/arch/aarch64/arch_ptrace.c b/src/arch/aarch64/arch_ptrace.c +index 4dee0e5..735927e 100644 +--- a/src/arch/aarch64/arch_ptrace.c ++++ b/src/arch/aarch64/arch_ptrace.c +@@ -22,6 +22,55 @@ + + #include + ++int kpatch_arch_prctl_remote(struct kpatch_ptrace_ctx *pctx, int code, unsigned long *addr) ++{ ++ struct user_regs_struct regs; ++ struct iovec regs_iov; ++ regs_iov.iov_base = ®s; ++ regs_iov.iov_len = sizeof(regs); ++ ++ unsigned long res, sp; ++ int ret; ++ ++ kpdebug("arch_prctl_remote: %d, %p\n", code, addr); ++ ret = ptrace(PTRACE_GETREGSET, pctx->pid, (void*)NT_PRSTATUS, (void*)®s_iov); ++ if (ret < 0) { ++ kpdebug("FAIL. Can't get regs - %s\n", strerror(errno)); ++ return -1; ++ } ++ ret = kpatch_process_mem_read(pctx->proc, ++ regs.sp, ++ &sp, ++ sizeof(sp)); ++ if (ret < 0) { ++ kplogerror("can't peek original stack data\n"); ++ return -1; ++ } ++ //ret = kpatch_syscall_remote(pctx, __NR_arch_prctl, code, regs.sp, 0, 0, 0, 0, &res); ++ if (ret < 0) ++ goto poke; ++ if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { ++ errno = -(long)res; ++ ret = -1; ++ goto poke; ++ } ++ ret = kpatch_process_mem_read(pctx->proc, ++ regs.sp, ++ &res, ++ sizeof(res)); ++ if (ret < 0) ++ kplogerror("can't peek new stack data\n"); ++ ++poke: ++ if (kpatch_process_mem_write(pctx->proc, ++ &sp, ++ regs.sp, ++ sizeof(sp))) ++ kplogerror("can't poke orig stack data\n"); ++ *addr = res; ++ return ret; ++} ++ + int kpatch_arch_ptrace_resolve_ifunc(struct kpatch_ptrace_ctx *pctx, + unsigned long *addr) + { +diff --git a/src/arch/x86/arch_ptrace.c b/src/arch/x86/arch_ptrace.c +index 3d49638..5f1e703 100644 +--- a/src/arch/x86/arch_ptrace.c ++++ b/src/arch/x86/arch_ptrace.c +@@ -22,6 +22,51 @@ + + #include + ++int kpatch_arch_prctl_remote(struct kpatch_ptrace_ctx *pctx, int code, unsigned long *addr) ++{ ++ struct user_regs_struct regs; ++ unsigned long res, rsp; ++ int ret; ++ ++ kpdebug("arch_prctl_remote: %d, %p\n", code, addr); ++ ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, ®s); ++ if (ret < 0) { ++ kpdebug("FAIL. Can't get regs - %s\n", strerror(errno)); ++ return -1; ++ } ++ ret = kpatch_process_mem_read(pctx->proc, ++ regs.rsp, ++ &rsp, ++ sizeof(rsp)); ++ if (ret < 0) { ++ kplogerror("can't peek original stack data\n"); ++ return -1; ++ } ++ ret = kpatch_syscall_remote(pctx, __NR_arch_prctl, code, regs.rsp, 0, 0, 0, 0, &res); ++ if (ret < 0) ++ goto poke; ++ if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { ++ errno = -(long)res; ++ ret = -1; ++ goto poke; ++ } ++ ret = kpatch_process_mem_read(pctx->proc, ++ regs.rsp, ++ &res, ++ sizeof(res)); ++ if (ret < 0) ++ kplogerror("can't peek new stack data\n"); ++ ++poke: ++ if (kpatch_process_mem_write(pctx->proc, ++ &rsp, ++ regs.rsp, ++ sizeof(rsp))) ++ kplogerror("can't poke orig stack data\n"); ++ *addr = res; ++ return ret; ++} ++ + int kpatch_arch_ptrace_resolve_ifunc(struct kpatch_ptrace_ctx *pctx, + unsigned long *addr) + { +diff --git a/src/include/kpatch_ptrace.h b/src/include/kpatch_ptrace.h +index 19a1b2c..ddaa9e6 100644 +--- a/src/include/kpatch_ptrace.h ++++ b/src/include/kpatch_ptrace.h +@@ -84,6 +84,8 @@ int + kpatch_munmap_remote(struct kpatch_ptrace_ctx *pctx, + unsigned long addr, + size_t length); ++ ++#define MAX_ERRNO 4095 + int kpatch_arch_prctl_remote(struct kpatch_ptrace_ctx *pctx, int code, unsigned long *addr); + + int +diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c +index cd961e1..4d2223e 100644 +--- a/src/kpatch_ptrace.c ++++ b/src/kpatch_ptrace.c +@@ -672,7 +672,6 @@ static int kpatch_syscall_remote(struct kpatch_ptrace_ctx *pctx, int nr, + return ret; + } + +-#define MAX_ERRNO 4095 + unsigned long + kpatch_mmap_remote(struct kpatch_ptrace_ctx *pctx, + unsigned long addr, +@@ -717,51 +716,6 @@ int kpatch_munmap_remote(struct kpatch_ptrace_ctx *pctx, + return 0; + } + +-int kpatch_arch_prctl_remote(struct kpatch_ptrace_ctx *pctx, int code, unsigned long *addr) +-{ +- struct user_regs_struct regs; +- unsigned long res, rsp; +- int ret; +- +- kpdebug("arch_prctl_remote: %d, %p\n", code, addr); +- ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, ®s); +- if (ret < 0) { +- kpdebug("FAIL. Can't get regs - %s\n", strerror(errno)); +- return -1; +- } +- ret = kpatch_process_mem_read(pctx->proc, +- regs.rsp, +- &rsp, +- sizeof(rsp)); +- if (ret < 0) { +- kplogerror("can't peek original stack data\n"); +- return -1; +- } +- ret = kpatch_syscall_remote(pctx, __NR_arch_prctl, code, regs.rsp, 0, 0, 0, 0, &res); +- if (ret < 0) +- goto poke; +- if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { +- errno = -(long)res; +- ret = -1; +- goto poke; +- } +- ret = kpatch_process_mem_read(pctx->proc, +- regs.rsp, +- &res, +- sizeof(res)); +- if (ret < 0) +- kplogerror("can't peek new stack data\n"); +- +-poke: +- if (kpatch_process_mem_write(pctx->proc, +- &rsp, +- regs.rsp, +- sizeof(rsp))) +- kplogerror("can't poke orig stack data\n"); +- *addr = res; +- return ret; +-} +- + int + kpatch_remote_write(struct kpatch_ptrace_ctx *pctx, + unsigned long dst, +-- +2.23.0 + diff --git a/0044-kpatch_ptrace-Split-function-kpatch_syscall_remote.patch b/0044-kpatch_ptrace-Split-function-kpatch_syscall_remote.patch new file mode 100644 index 0000000..fe64105 --- /dev/null +++ b/0044-kpatch_ptrace-Split-function-kpatch_syscall_remote.patch @@ -0,0 +1,191 @@ +From 335b90a54e629e0dc2f954ec2c3bd0b7e149aeec Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Mon, 12 Oct 2020 16:55:27 +0800 +Subject: [PATCH 44/89] kpatch_ptrace: Split function kpatch_syscall_remote + +The function kpatch_syscall_remote is arch related, first +rename it with kpatch_arch_syscall_remote, and then make +separate definations in arch/x86/arch_ptrace.c and arch/aarch64/arch_ptrace.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_ptrace.c | 31 ++++++++++++++++++++++++++++++- + src/arch/x86/arch_ptrace.c | 31 ++++++++++++++++++++++++++++++- + src/include/kpatch_ptrace.h | 5 +++++ + src/kpatch_ptrace.c | 33 ++------------------------------- + 4 files changed, 67 insertions(+), 33 deletions(-) + +diff --git a/src/arch/aarch64/arch_ptrace.c b/src/arch/aarch64/arch_ptrace.c +index 735927e..a444285 100644 +--- a/src/arch/aarch64/arch_ptrace.c ++++ b/src/arch/aarch64/arch_ptrace.c +@@ -22,6 +22,35 @@ + + #include + ++int kpatch_arch_syscall_remote(struct kpatch_ptrace_ctx *pctx, int nr, ++ unsigned long arg1, unsigned long arg2, unsigned long arg3, ++ unsigned long arg4, unsigned long arg5, unsigned long arg6, ++ unsigned long *res) ++{ ++ struct user_regs_struct regs; ++ ++ unsigned char syscall[] = { ++ 0x01, 0x00, 0x00, 0xd4, //0xd4000001 svc #0 = syscall ++ 0xa0, 0x00, 0x20, 0xd4, //0xd42000a0 brk #5 = int3 ++ }; ++ int ret; ++ ++ kpdebug("Executing syscall %d (pid %d)...\n", nr, pctx->pid); ++ regs.regs[8] = (unsigned long)nr; ++ regs.regs[0] = arg1; ++ regs.regs[1] = arg2; ++ regs.regs[2] = arg3; ++ regs.regs[3] = arg4; ++ regs.regs[4] = arg5; ++ regs.regs[5] = arg6; ++ ++ ret = kpatch_execute_remote(pctx, syscall, sizeof(syscall), ®s); ++ if (ret == 0) ++ *res = regs.regs[0]; ++ ++ return ret; ++} ++ + int kpatch_arch_prctl_remote(struct kpatch_ptrace_ctx *pctx, int code, unsigned long *addr) + { + struct user_regs_struct regs; +@@ -46,7 +75,7 @@ int kpatch_arch_prctl_remote(struct kpatch_ptrace_ctx *pctx, int code, unsigned + kplogerror("can't peek original stack data\n"); + return -1; + } +- //ret = kpatch_syscall_remote(pctx, __NR_arch_prctl, code, regs.sp, 0, 0, 0, 0, &res); ++ //ret = kpatch_arch_syscall_remote(pctx, __NR_arch_prctl, code, regs.sp, 0, 0, 0, 0, &res); + if (ret < 0) + goto poke; + if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { +diff --git a/src/arch/x86/arch_ptrace.c b/src/arch/x86/arch_ptrace.c +index 5f1e703..10127a3 100644 +--- a/src/arch/x86/arch_ptrace.c ++++ b/src/arch/x86/arch_ptrace.c +@@ -22,6 +22,35 @@ + + #include + ++int kpatch_arch_syscall_remote(struct kpatch_ptrace_ctx *pctx, int nr, ++ unsigned long arg1, unsigned long arg2, unsigned long arg3, ++ unsigned long arg4, unsigned long arg5, unsigned long arg6, ++ unsigned long *res) ++{ ++ struct user_regs_struct regs; ++ ++ unsigned char syscall[] = { ++ 0x0f, 0x05, /* syscall */ ++ 0xcc, /* int3 */ ++ }; ++ int ret; ++ ++ kpdebug("Executing syscall %d (pid %d)...\n", nr, pctx->pid); ++ regs.rax = (unsigned long)nr; ++ regs.rdi = arg1; ++ regs.rsi = arg2; ++ regs.rdx = arg3; ++ regs.r10 = arg4; ++ regs.r8 = arg5; ++ regs.r9 = arg6; ++ ++ ret = kpatch_execute_remote(pctx, syscall, sizeof(syscall), ®s); ++ if (ret == 0) ++ *res = regs.rax; ++ ++ return ret; ++} ++ + int kpatch_arch_prctl_remote(struct kpatch_ptrace_ctx *pctx, int code, unsigned long *addr) + { + struct user_regs_struct regs; +@@ -42,7 +71,7 @@ int kpatch_arch_prctl_remote(struct kpatch_ptrace_ctx *pctx, int code, unsigned + kplogerror("can't peek original stack data\n"); + return -1; + } +- ret = kpatch_syscall_remote(pctx, __NR_arch_prctl, code, regs.rsp, 0, 0, 0, 0, &res); ++ ret = kpatch_arch_syscall_remote(pctx, __NR_arch_prctl, code, regs.rsp, 0, 0, 0, 0, &res); + if (ret < 0) + goto poke; + if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { +diff --git a/src/include/kpatch_ptrace.h b/src/include/kpatch_ptrace.h +index ddaa9e6..c8cfd41 100644 +--- a/src/include/kpatch_ptrace.h ++++ b/src/include/kpatch_ptrace.h +@@ -136,4 +136,9 @@ kpatch_arch_execute_remote_func(struct kpatch_ptrace_ctx *pctx, + void *data), + void *data); + ++int kpatch_arch_syscall_remote(struct kpatch_ptrace_ctx *pctx, int nr, ++ unsigned long arg1, unsigned long arg2, unsigned long arg3, ++ unsigned long arg4, unsigned long arg5, unsigned long arg6, ++ unsigned long *res); ++ + #endif +diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c +index 4d2223e..057b08a 100644 +--- a/src/kpatch_ptrace.c ++++ b/src/kpatch_ptrace.c +@@ -643,35 +643,6 @@ kpatch_ptrace_kickstart_execve_wrapper(kpatch_process_t *proc) + return 0; + } + +-static int kpatch_syscall_remote(struct kpatch_ptrace_ctx *pctx, int nr, +- unsigned long arg1, unsigned long arg2, unsigned long arg3, +- unsigned long arg4, unsigned long arg5, unsigned long arg6, +- unsigned long *res) +-{ +- struct user_regs_struct regs; +- +- unsigned char syscall[] = { +- 0x0f, 0x05, /* syscall */ +- 0xcc, /* int3 */ +- }; +- int ret; +- +- kpdebug("Executing syscall %d (pid %d)...\n", nr, pctx->pid); +- regs.rax = (unsigned long)nr; +- regs.rdi = arg1; +- regs.rsi = arg2; +- regs.rdx = arg3; +- regs.r10 = arg4; +- regs.r8 = arg5; +- regs.r9 = arg6; +- +- ret = kpatch_execute_remote(pctx, syscall, sizeof(syscall), ®s); +- if (ret == 0) +- *res = regs.rax; +- +- return ret; +-} +- + unsigned long + kpatch_mmap_remote(struct kpatch_ptrace_ctx *pctx, + unsigned long addr, +@@ -686,7 +657,7 @@ kpatch_mmap_remote(struct kpatch_ptrace_ctx *pctx, + + kpdebug("mmap_remote: 0x%lx+%lx, %x, %x, %d, %lx\n", addr, length, + prot, flags, fd, offset); +- ret = kpatch_syscall_remote(pctx, __NR_mmap, (unsigned long)addr, ++ ret = kpatch_arch_syscall_remote(pctx, __NR_mmap, (unsigned long)addr, + length, prot, flags, fd, offset, &res); + if (ret < 0) + return 0; +@@ -705,7 +676,7 @@ int kpatch_munmap_remote(struct kpatch_ptrace_ctx *pctx, + unsigned long res; + + kpdebug("munmap_remote: 0x%lx+%lx\n", addr, length); +- ret = kpatch_syscall_remote(pctx, __NR_munmap, (unsigned long)addr, ++ ret = kpatch_arch_syscall_remote(pctx, __NR_munmap, (unsigned long)addr, + length, 0, 0, 0, 0, &res); + if (ret < 0) + return -1; +-- +2.23.0 + diff --git a/0045-kpatch_ptrace-Split-function-wait_for_mmap.patch b/0045-kpatch_ptrace-Split-function-wait_for_mmap.patch new file mode 100644 index 0000000..9556e27 --- /dev/null +++ b/0045-kpatch_ptrace-Split-function-wait_for_mmap.patch @@ -0,0 +1,239 @@ +From 05b64620354b8f8bd36c3f782eff9cd145f57fea Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Tue, 13 Oct 2020 11:10:11 +0800 +Subject: [PATCH 45/89] kpatch_ptrace: Split function wait_for_mmap + +The function wait_for_mmap is arch related, so make two separate +definations in arch/x86/arch_ptrace.c and arch/aarch64/arch_ptrace.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_ptrace.c | 58 ++++++++++++++++++++++++++++++++++ + src/arch/x86/arch_ptrace.c | 58 ++++++++++++++++++++++++++++++++++ + src/include/kpatch_ptrace.h | 3 ++ + src/kpatch_ptrace.c | 58 ---------------------------------- + 4 files changed, 119 insertions(+), 58 deletions(-) + +diff --git a/src/arch/aarch64/arch_ptrace.c b/src/arch/aarch64/arch_ptrace.c +index a444285..b21189e 100644 +--- a/src/arch/aarch64/arch_ptrace.c ++++ b/src/arch/aarch64/arch_ptrace.c +@@ -22,6 +22,64 @@ + + #include + ++int ++wait_for_mmap(struct kpatch_ptrace_ctx *pctx, ++ unsigned long *pbase) ++{ ++ int ret, status = 0, insyscall = 0; ++ long rv; ++ ++ while (1) { ++ ret = ptrace(PTRACE_SYSCALL, pctx->pid, NULL, ++ (void *)(uintptr_t)status); ++ if (ret < 0) { ++ kplogerror("can't PTRACE_SYSCALL tracee - %d\n", ++ pctx->pid); ++ return -1; ++ } ++ ++ ret = waitpid(pctx->pid, &status, __WALL); ++ if (ret < 0) { ++ kplogerror("can't wait tracee - %d\n", pctx->pid); ++ return -1; ++ } ++ ++ if (WIFEXITED(status)) { ++ status = WTERMSIG(status); ++ continue; ++ } else if (!WIFSTOPPED(status)) { ++ status = 0; ++ continue; ++ } ++ ++ status = 0; ++ ++ if (insyscall == 0) { ++ rv = ptrace(PTRACE_PEEKUSER, pctx->pid, ++ offsetof(struct user_regs_struct, ++ regs[29]), ++ NULL); ++ if (rv == -1) { ++ kplogerror("ptrace(PTRACE_PEEKUSER)\n"); ++ return -1; ++ } ++ insyscall = rv; ++ continue; ++ } else if (insyscall == __NR_mmap) { ++ rv = ptrace(PTRACE_PEEKUSER, pctx->pid, ++ offsetof(struct user_regs_struct, ++ regs[8]), ++ NULL); ++ *pbase = rv; ++ break; ++ } ++ ++ insyscall = !insyscall; ++ } ++ ++ return 0; ++} ++ + int kpatch_arch_syscall_remote(struct kpatch_ptrace_ctx *pctx, int nr, + unsigned long arg1, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5, unsigned long arg6, +diff --git a/src/arch/x86/arch_ptrace.c b/src/arch/x86/arch_ptrace.c +index 10127a3..0032cbd 100644 +--- a/src/arch/x86/arch_ptrace.c ++++ b/src/arch/x86/arch_ptrace.c +@@ -22,6 +22,64 @@ + + #include + ++int ++wait_for_mmap(struct kpatch_ptrace_ctx *pctx, ++ unsigned long *pbase) ++{ ++ int ret, status = 0, insyscall = 0; ++ long rv; ++ ++ while (1) { ++ ret = ptrace(PTRACE_SYSCALL, pctx->pid, NULL, ++ (void *)(uintptr_t)status); ++ if (ret < 0) { ++ kplogerror("can't PTRACE_SYSCALL tracee - %d\n", ++ pctx->pid); ++ return -1; ++ } ++ ++ ret = waitpid(pctx->pid, &status, __WALL); ++ if (ret < 0) { ++ kplogerror("can't wait tracee - %d\n", pctx->pid); ++ return -1; ++ } ++ ++ if (WIFEXITED(status)) { ++ status = WTERMSIG(status); ++ continue; ++ } else if (!WIFSTOPPED(status)) { ++ status = 0; ++ continue; ++ } ++ ++ status = 0; ++ ++ if (insyscall == 0) { ++ rv = ptrace(PTRACE_PEEKUSER, pctx->pid, ++ offsetof(struct user_regs_struct, ++ orig_rax), ++ NULL); ++ if (rv == -1) { ++ kplogerror("ptrace(PTRACE_PEEKUSER)\n"); ++ return -1; ++ } ++ insyscall = rv; ++ continue; ++ } else if (insyscall == __NR_mmap) { ++ rv = ptrace(PTRACE_PEEKUSER, pctx->pid, ++ offsetof(struct user_regs_struct, ++ rax), ++ NULL); ++ *pbase = rv; ++ break; ++ } ++ ++ insyscall = !insyscall; ++ } ++ ++ return 0; ++} ++ + int kpatch_arch_syscall_remote(struct kpatch_ptrace_ctx *pctx, int nr, + unsigned long arg1, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5, unsigned long arg6, +diff --git a/src/include/kpatch_ptrace.h b/src/include/kpatch_ptrace.h +index c8cfd41..5abcf26 100644 +--- a/src/include/kpatch_ptrace.h ++++ b/src/include/kpatch_ptrace.h +@@ -141,4 +141,7 @@ int kpatch_arch_syscall_remote(struct kpatch_ptrace_ctx *pctx, int nr, + unsigned long arg4, unsigned long arg5, unsigned long arg6, + unsigned long *res); + ++int wait_for_mmap(struct kpatch_ptrace_ctx *pctx, ++ unsigned long *pbase); ++ + #endif +diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c +index 057b08a..7ab550c 100644 +--- a/src/kpatch_ptrace.c ++++ b/src/kpatch_ptrace.c +@@ -448,64 +448,6 @@ wait_for_stop(struct kpatch_ptrace_ctx *pctx, + return 0; + } + +-static int +-wait_for_mmap(struct kpatch_ptrace_ctx *pctx, +- unsigned long *pbase) +-{ +- int ret, status = 0, insyscall = 0; +- long rv; +- +- while (1) { +- ret = ptrace(PTRACE_SYSCALL, pctx->pid, NULL, +- (void *)(uintptr_t)status); +- if (ret < 0) { +- kplogerror("can't PTRACE_SYSCALL tracee - %d\n", +- pctx->pid); +- return -1; +- } +- +- ret = waitpid(pctx->pid, &status, __WALL); +- if (ret < 0) { +- kplogerror("can't wait tracee - %d\n", pctx->pid); +- return -1; +- } +- +- if (WIFEXITED(status)) { +- status = WTERMSIG(status); +- continue; +- } else if (!WIFSTOPPED(status)) { +- status = 0; +- continue; +- } +- +- status = 0; +- +- if (insyscall == 0) { +- rv = ptrace(PTRACE_PEEKUSER, pctx->pid, +- offsetof(struct user_regs_struct, +- orig_rax), +- NULL); +- if (rv == -1) { +- kplogerror("ptrace(PTRACE_PEEKUSER)\n"); +- return -1; +- } +- insyscall = rv; +- continue; +- } else if (insyscall == __NR_mmap) { +- rv = ptrace(PTRACE_PEEKUSER, pctx->pid, +- offsetof(struct user_regs_struct, +- rax), +- NULL); +- *pbase = rv; +- break; +- } +- +- insyscall = !insyscall; +- } +- +- return 0; +-} +- + int + kpatch_execute_remote(struct kpatch_ptrace_ctx *pctx, + const unsigned char *code, +-- +2.23.0 + diff --git a/0046-kpatch_ptrace-Split-function-kpatch_ptrace_kickstart.patch b/0046-kpatch_ptrace-Split-function-kpatch_ptrace_kickstart.patch new file mode 100644 index 0000000..e8aef2a --- /dev/null +++ b/0046-kpatch_ptrace-Split-function-kpatch_ptrace_kickstart.patch @@ -0,0 +1,401 @@ +From f53cbbe63cbe16b2b0eb0466b5c85ea35f68da2b Mon Sep 17 00:00:00 2001 +From: Jiajie Li +Date: Tue, 13 Oct 2020 11:22:27 +0800 +Subject: [PATCH 46/89] kpatch_ptrace: Split function + kpatch_ptrace_kickstart_execve_wrapper + +The function kpatch_ptrace_kickstart_execve_wrapper is arch related, +first rename it with kpatch_arch_ptrace_kickstart_execve_wrapper, +and then make separate definations in arch/x86/arch_ptrace.c and +arch/aarch64/arch_ptrace.c + +Signed-off-by: Jiajie Li +Signed-off-by: Ying Fang +--- + src/arch/aarch64/arch_ptrace.c | 99 +++++++++++++++++++++++++++++++ + src/arch/x86/arch_ptrace.c | 99 +++++++++++++++++++++++++++++++ + src/include/kpatch_ptrace.h | 5 +- + src/kpatch_process.c | 2 +- + src/kpatch_ptrace.c | 103 +-------------------------------- + 5 files changed, 205 insertions(+), 103 deletions(-) + +diff --git a/src/arch/aarch64/arch_ptrace.c b/src/arch/aarch64/arch_ptrace.c +index b21189e..9f87d10 100644 +--- a/src/arch/aarch64/arch_ptrace.c ++++ b/src/arch/aarch64/arch_ptrace.c +@@ -22,6 +22,105 @@ + + #include + ++/** ++ * This is rather tricky since we are accounting for the non-main ++ * thread calling for execve(). See `ptrace(2)` for details. ++ * ++ * FIXME(pboldin): this is broken for multi-threaded calls ++ * to execve. Sight. ++ */ ++int ++kpatch_arch_ptrace_kickstart_execve_wrapper(kpatch_process_t *proc) ++{ ++ int ret, pid = 0; ++ struct kpatch_ptrace_ctx *pctx, *ptmp, *execve_pctx = NULL; ++ long rv; ++ ++ kpdebug("kpatch_arch_ptrace_kickstart_execve_wrapper\n"); ++ ++ list_for_each_entry(pctx, &proc->ptrace.pctxs, list) { ++ /* proc->pid equals to THREAD ID of the thread ++ * executing execve.so's version of execve ++ */ ++ if (pctx->pid != proc->pid) ++ continue; ++ execve_pctx = pctx; ++ break; ++ } ++ ++ if (execve_pctx == NULL) { ++ kperr("can't find thread executing execve"); ++ return -1; ++ } ++ ++ /* Send a message to our `execve` wrapper so it will continue ++ * execution ++ */ ++ ret = send(proc->send_fd, &ret, sizeof(ret), 0); ++ if (ret < 0) { ++ kplogerror("send failed\n"); ++ return ret; ++ } ++ ++ /* Wait for it to reach BRKN instruction just before real execve */ ++ while (1) { ++ ret = wait_for_stop(execve_pctx, NULL); ++ if (ret < 0) { ++ kplogerror("wait_for_stop\n"); ++ return ret; ++ } ++ ++ rv = ptrace(PTRACE_PEEKUSER, execve_pctx->pid, ++ offsetof(struct user_regs_struct, pc), ++ NULL); ++ if (rv == -1) ++ return rv; ++ ++ rv = ptrace(PTRACE_PEEKTEXT, execve_pctx->pid, ++ rv - 1, NULL); ++ if (rv == -1) ++ return rv; ++ if ((unsigned char)rv == 0xcc) ++ break; ++ } ++ ++ /* Wait for SIGTRAP from the execve. It happens from the thread ++ * group ID, so find it if thread doing execve() is not it. */ ++ if (execve_pctx != proc2pctx(proc)) { ++ pid = get_threadgroup_id(proc->pid); ++ if (pid < 0) ++ return -1; ++ ++ proc->pid = pid; ++ } ++ ++ ret = wait_for_stop(execve_pctx, (void *)(uintptr_t)pid); ++ if (ret < 0) { ++ kplogerror("waitpid\n"); ++ return ret; ++ } ++ ++ list_for_each_entry_safe(pctx, ptmp, &proc->ptrace.pctxs, list) { ++ if (pctx->pid == proc->pid) ++ continue; ++ kpatch_ptrace_detach(pctx); ++ kpatch_ptrace_ctx_destroy(pctx); ++ } ++ ++ /* Suddenly, /proc/pid/mem gets invalidated */ ++ { ++ char buf[128]; ++ close(proc->memfd); ++ ++ snprintf(buf, sizeof(buf), "/proc/%d/mem", proc->pid); ++ proc->memfd = open(buf, O_RDWR); ++ } ++ ++ kpdebug("...done\n"); ++ ++ return 0; ++} ++ + int + wait_for_mmap(struct kpatch_ptrace_ctx *pctx, + unsigned long *pbase) +diff --git a/src/arch/x86/arch_ptrace.c b/src/arch/x86/arch_ptrace.c +index 0032cbd..ef0f460 100644 +--- a/src/arch/x86/arch_ptrace.c ++++ b/src/arch/x86/arch_ptrace.c +@@ -22,6 +22,105 @@ + + #include + ++/** ++ * This is rather tricky since we are accounting for the non-main ++ * thread calling for execve(). See `ptrace(2)` for details. ++ * ++ * FIXME(pboldin): this is broken for multi-threaded calls ++ * to execve. Sight. ++ */ ++int ++kpatch_arch_ptrace_kickstart_execve_wrapper(kpatch_process_t *proc) ++{ ++ int ret, pid = 0; ++ struct kpatch_ptrace_ctx *pctx, *ptmp, *execve_pctx = NULL; ++ long rv; ++ ++ kpdebug("kpatch_arch_ptrace_kickstart_execve_wrapper\n"); ++ ++ list_for_each_entry(pctx, &proc->ptrace.pctxs, list) { ++ /* proc->pid equals to THREAD ID of the thread ++ * executing execve.so's version of execve ++ */ ++ if (pctx->pid != proc->pid) ++ continue; ++ execve_pctx = pctx; ++ break; ++ } ++ ++ if (execve_pctx == NULL) { ++ kperr("can't find thread executing execve"); ++ return -1; ++ } ++ ++ /* Send a message to our `execve` wrapper so it will continue ++ * execution ++ */ ++ ret = send(proc->send_fd, &ret, sizeof(ret), 0); ++ if (ret < 0) { ++ kplogerror("send failed\n"); ++ return ret; ++ } ++ ++ /* Wait for it to reach BRKN instruction just before real execve */ ++ while (1) { ++ ret = wait_for_stop(execve_pctx, NULL); ++ if (ret < 0) { ++ kplogerror("wait_for_stop\n"); ++ return ret; ++ } ++ ++ rv = ptrace(PTRACE_PEEKUSER, execve_pctx->pid, ++ offsetof(struct user_regs_struct, rip), ++ NULL); ++ if (rv == -1) ++ return rv; ++ ++ rv = ptrace(PTRACE_PEEKTEXT, execve_pctx->pid, ++ rv - 1, NULL); ++ if (rv == -1) ++ return rv; ++ if ((unsigned char)rv == 0xcc) ++ break; ++ } ++ ++ /* Wait for SIGTRAP from the execve. It happens from the thread ++ * group ID, so find it if thread doing execve() is not it. */ ++ if (execve_pctx != proc2pctx(proc)) { ++ pid = get_threadgroup_id(proc->pid); ++ if (pid < 0) ++ return -1; ++ ++ proc->pid = pid; ++ } ++ ++ ret = wait_for_stop(execve_pctx, (void *)(uintptr_t)pid); ++ if (ret < 0) { ++ kplogerror("waitpid\n"); ++ return ret; ++ } ++ ++ list_for_each_entry_safe(pctx, ptmp, &proc->ptrace.pctxs, list) { ++ if (pctx->pid == proc->pid) ++ continue; ++ kpatch_ptrace_detach(pctx); ++ kpatch_ptrace_ctx_destroy(pctx); ++ } ++ ++ /* Suddenly, /proc/pid/mem gets invalidated */ ++ { ++ char buf[128]; ++ close(proc->memfd); ++ ++ snprintf(buf, sizeof(buf), "/proc/%d/mem", proc->pid); ++ proc->memfd = open(buf, O_RDWR); ++ } ++ ++ kpdebug("...done\n"); ++ ++ return 0; ++} ++ + int + wait_for_mmap(struct kpatch_ptrace_ctx *pctx, + unsigned long *pbase) +diff --git a/src/include/kpatch_ptrace.h b/src/include/kpatch_ptrace.h +index 5abcf26..f0e83c0 100644 +--- a/src/include/kpatch_ptrace.h ++++ b/src/include/kpatch_ptrace.h +@@ -55,7 +55,10 @@ int kpatch_ptrace_detach(struct kpatch_ptrace_ctx *pctx); + int kpatch_ptrace_handle_ld_linux(kpatch_process_t *proc, + unsigned long *pentry_point); + +-int kpatch_ptrace_kickstart_execve_wrapper(kpatch_process_t *proc); ++ ++int wait_for_stop(struct kpatch_ptrace_ctx *pctx, void *data); ++int get_threadgroup_id(int tid); ++int kpatch_arch_ptrace_kickstart_execve_wrapper(kpatch_process_t *proc); + int kpatch_ptrace_get_entry_point(struct kpatch_ptrace_ctx *pctx, + unsigned long *pentry_point); + +diff --git a/src/kpatch_process.c b/src/kpatch_process.c +index 9561962..f987b7e 100644 +--- a/src/kpatch_process.c ++++ b/src/kpatch_process.c +@@ -856,7 +856,7 @@ kpatch_process_kickstart_execve_wrapper(kpatch_process_t *proc) + { + int ret; + +- ret = kpatch_ptrace_kickstart_execve_wrapper(proc); ++ ret = kpatch_arch_ptrace_kickstart_execve_wrapper(proc); + if (ret < 0) + return -1; + +diff --git a/src/kpatch_ptrace.c b/src/kpatch_ptrace.c +index 7ab550c..d0bfbdd 100644 +--- a/src/kpatch_ptrace.c ++++ b/src/kpatch_ptrace.c +@@ -413,7 +413,7 @@ poke_back: + return ret; + } + +-static int ++int + wait_for_stop(struct kpatch_ptrace_ctx *pctx, + void *data) + { +@@ -463,7 +463,7 @@ kpatch_execute_remote(struct kpatch_ptrace_ctx *pctx, + } + + /* FIXME(pboldin) buf might be too small */ +-static int ++int + get_threadgroup_id(int tid) + { + FILE *fh; +@@ -486,105 +486,6 @@ get_threadgroup_id(int tid) + return pid; + } + +-/** +- * This is rather tricky since we are accounting for the non-main +- * thread calling for execve(). See `ptrace(2)` for details. +- * +- * FIXME(pboldin): this is broken for multi-threaded calls +- * to execve. Sight. +- */ +-int +-kpatch_ptrace_kickstart_execve_wrapper(kpatch_process_t *proc) +-{ +- int ret, pid = 0; +- struct kpatch_ptrace_ctx *pctx, *ptmp, *execve_pctx = NULL; +- long rv; +- +- kpdebug("kpatch_ptrace_kickstart_execve_wrapper\n"); +- +- list_for_each_entry(pctx, &proc->ptrace.pctxs, list) { +- /* proc->pid equals to THREAD ID of the thread +- * executing execve.so's version of execve +- */ +- if (pctx->pid != proc->pid) +- continue; +- execve_pctx = pctx; +- break; +- } +- +- if (execve_pctx == NULL) { +- kperr("can't find thread executing execve"); +- return -1; +- } +- +- /* Send a message to our `execve` wrapper so it will continue +- * execution +- */ +- ret = send(proc->send_fd, &ret, sizeof(ret), 0); +- if (ret < 0) { +- kplogerror("send failed\n"); +- return ret; +- } +- +- /* Wait for it to reach BRKN instruction just before real execve */ +- while (1) { +- ret = wait_for_stop(execve_pctx, NULL); +- if (ret < 0) { +- kplogerror("wait_for_stop\n"); +- return ret; +- } +- +- rv = ptrace(PTRACE_PEEKUSER, execve_pctx->pid, +- offsetof(struct user_regs_struct, rip), +- NULL); +- if (rv == -1) +- return rv; +- +- rv = ptrace(PTRACE_PEEKTEXT, execve_pctx->pid, +- rv - 1, NULL); +- if (rv == -1) +- return rv; +- if ((unsigned char)rv == 0xcc) +- break; +- } +- +- /* Wait for SIGTRAP from the execve. It happens from the thread +- * group ID, so find it if thread doing execve() is not it. */ +- if (execve_pctx != proc2pctx(proc)) { +- pid = get_threadgroup_id(proc->pid); +- if (pid < 0) +- return -1; +- +- proc->pid = pid; +- } +- +- ret = wait_for_stop(execve_pctx, (void *)(uintptr_t)pid); +- if (ret < 0) { +- kplogerror("waitpid\n"); +- return ret; +- } +- +- list_for_each_entry_safe(pctx, ptmp, &proc->ptrace.pctxs, list) { +- if (pctx->pid == proc->pid) +- continue; +- kpatch_ptrace_detach(pctx); +- kpatch_ptrace_ctx_destroy(pctx); +- } +- +- /* Suddenly, /proc/pid/mem gets invalidated */ +- { +- char buf[128]; +- close(proc->memfd); +- +- snprintf(buf, sizeof(buf), "/proc/%d/mem", proc->pid); +- proc->memfd = open(buf, O_RDWR); +- } +- +- kpdebug("...done\n"); +- +- return 0; +-} +- + unsigned long + kpatch_mmap_remote(struct kpatch_ptrace_ctx *pctx, + unsigned long addr, +-- +2.23.0 + diff --git a/libcareplus.spec b/libcareplus.spec index 884aa95..c886568 100644 --- a/libcareplus.spec +++ b/libcareplus.spec @@ -1,15 +1,60 @@ Version: 0.1.4 Name: libcareplus Summary: LibcarePlus tools -Release: 2 +Release: 3 Group: Applications/System License: GPLv2 Url: https://gitee.com/openeuler/libcareplus Source0: %{name}-%{version}.tar.gz Patch0001: src-Makefile-install-kpatch_gensrc-into-bindir.patch +Patch0002: 0001-Split-kpatch_storage.c-from-kpatch_user.c.patch +Patch0003: 0002-Split-kpatch_patch.c-from-kpatch_user.c.patch +Patch0004: 0003-cmd_patch-pass-arguments-directly.patch +Patch0005: 0004-travis-use-VM-for-now.patch +Patch0006: 0005-scripts-pkgbuild-add-prepare_env-hook.patch +Patch0007: 0006-pkgbuild-fix-for-non-root-rpmbuild-built-root.patch +Patch0008: 0007-Toil-package-builder.patch +Patch0009: 0008-pkgbuild-use-yumdownloader-if-source-url-is-missing.patch +Patch0010: 0009-execve-abort-on-failure.patch +Patch0011: 0010-Add-test-stage-to-pkgbuild.patch +Patch0012: 0011-glibc-minimal-readme-for-toil-builder.patch +Patch0013: 0012-Fix-kpatch_process_init-kpatch_coroutines_free.patch +Patch0014: 0013-Add-libcare-stresstest.patch +Patch0015: 0014-read-auxv-from-proc-pid-auxv.patch +Patch0016: 0015-add-fail-to-unpatch-test.patch +Patch0017: 0016-Waitpid-for-finished-threads-after-detach.patch +Patch0018: 0017-.gitignore-build-artefacts.patch +Patch0019: 0018-kpatch_storage-put-an-end-to-description-string-load.patch +Patch0020: 0019-Fix-README-files.patch +Patch0021: 0020-include-Create-include-directory-for-header-files.patch +Patch0022: 0021-src-Update-header-file-position.patch +Patch0023: 0022-arch-Create-arch-directory-to-support-multi-arch.patch +Patch0024: 0023-config-configure-out-the-running-arch.patch +Patch0025: 0024-Makefile-Adapt-Makefile-for-different-architectures.patch +Patch0026: 0025-kpatch_parse-Update-asm_directives-for-aarch64.patch +Patch0027: 0026-kpatch_parse-Split-function-parse_ctype.patch +Patch0028: 0027-kpatch_parse-Split-function-init_multilines.patch +Patch0029: 0028-kpatch_parse-Split-function-is_variable_start.patch +Patch0030: 0029-kpatch_parse-Split-function-is_data_def.patch +Patch0031: 0030-kpatch_parse-Split-function-is_function_start.patch +Patch0032: 0031-kpatch_common.h-Factor-out-PAGE_SIZE-marco.patch +Patch0033: 0032-kpatch_coro-Split-function-_UCORO_access_reg.patch +Patch0034: 0033-kpatch_coro-Split-function-get_ptr_guard.patch +Patch0035: 0034-kpatch_coro-Split-function-locate_start_context_symb.patch +Patch0036: 0035-kpatch_patch-Split-function-patch_apply_hunk.patch +Patch0037: 0036-kpatch_elf-Split-function-kpatch_add_jmp_entry.patch +Patch0038: 0037-kpatch_elf-Split-function-kpatch_apply_relocate_add.patch +Patch0039: 0038-kpatch_process-Split-function-object_find_patch_regi.patch +Patch0040: 0039-kpatch_ptrace-Split-function-kpatch_ptrace_waitpid.patch +Patch0041: 0040-kpatch_ptrace-Split-function-copy_regs.patch +Patch0042: 0041-kpatch_ptrace-Split-function-kpatch_execute_remote_f.patch +Patch0043: 0042-kpatch_ptrace-Split-function-kpatch_ptrace_resolve_i.patch +Patch0044: 0043-kpatch_ptrace-Split-function-kpatch_arch_prctl_remot.patch +Patch0045: 0044-kpatch_ptrace-Split-function-kpatch_syscall_remote.patch +Patch0046: 0045-kpatch_ptrace-Split-function-wait_for_mmap.patch +Patch0047: 0046-kpatch_ptrace-Split-function-kpatch_ptrace_kickstart.patch -ExclusiveArch: x86_64 BuildRequires: elfutils-libelf-devel libunwind-devel %if 0%{with selinux} @@ -53,7 +98,9 @@ LibcarePlus devel files. %autopatch -p1 %build - +cd src +sh ./config +cd ../ make -C src %if 0%{with selinux} make -C dist/selinux @@ -163,6 +210,9 @@ exit 0 %endif %changelog +* Tue Feb 09 2021 Jiajie Li - 0.1.4-3 +- Add basic support libcareplus on aarch64 + * Mon Dec 28 2020 sunguoshuai - 0.1.4-2 - Del the {dist} in release.