From 5b2df760c2f664d3cc07ab9a1c30a55908f18347 Mon Sep 17 00:00:00 2001 From: lzwycc Date: Fri, 18 Aug 2023 16:49:04 -0400 Subject: [PATCH] upatch-manage: apply patch whitout kernel patch apply patch whitout kernel patch Signed-off-by: lzwycc --- upatch/CMakeLists.txt | 2 +- upatch/upatch-diff/CMakeLists.txt | 1 - upatch/upatch-diff/create-diff-object.c | 5 +- upatch/upatch-manage/CMakeLists.txt | 42 +- upatch/upatch-manage/Makefile | 23 - upatch/upatch-manage/arch/aarch64/insn.c | 130 +++ upatch/upatch-manage/arch/aarch64/insn.h | 71 ++ .../{kmod.h => arch/aarch64/process.h} | 17 +- upatch/upatch-manage/arch/aarch64/ptrace.c | 160 +++ .../upatch-manage/arch/aarch64/relocation.c | 313 +++++ upatch/upatch-manage/arch/aarch64/resolve.c | 155 +++ upatch/upatch-manage/arch/arm64/insn.c | 112 -- upatch/upatch-manage/arch/arm64/insn.h | 16 - upatch/upatch-manage/arch/arm64/patch-load.c | 449 -------- .../arch/arm64/patch-syscall-impl.S | 25 - .../arch/arm64/patch-syscall-register.c | 29 - upatch/upatch-manage/arch/patch-load.h | 44 - upatch/upatch-manage/arch/patch-syscall.h | 23 - upatch/upatch-manage/arch/x86/patch-load.c | 242 ---- .../arch/x86/patch-syscall-impl.S | 31 - .../arch/x86/patch-syscall-register.c | 29 - upatch/upatch-manage/arch/x86_64/process.h | 17 + upatch/upatch-manage/arch/x86_64/ptrace.c | 145 +++ upatch/upatch-manage/arch/x86_64/relocation.c | 129 +++ upatch/upatch-manage/arch/x86_64/resolve.c | 122 ++ upatch/upatch-manage/common.c | 151 --- upatch/upatch-manage/common.h | 36 - ...BE_ALTER_PC-flag-for-uprobe-handlers.patch | 89 -- upatch/upatch-manage/kmod.c | 292 ----- upatch/upatch-manage/list.h | 516 +++++++++ upatch/upatch-manage/log.h | 75 ++ upatch/upatch-manage/patch-load.c | 1008 ----------------- upatch/upatch-manage/patch-syscall.c | 60 - upatch/upatch-manage/patch-syscall.h | 20 - upatch/upatch-manage/patch-uprobe.c | 66 -- upatch/upatch-manage/patch-uprobe.h | 26 - upatch/upatch-manage/patch.c | 829 -------------- upatch/upatch-manage/patch.h | 174 --- upatch/upatch-manage/upatch-common.c | 17 + upatch/upatch-manage/upatch-common.h | 53 + upatch/upatch-manage/upatch-elf.c | 224 ++++ upatch/upatch-manage/upatch-elf.h | 141 +++ upatch/upatch-manage/upatch-ioctl.h | 38 - upatch/upatch-manage/upatch-manage.c | 227 ++++ upatch/upatch-manage/upatch-manage.h | 24 - upatch/upatch-manage/upatch-patch.c | 904 +++++++++++++++ upatch/upatch-manage/upatch-patch.h | 23 + upatch/upatch-manage/upatch-process.c | 823 ++++++++++++++ upatch/upatch-manage/upatch-process.h | 125 ++ upatch/upatch-manage/upatch-ptrace.c | 266 +++++ upatch/upatch-manage/upatch-ptrace.h | 63 ++ upatch/upatch-manage/upatch-relocation.c | 46 + upatch/upatch-manage/upatch-relocation.h | 31 + upatch/upatch-manage/upatch-resolve.c | 246 ++++ upatch/upatch-manage/upatch-resolve.h | 33 + upatch/upatch-tool/CMakeLists.txt | 1 - upatch/upatch-tool/upatch-tool.c | 5 +- 57 files changed, 5095 insertions(+), 3869 deletions(-) delete mode 100755 upatch/upatch-manage/Makefile create mode 100644 upatch/upatch-manage/arch/aarch64/insn.c create mode 100644 upatch/upatch-manage/arch/aarch64/insn.h rename upatch/upatch-manage/{kmod.h => arch/aarch64/process.h} (30%) mode change 100755 => 100644 create mode 100644 upatch/upatch-manage/arch/aarch64/ptrace.c create mode 100644 upatch/upatch-manage/arch/aarch64/relocation.c create mode 100644 upatch/upatch-manage/arch/aarch64/resolve.c delete mode 100644 upatch/upatch-manage/arch/arm64/insn.c delete mode 100644 upatch/upatch-manage/arch/arm64/insn.h delete mode 100644 upatch/upatch-manage/arch/arm64/patch-load.c delete mode 100755 upatch/upatch-manage/arch/arm64/patch-syscall-impl.S delete mode 100644 upatch/upatch-manage/arch/arm64/patch-syscall-register.c delete mode 100644 upatch/upatch-manage/arch/patch-load.h delete mode 100644 upatch/upatch-manage/arch/patch-syscall.h delete mode 100644 upatch/upatch-manage/arch/x86/patch-load.c delete mode 100755 upatch/upatch-manage/arch/x86/patch-syscall-impl.S delete mode 100644 upatch/upatch-manage/arch/x86/patch-syscall-register.c create mode 100644 upatch/upatch-manage/arch/x86_64/process.h create mode 100644 upatch/upatch-manage/arch/x86_64/ptrace.c create mode 100644 upatch/upatch-manage/arch/x86_64/relocation.c create mode 100644 upatch/upatch-manage/arch/x86_64/resolve.c delete mode 100644 upatch/upatch-manage/common.c delete mode 100644 upatch/upatch-manage/common.h delete mode 100755 upatch/upatch-manage/kernel-patch/0001-uprobe-add-UPROBE_ALTER_PC-flag-for-uprobe-handlers.patch delete mode 100755 upatch/upatch-manage/kmod.c create mode 100644 upatch/upatch-manage/list.h create mode 100644 upatch/upatch-manage/log.h delete mode 100644 upatch/upatch-manage/patch-load.c delete mode 100644 upatch/upatch-manage/patch-syscall.c delete mode 100644 upatch/upatch-manage/patch-syscall.h delete mode 100644 upatch/upatch-manage/patch-uprobe.c delete mode 100644 upatch/upatch-manage/patch-uprobe.h delete mode 100644 upatch/upatch-manage/patch.c delete mode 100644 upatch/upatch-manage/patch.h create mode 100644 upatch/upatch-manage/upatch-common.c create mode 100644 upatch/upatch-manage/upatch-common.h create mode 100644 upatch/upatch-manage/upatch-elf.c create mode 100644 upatch/upatch-manage/upatch-elf.h delete mode 100644 upatch/upatch-manage/upatch-ioctl.h create mode 100644 upatch/upatch-manage/upatch-manage.c delete mode 100644 upatch/upatch-manage/upatch-manage.h create mode 100644 upatch/upatch-manage/upatch-patch.c create mode 100644 upatch/upatch-manage/upatch-patch.h create mode 100644 upatch/upatch-manage/upatch-process.c create mode 100644 upatch/upatch-manage/upatch-process.h create mode 100644 upatch/upatch-manage/upatch-ptrace.c create mode 100644 upatch/upatch-manage/upatch-ptrace.h create mode 100644 upatch/upatch-manage/upatch-relocation.c create mode 100644 upatch/upatch-manage/upatch-relocation.h create mode 100644 upatch/upatch-manage/upatch-resolve.c create mode 100644 upatch/upatch-manage/upatch-resolve.h diff --git a/upatch/CMakeLists.txt b/upatch/CMakeLists.txt index e5099d9..6517e33 100644 --- a/upatch/CMakeLists.txt +++ b/upatch/CMakeLists.txt @@ -3,7 +3,7 @@ add_compile_options(-DUPATCH_VERSION="${SYSCARE_VERSION}") add_subdirectory(upatch-diff) -add_subdirectory(upatch-tool) +# add_subdirectory(upatch-tool) add_subdirectory(upatch-manage) add_subdirectory(upatch-compile) diff --git a/upatch/upatch-diff/CMakeLists.txt b/upatch/upatch-diff/CMakeLists.txt index 45091fc..eec3b34 100644 --- a/upatch/upatch-diff/CMakeLists.txt +++ b/upatch/upatch-diff/CMakeLists.txt @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 include_directories( - ../upatch-manage/ ./ ./insn/ ) diff --git a/upatch/upatch-diff/create-diff-object.c b/upatch/upatch-diff/create-diff-object.c index 94796bb..8f441c3 100644 --- a/upatch/upatch-diff/create-diff-object.c +++ b/upatch/upatch-diff/create-diff-object.c @@ -56,9 +56,12 @@ #include "elf-resolve.h" #include "elf-create.h" #include "running-elf.h" -#include "upatch-manage.h" #include "upatch-patch.h" +#ifndef UPATCH_VERSION +#define UPATCH_VERSION "dev" +#endif + enum loglevel loglevel = NORMAL; char *logprefix; char *upatch_elf_name; diff --git a/upatch/upatch-manage/CMakeLists.txt b/upatch/upatch-manage/CMakeLists.txt index 30be928..5204398 100644 --- a/upatch/upatch-manage/CMakeLists.txt +++ b/upatch/upatch-manage/CMakeLists.txt @@ -1,28 +1,34 @@ # SPDX-License-Identifier: GPL-2.0 -set(UPATCH_KMOD "upatch.ko") +set(UPATCH_MANAGE "upatch-manage") -if (DEFINED KERNEL_VERSION) - set(KERNEL_BUILD /lib/modules/${KERNEL_VERSION}/build) - set(UPATCH_KMOD_CMD make UPATCH_VERSION=${SYSCARE_VERSION} kernel=${KERNEL_BUILD}) -else() - set(UPATCH_KMOD_CMD make UPATCH_VERSION=${SYSCARE_VERSION}) -endif() +EXECUTE_PROCESS(COMMAND uname -m + OUTPUT_VARIABLE ARCH + OUTPUT_STRIP_TRAILING_WHITESPACE) -add_custom_target(upatch-kmod ALL - COMMENT "Compiling upatch-kmod..." - BYPRODUCTS ${UPATCH_KMOD} - COMMAND ${UPATCH_KMOD_CMD} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +set(ARCH_PATH arch/${ARCH}) + +include_directories( + arch/${ARCH}/ + ./ +) + +file(GLOB HOST_SRC_FILES + arch/${ARCH}/*.c + *.c ) +add_compile_options(-g -Wall) +add_executable(${UPATCH_MANAGE} ${HOST_SRC_FILES}) +target_link_libraries(${UPATCH_MANAGE} elf) + install( - FILES - ${UPATCH_KMOD} + TARGETS + ${UPATCH_MANAGE} PERMISSIONS - OWNER_WRITE OWNER_READ - GROUP_READ - WORLD_READ + OWNER_EXECUTE OWNER_WRITE OWNER_READ + GROUP_EXECUTE GROUP_READ + WORLD_READ WORLD_EXECUTE DESTINATION ${SYSCARE_LIBEXEC_DIR} -) +) \ No newline at end of file diff --git a/upatch/upatch-manage/Makefile b/upatch/upatch-manage/Makefile deleted file mode 100755 index f067b61..0000000 --- a/upatch/upatch-manage/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -kernel ?= /lib/modules/$(shell uname -r)/build - -EXTRA_CFLAGS += -I$(PWD) -EXTRA_CFLAGS += -I$(PWD)/../upatch-diff - -ifdef UPATCH_VERSION - EXTRA_CFLAGS += -DUPATCH_VERSION=\"$(UPATCH_VERSION)\" -endif - -obj-m += upatch.o -upatch-objs := kmod.o common.o -upatch-objs += patch-uprobe.o patch-syscall.o patch.o patch-load.o -upatch-objs += arch/$(ARCH)/patch-syscall-impl.o arch/$(ARCH)/patch-syscall-register.o arch/$(ARCH)/patch-load.o -ifeq ($(ARCH), arm64) - upatch-objs += arch/$(ARCH)/insn.o -endif - -all: - make -C $(kernel) M=$(shell pwd) modules - -clean: - make -C $(kernel) M=$(shell pwd) clean diff --git a/upatch/upatch-manage/arch/aarch64/insn.c b/upatch/upatch-manage/arch/aarch64/insn.c new file mode 100644 index 0000000..02faa3e --- /dev/null +++ b/upatch/upatch-manage/arch/aarch64/insn.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 Huawei Ltd. + * Author: Jiang Liu + * + * Copyright (C) 2014-2016 Zi Shen Lim + */ + +#include + +#include "insn.h" + +static int aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type type, + u32 *maskp, int *shiftp) +{ + u32 mask; + int shift; + + switch (type) { + case AARCH64_INSN_IMM_26: + mask = BIT(26) - 1; + shift = 0; + break; + case AARCH64_INSN_IMM_19: + mask = BIT(19) - 1; + shift = 5; + break; + case AARCH64_INSN_IMM_16: + mask = BIT(16) - 1; + shift = 5; + break; + case AARCH64_INSN_IMM_14: + mask = BIT(14) - 1; + shift = 5; + break; + case AARCH64_INSN_IMM_12: + mask = BIT(12) - 1; + shift = 10; + break; + case AARCH64_INSN_IMM_9: + mask = BIT(9) - 1; + shift = 12; + break; + case AARCH64_INSN_IMM_7: + mask = BIT(7) - 1; + shift = 15; + break; + case AARCH64_INSN_IMM_6: + case AARCH64_INSN_IMM_S: + mask = BIT(6) - 1; + shift = 10; + break; + case AARCH64_INSN_IMM_R: + mask = BIT(6) - 1; + shift = 16; + break; + case AARCH64_INSN_IMM_N: + mask = 1; + shift = 22; + break; + default: + return -EINVAL; + } + + *maskp = mask; + *shiftp = shift; + + return 0; +} + +u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, u32 insn, + u64 imm) +{ + u32 immlo, immhi, mask; + int shift; + + if (insn == AARCH64_BREAK_FAULT) + return AARCH64_BREAK_FAULT; + + switch (type) { + case AARCH64_INSN_IMM_ADR: + shift = 0; + immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT; + imm >>= ADR_IMM_HILOSPLIT; + immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT; + imm = immlo | immhi; + mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) | + (ADR_IMM_HIMASK << ADR_IMM_HISHIFT)); + break; + default: + if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) { + log_error("upatch: unknown immediate encoding %d\n", + type); + return AARCH64_BREAK_FAULT; + } + } + + /* Update the immediate field. */ + insn &= ~(mask << shift); + insn |= (imm & mask) << shift; + + return insn; +} + +u64 extract_insn_imm(s64 sval, int len, int lsb) +{ + u64 imm, imm_mask; + + imm = sval >> lsb; + imm_mask = (BIT(lsb + len) - 1) >> lsb; + imm = imm & imm_mask; + + log_debug("upatch: extract imm, X=0x%lx, X[%d:%d]=0x%lx \n", sval, + (len + lsb - 1), lsb, imm); + return imm; +} + +u32 insert_insn_imm(enum aarch64_insn_imm_type imm_type, void *place, u64 imm) +{ + u32 insn, new_insn; + + insn = le32_to_cpu(*(__le32 *)place); + new_insn = aarch64_insn_encode_immediate(imm_type, insn, imm); + + log_debug( + "upatch: insert imm, P=0x%lx, insn=0x%x, imm_type=%d, imm=0x%lx, " + "new_insn=0x%x \n", + (u64)place, insn, imm_type, imm, new_insn); + return new_insn; +} diff --git a/upatch/upatch-manage/arch/aarch64/insn.h b/upatch/upatch-manage/arch/aarch64/insn.h new file mode 100644 index 0000000..bfa0539 --- /dev/null +++ b/upatch/upatch-manage/arch/aarch64/insn.h @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 Huawei Ltd. + * Author: Jiang Liu + * + * Copyright (C) 2014-2016 Zi Shen Lim + */ + +#ifndef _ARCH_AARCH64_INSN_H +#define _ARCH_AARCH64_INSN_H + +#include +#include + +#include "upatch-relocation.h" + +enum aarch64_insn_imm_type { + AARCH64_INSN_IMM_ADR, + AARCH64_INSN_IMM_26, + AARCH64_INSN_IMM_19, + AARCH64_INSN_IMM_16, + AARCH64_INSN_IMM_14, + AARCH64_INSN_IMM_12, + AARCH64_INSN_IMM_9, + AARCH64_INSN_IMM_7, + AARCH64_INSN_IMM_6, + AARCH64_INSN_IMM_S, + AARCH64_INSN_IMM_R, + AARCH64_INSN_IMM_N, + AARCH64_INSN_IMM_MAX +}; + +#define SZ_2M 0x00200000 +#define ADR_IMM_HILOSPLIT 2 +#define ADR_IMM_SIZE SZ_2M +#define ADR_IMM_LOMASK ((1 << ADR_IMM_HILOSPLIT) - 1) +#define ADR_IMM_HIMASK ((ADR_IMM_SIZE >> ADR_IMM_HILOSPLIT) - 1) +#define ADR_IMM_LOSHIFT 29 +#define ADR_IMM_HISHIFT 5 + +#define FAULT_BRK_IMM 0x100 + +/* + * BRK instruction encoding + * The #imm16 value should be placed at bits[20:5] within BRK ins + */ +#define AARCH64_BREAK_MON 0xd4200000 + +/* + * BRK instruction for provoking a fault on purpose + * Unlike kgdb, #imm16 value with unallocated handler is used for faulting. + */ +#define AARCH64_BREAK_FAULT (AARCH64_BREAK_MON | (FAULT_BRK_IMM << 5)) + +#if BYTE_ORDER == LITTLE_ENDIAN +#define le32_to_cpu(val) (val) +#define cpu_to_le32(val) (val) +#endif +#if BYTE_ORDER == BIG_ENDIAN +#define le32_to_cpu(val) bswap_32(val) +#define cpu_to_le32(val) bswap_32(val) +#endif + +u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, u32 insn, + u64 imm); + +u64 extract_insn_imm(s64, int, int); + +u32 insert_insn_imm(enum aarch64_insn_imm_type, void *, u64); + +#endif /* _ARCH_AARCH64_INSN_H */ diff --git a/upatch/upatch-manage/kmod.h b/upatch/upatch-manage/arch/aarch64/process.h old mode 100755 new mode 100644 similarity index 30% rename from upatch/upatch-manage/kmod.h rename to upatch/upatch-manage/arch/aarch64/process.h index e4fe99b..ba1bc54 --- a/upatch/upatch-manage/kmod.h +++ b/upatch/upatch-manage/arch/aarch64/process.h @@ -3,18 +3,15 @@ * Copyright (C) 2022 HUAWEI, Inc. * * Authors: - * Longjun Luo + * Zongwu Li * */ -#ifndef _UPATCH_KMOD_H -#define _UPATCH_KMOD_H +#ifndef __PROCESS__ +#define __PROCESS__ -#include -#include +#ifndef MAX_DISTANCE +#define MAX_DISTANCE 0x8000000 +#endif -#include "upatch-ioctl.h" - -extern void **sys_call_table_p; - -#endif /* _UPATCH_KMOD_H */ +#endif \ No newline at end of file diff --git a/upatch/upatch-manage/arch/aarch64/ptrace.c b/upatch/upatch-manage/arch/aarch64/ptrace.c new file mode 100644 index 0000000..1445a6b --- /dev/null +++ b/upatch/upatch-manage/arch/aarch64/ptrace.c @@ -0,0 +1,160 @@ +#include +#include +#include + +#include "insn.h" +#include "upatch-ptrace.h" + +#define ORIGIN_INSN_LEN 4 + +int upatch_arch_syscall_remote(struct upatch_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; + + log_debug("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 = upatch_execute_remote(pctx, syscall, sizeof(syscall), ®s); + if (ret == 0) + *res = regs.regs[0]; + + return ret; +} + +int upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, + const unsigned char *code, size_t codelen, + struct user_regs_struct *pregs, + int (*func)(struct upatch_ptrace_ctx *pctx, + const void *data), + const 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; + struct upatch_process *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) { + log_error("can't get regs - %d\n", pctx->pid); + return -1; + } + ret = upatch_process_mem_read(proc, libc_base, + (unsigned long *)orig_code, codelen); + if (ret < 0) { + log_error("can't peek original code - %d\n", pctx->pid); + return -1; + } + ret = upatch_process_mem_write(proc, (unsigned long *)code, libc_base, + codelen); + if (ret < 0) { + log_error("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) { + log_error("can't set regs - %d\n", pctx->pid); + goto poke_back; + } + + ret = func(pctx, data); + if (ret < 0) { + log_error("failed call to func\n"); + goto poke_back; + } + + ret = ptrace(PTRACE_GETREGSET, pctx->pid, (void *)NT_PRSTATUS, + (void *)®s_iov); + if (ret < 0) { + log_error("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) { + log_error("can't restore regs - %d\n", pctx->pid); + goto poke_back; + } + + *pregs = regs; + +poke_back: + upatch_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) +{ +#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 +} + +size_t get_origin_insn_len() +{ + return ORIGIN_INSN_LEN; +} + +unsigned long get_new_insn(struct object_file *obj, unsigned long old_addr, + unsigned long new_addr) +{ + unsigned char b_insn[] = { 0x00, 0x00, 0x00, 0x00 }; /* ins: b IMM */ + + *(unsigned int *)(b_insn) = (unsigned int)(new_addr - old_addr) / 4; + b_insn[3] &= 0x3; + b_insn[3] |= 0x14; + + return *(unsigned int *)b_insn; +} diff --git a/upatch/upatch-manage/arch/aarch64/relocation.c b/upatch/upatch-manage/arch/aarch64/relocation.c new file mode 100644 index 0000000..a991b2f --- /dev/null +++ b/upatch/upatch-manage/arch/aarch64/relocation.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * renoseven + * Zongwu Li + * + */ + +#include + +#include "insn.h" +#include "upatch-relocation.h" +#include "upatch-resolve.h" + +#define TCB_SIZE 2 * sizeof(void *) + +enum aarch64_reloc_op { + RELOC_OP_NONE, + RELOC_OP_ABS, + RELOC_OP_PREL, + RELOC_OP_PAGE, +}; + +static inline s64 calc_reloc(enum aarch64_reloc_op op, void *place, u64 val) +{ + s64 sval; + switch (op) { + case RELOC_OP_ABS: + // S + A + sval = val; + break; + case RELOC_OP_PREL: + // S + A - P + sval = val - (u64)place; + break; + case RELOC_OP_PAGE: + // Page(S + A) - Page(P) + sval = (val & ~0xfff) - ((u64)place & ~0xfff); + break; + default: + log_error("upatch: unknown relocation operation %d\n", op); + break; + } + + log_debug("upatch: reloc, S+A=0x%lx, P=0x%lx, X=0x%lx\n", val, + (u64)place, sval); + return sval; +} + +int apply_relocate_add(struct upatch_elf *uelf, unsigned int symindex, + unsigned int relsec) +{ + unsigned int i; + GElf_Sym *sym; + char const *sym_name; + void *loc; + void *uloc; + u64 val; + s64 result; + GElf_Shdr *shdrs = (void *)uelf->info.shdrs; + GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; + + for (i = 0; i < shdrs[relsec].sh_size / sizeof(*rel); i++) { + /* loc corresponds to P in the kernel space */ + loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + + // /* uloc corresponds P in user space */ + uloc = (void *)shdrs[shdrs[relsec].sh_info].sh_addralign + + rel[i].r_offset; + + /* sym is the ELF symbol we're referring to */ + sym = (GElf_Sym *)shdrs[symindex].sh_addr + + GELF_R_SYM(rel[i].r_info); + if (GELF_ST_TYPE(sym[i].st_info) == STT_SECTION && + sym->st_shndx < uelf->info.hdr->e_shnum) + sym_name = uelf->info.shstrtab + + shdrs[sym->st_shndx].sh_name; + else + sym_name = uelf->strtab + sym->st_name; + + /* val corresponds to (S + A) */ + val = (s64)(sym->st_value + rel[i].r_addend); + log_debug( + "upatch: reloc symbol, name=%s, k_addr=0x%lx, u_addr=0x%lx, " + "r_offset=0x%lx, st_value=0x%lx, r_addend=0x%lx \n", + sym_name, shdrs[shdrs[relsec].sh_info].sh_addr, + shdrs[shdrs[relsec].sh_info].sh_addralign, + rel[i].r_offset, sym->st_value, rel[i].r_addend); + + /* Perform the static relocation. */ + switch (GELF_R_TYPE(rel[i].r_info)) { + case R_AARCH64_NONE: + break; + /* Data relocations. */ + case R_AARCH64_ABS64: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + *(s64 *)loc = result; + break; + case R_AARCH64_ABS32: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + if (result < -(s64)BIT(31) || result >= (s64)BIT(32)) + goto overflow; + *(s32 *)loc = result; + break; + case R_AARCH64_ABS16: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + if (result < -(s64)BIT(15) || result >= (s64)BIT(16)) + goto overflow; + *(s16 *)loc = result; + break; + case R_AARCH64_PREL64: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + *(s64 *)loc = result; + break; + case R_AARCH64_PREL32: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(31) || result >= (s64)BIT(32)) + goto overflow; + *(s32 *)loc = result; + break; + case R_AARCH64_PREL16: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(15) || result >= (s64)BIT(16)) + goto overflow; + *(s16 *)loc = result; + break; + /* Immediate instruction relocations. */ + case R_AARCH64_LD_PREL_LO19: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(20) || result >= (s64)BIT(20)) + goto overflow; + result = extract_insn_imm(result, 19, 2); + result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_ADR_PREL_LO21: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(20) || result >= (s64)BIT(20)) + goto overflow; + result = extract_insn_imm(result, 21, 0); + result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_ADR_PREL_PG_HI21: + result = calc_reloc(RELOC_OP_PAGE, uloc, val); + if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) + goto overflow; + result = extract_insn_imm(result, 21, 12); + result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_ADR_PREL_PG_HI21_NC: + result = calc_reloc(RELOC_OP_PAGE, uloc, val); + result = extract_insn_imm(result, 21, 12); + result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_LDST8_ABS_LO12_NC: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 12, 0); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_LDST16_ABS_LO12_NC: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 11, 1); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_LDST32_ABS_LO12_NC: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 10, 2); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_LDST64_ABS_LO12_NC: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 9, 3); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_LDST128_ABS_LO12_NC: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 8, 4); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_TSTBR14: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(15) || result >= (s64)BIT(15)) + goto overflow; + result = extract_insn_imm(result, 14, 2); + result = insert_insn_imm(AARCH64_INSN_IMM_14, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_CONDBR19: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + result = extract_insn_imm(result, 19, 2); + result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + result = calc_reloc(RELOC_OP_PREL, uloc, val); + if (result < -(s64)BIT(27) || result >= (s64)BIT(27)) { + log_debug( + "R_AARCH64_CALL26 overflow: result = 0x%lx, uloc = " + "0x%lx, val = 0x%lx\n", + result, (unsigned long)uloc, val); + val = search_insert_plt_table(uelf, val, + (u64)&val); + log_debug( + "R_AARCH64_CALL26 overflow: plt.addr = 0x%lx\n", + val); + if (!val) + goto overflow; + result = calc_reloc(RELOC_OP_PREL, uloc, val); + } + result = extract_insn_imm(result, 26, 2); + result = insert_insn_imm(AARCH64_INSN_IMM_26, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_ADR_GOT_PAGE: + result = calc_reloc(RELOC_OP_PAGE, uloc, val); + if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) + goto overflow; + result = extract_insn_imm(result, 21, 12); + result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_LD64_GOT_LO12_NC: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + // don't check result & 7 == 0. + // sometimes, result & 7 != 0, it works fine. + result = extract_insn_imm(result, 9, 3); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_TLSLE_ADD_TPREL_HI12: + result = ALIGN(TCB_SIZE, uelf->relf->tls_align) + val; + if (result < 0 || result >= BIT(24)) + goto overflow; + result = extract_insn_imm(result, 12, 12); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: + result = ALIGN(TCB_SIZE, uelf->relf->tls_align) + val; + result = extract_insn_imm(result, 12, 0); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_TLSDESC_ADR_PAGE21: + result = calc_reloc(RELOC_OP_PAGE, uloc, val); + if (result < -(s64)BIT(32) || result >= (s64)BIT(32)) + goto overflow; + result = extract_insn_imm(result, 21, 12); + result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_TLSDESC_LD64_LO12: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + // don't check result & 7 == 0. + result = extract_insn_imm(result, 9, 3); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_TLSDESC_ADD_LO12: + result = calc_reloc(RELOC_OP_ABS, uloc, val); + result = extract_insn_imm(result, 12, 0); + result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, + result); + *(__le32 *)loc = cpu_to_le32(result); + break; + case R_AARCH64_TLSDESC_CALL: + // this is a blr instruction, don't need to modify + break; + + default: + log_error("upatch: unsupported RELA relocation: %lu\n", + GELF_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; + +overflow: + log_error("upatch: overflow in relocation type %d val %lx reloc %lx\n", + (int)GELF_R_TYPE(rel[i].r_info), val, result); + return -ENOEXEC; +} diff --git a/upatch/upatch-manage/arch/aarch64/resolve.c b/upatch/upatch-manage/arch/aarch64/resolve.c new file mode 100644 index 0000000..a3ea30f --- /dev/null +++ b/upatch/upatch-manage/arch/aarch64/resolve.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#include + +#include "log.h" +#include "upatch-ptrace.h" +#include "upatch-resolve.h" + +/* + * ldr x16, #24 + * ldr x17, #12 + * br x17 + * undefined + */ +#define AARCH64_JUMP_TABLE_JMP1 0x58000071580000d0 +#define AARCH64_JUMP_TABLE_JMP2 0xffffffffd61f0220 + +struct upatch_jmp_table_entry { + unsigned long inst[2]; + unsigned long addr[2]; +}; + +unsigned int get_jmp_table_entry() +{ + return sizeof(struct upatch_jmp_table_entry); +} + +static unsigned long setup_jmp_table(struct upatch_elf *uelf, + unsigned long jmp_addr, + unsigned long origin_addr) +{ + struct upatch_jmp_table_entry *table = + uelf->core_layout.kbase + uelf->jmp_offs; + unsigned int index = uelf->jmp_cur_entry; + if (index >= uelf->jmp_max_entry) { + log_error("jmp table overflow \n"); + return 0; + } + + table[index].inst[0] = AARCH64_JUMP_TABLE_JMP1; + table[index].inst[1] = AARCH64_JUMP_TABLE_JMP2; + table[index].addr[0] = jmp_addr; + table[index].addr[1] = origin_addr; + uelf->jmp_cur_entry++; + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); +} + +static unsigned long setup_got_table(struct upatch_elf *uelf, + unsigned long jmp_addr, + unsigned long tls_addr) +{ + struct upatch_jmp_table_entry *table = + uelf->core_layout.kbase + uelf->jmp_offs; + unsigned int index = uelf->jmp_cur_entry; + + if (index >= uelf->jmp_max_entry) { + log_error("got table overflow \n"); + return 0; + } + + table[index].inst[0] = jmp_addr; + table[index].inst[1] = tls_addr; + table[index].addr[0] = 0xffffffff; + table[index].addr[1] = 0xffffffff; + uelf->jmp_cur_entry++; + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); +} + +unsigned long insert_plt_table(struct upatch_elf *uelf, struct object_file *obj, + unsigned long r_type, unsigned long addr) +{ + unsigned long jmp_addr = 0xffffffff; + unsigned long tls_addr = 0xffffffff; + unsigned long elf_addr = 0; + + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, + sizeof(jmp_addr))) { + log_error("copy address failed \n"); + goto out; + } + + if (r_type == R_AARCH64_TLSDESC && + upatch_process_mem_read(obj->proc, addr + sizeof(unsigned long), + &tls_addr, sizeof(tls_addr))) { + log_error("copy address failed \n"); + goto out; + } + + if (r_type == R_AARCH64_TLSDESC) + elf_addr = setup_got_table(uelf, jmp_addr, tls_addr); + else + elf_addr = setup_jmp_table(uelf, jmp_addr, (unsigned long)addr); + + log_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx \n", elf_addr, + jmp_addr, tls_addr); + +out: + return elf_addr; +} + +unsigned long insert_got_table(struct upatch_elf *uelf, struct object_file *obj, + unsigned long r_type, unsigned long addr) +{ + unsigned long jmp_addr = 0xffffffff; + unsigned long tls_addr = 0xffffffff; + unsigned long elf_addr = 0; + + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, + sizeof(jmp_addr))) { + log_error("copy address failed \n"); + goto out; + } + + if (r_type == R_AARCH64_TLSDESC && + upatch_process_mem_read(obj->proc, addr + sizeof(unsigned long), + &tls_addr, sizeof(tls_addr))) { + log_error("copy address failed \n"); + goto out; + } + + elf_addr = setup_got_table(uelf, jmp_addr, tls_addr); + + log_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx \n", elf_addr, + jmp_addr, tls_addr); + +out: + return elf_addr; +} + +unsigned long search_insert_plt_table(struct upatch_elf *uelf, + unsigned long jmp_addr, + unsigned long origin_addr) +{ + struct upatch_jmp_table_entry *table = + uelf->core_layout.kbase + uelf->jmp_offs; + unsigned int i = 0; + + for (i = 0; i < uelf->jmp_cur_entry; ++i) { + if (table[i].addr[0] != jmp_addr) + continue; + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + i * sizeof(struct upatch_jmp_table_entry)); + } + + return setup_jmp_table(uelf, jmp_addr, origin_addr); +} \ No newline at end of file diff --git a/upatch/upatch-manage/arch/arm64/insn.c b/upatch/upatch-manage/arch/arm64/insn.c deleted file mode 100644 index 54bcd7c..0000000 --- a/upatch/upatch-manage/arch/arm64/insn.c +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2013 Huawei Ltd. - * Author: Jiang Liu - * - * Copyright (C) 2014-2016 Zi Shen Lim - */ - -#ifdef __aarch64__ - -#include "arch/arm64/insn.h" - -#include - -#define ADR_IMM_HILOSPLIT 2 -#define ADR_IMM_SIZE SZ_2M -#define ADR_IMM_LOMASK ((1 << ADR_IMM_HILOSPLIT) - 1) -#define ADR_IMM_HIMASK ((ADR_IMM_SIZE >> ADR_IMM_HILOSPLIT) - 1) -#define ADR_IMM_LOSHIFT 29 -#define ADR_IMM_HISHIFT 5 - -static int aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type type, u32 *maskp, int *shiftp) -{ - u32 mask; - int shift; - - switch (type) { - case AARCH64_INSN_IMM_26: - mask = BIT(26) - 1; - shift = 0; - break; - case AARCH64_INSN_IMM_19: - mask = BIT(19) - 1; - shift = 5; - break; - case AARCH64_INSN_IMM_16: - mask = BIT(16) - 1; - shift = 5; - break; - case AARCH64_INSN_IMM_14: - mask = BIT(14) - 1; - shift = 5; - break; - case AARCH64_INSN_IMM_12: - mask = BIT(12) - 1; - shift = 10; - break; - case AARCH64_INSN_IMM_9: - mask = BIT(9) - 1; - shift = 12; - break; - case AARCH64_INSN_IMM_7: - mask = BIT(7) - 1; - shift = 15; - break; - case AARCH64_INSN_IMM_6: - case AARCH64_INSN_IMM_S: - mask = BIT(6) - 1; - shift = 10; - break; - case AARCH64_INSN_IMM_R: - mask = BIT(6) - 1; - shift = 16; - break; - case AARCH64_INSN_IMM_N: - mask = 1; - shift = 22; - break; - default: - return -EINVAL; - } - - *maskp = mask; - *shiftp = shift; - - return 0; -} - -u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, u32 insn, u64 imm) -{ - u32 immlo, immhi, mask; - int shift; - - if (insn == AARCH64_BREAK_FAULT) - return AARCH64_BREAK_FAULT; - - switch (type) { - case AARCH64_INSN_IMM_ADR: - shift = 0; - immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT; - imm >>= ADR_IMM_HILOSPLIT; - immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT; - imm = immlo | immhi; - mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) | - (ADR_IMM_HIMASK << ADR_IMM_HISHIFT)); - break; - default: - if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) { - pr_err("upatch: unknown immediate encoding %d\n", - type); - return AARCH64_BREAK_FAULT; - } - } - - /* Update the immediate field. */ - insn &= ~(mask << shift); - insn |= (imm & mask) << shift; - - return insn; -} - -#endif /* __aarch64__ */ diff --git a/upatch/upatch-manage/arch/arm64/insn.h b/upatch/upatch-manage/arch/arm64/insn.h deleted file mode 100644 index 6e8c165..0000000 --- a/upatch/upatch-manage/arch/arm64/insn.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2013 Huawei Ltd. - * Author: Jiang Liu - * - * Copyright (C) 2014-2016 Zi Shen Lim - */ - -#ifndef _ARCH_AARCH64_INSN_H -#define _ARCH_AARCH64_INSN_H - -#include - -u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, u32 insn, u64 imm); - -#endif /* _ARCH_AARCH64_INSN_H */ diff --git a/upatch/upatch-manage/arch/arm64/patch-load.c b/upatch/upatch-manage/arch/arm64/patch-load.c deleted file mode 100644 index 3eaffae..0000000 --- a/upatch/upatch-manage/arch/arm64/patch-load.c +++ /dev/null @@ -1,449 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * renoseven - * - */ - -#ifdef __aarch64__ - -#include "arch/patch-load.h" -#include "arch/arm64/insn.h" - -/* - * ldr x16, #24 - * ldr x17, #12 - * br x17 - * undefined - */ -#define AARCH64_JUMP_TABLE_JMP1 0x58000071580000d0 -#define AARCH64_JUMP_TABLE_JMP2 0x00000000d61f0220 - -#ifndef R_AARCH64_ADR_GOT_PAGE -#define R_AARCH64_ADR_GOT_PAGE 311 -#endif - -#ifndef R_AARCH64_LD64_GOT_LO12_NC -#define R_AARCH64_LD64_GOT_LO12_NC 312 -#endif - -#ifndef R_AARCH64_TLSLE_ADD_TPREL_HI12 -#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 -#endif - -#ifndef R_AARCH64_TLSLE_ADD_TPREL_LO12_NC -#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 -#endif - -#ifndef R_AARCH64_TLSDESC_ADR_PAGE21 -#define R_AARCH64_TLSDESC_ADR_PAGE21 562 -#endif - -#ifndef R_AARCH64_TLSDESC_LD64_LO12 -#define R_AARCH64_TLSDESC_LD64_LO12 563 -#endif - -#ifndef R_AARCH64_TLSDESC_ADD_LO12 -#define R_AARCH64_TLSDESC_ADD_LO12 564 -#endif - -#ifndef R_AARCH64_TLSDESC_CALL -#define R_AARCH64_TLSDESC_CALL 569 -#endif - -#ifndef R_AARCH64_TLSDESC -#define R_AARCH64_TLSDESC 1031 -#endif - -#define TCB_SIZE 2 * sizeof(void *) -#define CHECK_MAGIC 7 - -enum aarch64_reloc_op { - RELOC_OP_NONE, - RELOC_OP_ABS, - RELOC_OP_PREL, - RELOC_OP_PAGE, -}; - -void setup_parameters(struct pt_regs *regs, unsigned long para_a, - unsigned long para_b, unsigned long para_c) -{ - regs->regs[0] = para_a; - regs->regs[1] = para_b; - regs->regs[2] = para_c; -} - -static unsigned long setup_jmp_table(struct upatch_load_info *info, unsigned long jmp_addr, unsigned long origin_addr) -{ - struct upatch_jmp_table_entry *table = info->mod->core_layout.kbase + info->jmp_offs; - unsigned int index = info->jmp_cur_entry; - if (index >= info->jmp_max_entry) { - pr_err("jmp table overflow \n"); - return 0; - } - - table[index].inst[0] = AARCH64_JUMP_TABLE_JMP1; - table[index].inst[1] = AARCH64_JUMP_TABLE_JMP2; - table[index].addr[0] = jmp_addr; - table[index].addr[1] = origin_addr; - info->jmp_cur_entry ++; - return (unsigned long)(info->mod->core_layout.base + info->jmp_offs + - index * sizeof(struct upatch_jmp_table_entry)); -} - -static unsigned long setup_got_table(struct upatch_load_info *info, unsigned long jmp_addr, unsigned long tls_addr) -{ - struct upatch_jmp_table_entry *table = - info->mod->core_layout.kbase + info->jmp_offs; - unsigned int index = info->jmp_cur_entry; - - if (index >= info->jmp_max_entry) { - pr_err("got table overflow \n"); - return 0; - } - - table[index].inst[0] = jmp_addr; - table[index].inst[1] = tls_addr; - table[index].addr[0] = 0xffffffff; - table[index].addr[1] = 0xffffffff; - info->jmp_cur_entry ++; - return (unsigned long)(info->mod->core_layout.base + info->jmp_offs - + index * sizeof(struct upatch_jmp_table_entry)); -} - -unsigned long insert_plt_table(struct upatch_load_info *info, unsigned long r_type, void __user *addr) -{ - unsigned long jmp_addr; - unsigned long tls_addr = 0xffffffff; - unsigned long elf_addr = 0; - - if (copy_from_user((void *)&jmp_addr, addr, sizeof(unsigned long))) { - pr_err("copy address failed \n"); - goto out; - } - - if (r_type == R_AARCH64_TLSDESC && - copy_from_user((void *)&tls_addr, addr + sizeof(unsigned long), sizeof(unsigned long))) { - pr_err("copy address failed \n"); - goto out; - } - - if (r_type == R_AARCH64_TLSDESC) - elf_addr = setup_got_table(info, jmp_addr, tls_addr); - else - elf_addr = setup_jmp_table(info, jmp_addr, (unsigned long)addr); - - pr_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx \n", - elf_addr, jmp_addr, tls_addr); - -out: - return elf_addr; -} - - -unsigned long insert_got_table(struct upatch_load_info *info, unsigned long r_type, void __user *addr) -{ - unsigned long jmp_addr; - unsigned long tls_addr = 0xffffffff; - unsigned long elf_addr = 0; - - if (copy_from_user((void *)&jmp_addr, addr, sizeof(unsigned long))) { - pr_err("copy address failed \n"); - goto out; - } - - if (r_type == R_AARCH64_TLSDESC && - copy_from_user((void *)&tls_addr, addr + sizeof(unsigned long), sizeof(unsigned long))) { - pr_err("copy address failed \n"); - goto out; - } - - elf_addr = setup_got_table(info, jmp_addr, tls_addr); - - pr_debug("0x%lx: jmp_addr=0x%lx, tls_addr=0x%lx \n", - elf_addr, jmp_addr, tls_addr); - -out: - return elf_addr; -} - -static inline s64 calc_reloc(enum aarch64_reloc_op op, void *place, u64 val) -{ - s64 sval; - switch (op) { - case RELOC_OP_ABS: - // S + A - sval = val; - break; - case RELOC_OP_PREL: - // S + A - P - sval = val - (u64)place; - break; - case RELOC_OP_PAGE: - // Page(S + A) - Page(P) - sval = (val & ~0xfff) - ((u64)place & ~0xfff); - break; - default: - pr_err("upatch: unknown relocation operation %d\n", op); - break; - } - - pr_debug("upatch: reloc, S+A=0x%llx, P=0x%llx, X=0x%llx", val, (u64)place, sval); - return sval; -} - -static inline u64 extract_insn_imm(s64 sval, int len, int lsb) -{ - u64 imm, imm_mask; - - imm = sval >> lsb; - imm_mask = (BIT(lsb + len) - 1) >> lsb; - imm = imm & imm_mask; - - pr_debug("upatch: extract imm, X=0x%llx, X[%d:%d]=0x%llx", sval, (len + lsb - 1), lsb, imm); - return imm; -} - -static inline u32 insert_insn_imm(enum aarch64_insn_imm_type imm_type, void *place, u64 imm) -{ - u32 insn, new_insn; - - insn = le32_to_cpu(*(__le32 *)place); - new_insn = aarch64_insn_encode_immediate(imm_type, insn, imm); - - pr_debug("upatch: insert imm, P=0x%llx, insn=0x%x, imm_type=%d, imm=0x%llx, new_insn=0x%x", - (u64)place, insn, imm_type, imm, new_insn); - return new_insn; -} - -int apply_relocate_add(struct upatch_load_info *info, Elf64_Shdr *sechdrs, - const char *strtab, unsigned int symindex, - unsigned int relsec, struct upatch_module *me) -{ - unsigned int i; - Elf64_Sym *sym; - char const *sym_name; - void *loc; - void *uloc; - u64 val; - s64 result; - Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; - - for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { - /* loc corresponds to P in the kernel space */ - loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr - + rel[i].r_offset; - - /* uloc corresponds P in user space */ - uloc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addralign - + rel[i].r_offset; - - /* sym is the ELF symbol we're referring to */ - sym = (Elf64_Sym *)sechdrs[symindex].sh_addr - + ELF64_R_SYM(rel[i].r_info); - sym_name = strtab + sym->st_name; - - /* val corresponds to (S + A) */ - val = (s64)(sym->st_value + rel[i].r_addend); - pr_debug("upatch: reloc symbol, name=%s, k_addr=0x%llx, u_addr=0x%llx, r_offset=0x%llx, st_value=0x%llx, r_addend=0x%llx", - sym_name, - (unsigned long long)sechdrs[sechdrs[relsec].sh_info].sh_addr, - (unsigned long long)sechdrs[sechdrs[relsec].sh_info].sh_addralign, - rel[i].r_offset, sym->st_value, rel[i].r_addend); - - /* Perform the static relocation. */ - switch (ELF64_R_TYPE(rel[i].r_info)) { - /* Null relocations. */ - case R_ARM_NONE: - case R_AARCH64_NONE: - break; - /* Data relocations. */ - case R_AARCH64_ABS64: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - *(s64 *)loc = result; - break; - case R_AARCH64_ABS32: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - if (result < S32_MIN || result > S32_MAX) { - goto overflow; - } - *(s32 *)loc = result; - break; - case R_AARCH64_ABS16: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - if (result < S16_MIN || result > S16_MAX) { - goto overflow; - } - *(s16 *)loc = result; - break; - case R_AARCH64_PREL64: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - *(s64 *)loc = result; - break; - case R_AARCH64_PREL32: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - if (result < S32_MIN || result > S32_MAX) { - goto overflow; - } - *(s32 *)loc = result; - break; - case R_AARCH64_PREL16: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - if (result < S32_MIN || result > S32_MAX) { - goto overflow; - } - *(s16 *)loc = result; - break; - /* Immediate instruction relocations. */ - case R_AARCH64_LD_PREL_LO19: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - // TODO: ovf check -2^20 < X < 2^20 - result = extract_insn_imm(result, 19, 2); - result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_ADR_PREL_LO21: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - // TODO: ovf check -2^20 < X < 2^20 - result = extract_insn_imm(result, 21, 0); - result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_ADR_PREL_PG_HI21: - result = calc_reloc(RELOC_OP_PAGE, uloc, val); - if (result < S32_MIN || result > S32_MAX) { - goto overflow; - } - result = extract_insn_imm(result, 21, 12); - result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_ADR_PREL_PG_HI21_NC: - result = calc_reloc(RELOC_OP_PAGE, uloc, val); - result = extract_insn_imm(result, 21, 12); - result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_ADD_ABS_LO12_NC: - case R_AARCH64_LDST8_ABS_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 12, 0); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_LDST16_ABS_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 11, 1); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_LDST32_ABS_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 10, 2); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_LDST64_ABS_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 9, 3); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_LDST128_ABS_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 8, 4); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TSTBR14: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - // TODO: ovf check -2^15 < X < 2^15 - result = extract_insn_imm(result, 14, 2); - result = insert_insn_imm(AARCH64_INSN_IMM_14, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_CONDBR19: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - result = extract_insn_imm(result, 19, 2); - result = insert_insn_imm(AARCH64_INSN_IMM_19, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_JUMP26: - case R_AARCH64_CALL26: - result = calc_reloc(RELOC_OP_PREL, uloc, val); - // TODO: ovf check -2^27 < X < 2^27 - result = extract_insn_imm(result, 26, 2); - result = insert_insn_imm(AARCH64_INSN_IMM_26, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_ADR_GOT_PAGE: - result = calc_reloc(RELOC_OP_PAGE, uloc, val); - // TODO: ovf check -2^32 < X < 2^32 - result = extract_insn_imm(result, 21, 12); - result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_LD64_GOT_LO12_NC: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - if ((result & CHECK_MAGIC) != 0) - goto overflow; - result = extract_insn_imm(result, 9, 3); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSLE_ADD_TPREL_HI12: - result = ALIGN(TCB_SIZE, info->running_elf.tls_align) + val; - if (result < 0 || result >= BIT(24)) - goto overflow; - result = extract_insn_imm(result, 12, 12); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: - result = ALIGN(TCB_SIZE, info->running_elf.tls_align) + val; - result = extract_insn_imm(result, 12, 0); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSDESC_ADR_PAGE21: - result = calc_reloc(RELOC_OP_PAGE, uloc, val); - // TODO: ovf check -2^32 < X < 2^32 - result = extract_insn_imm(result, 21, 12); - result = insert_insn_imm(AARCH64_INSN_IMM_ADR, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSDESC_LD64_LO12: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - if ((result & CHECK_MAGIC) != 0) - goto overflow; - result = extract_insn_imm(result, 9, 3); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSDESC_ADD_LO12: - result = calc_reloc(RELOC_OP_ABS, uloc, val); - result = extract_insn_imm(result, 12, 0); - result = insert_insn_imm(AARCH64_INSN_IMM_12, loc, result); - *(__le32 *)loc = cpu_to_le32(result); - break; - case R_AARCH64_TLSDESC_CALL: - // this is a blr instruction, don't need to modify - break; - - default: - pr_err("upatch: unsupported RELA relocation: %llu\n", - ELF64_R_TYPE(rel[i].r_info)); - return -ENOEXEC; - } - } - return 0; - -overflow: - pr_err("upatch: overflow in relocation type %d val %Lx reloc %llx\n", - (int)ELF64_R_TYPE(rel[i].r_info), val, result); - return -ENOEXEC; -} - -#endif /* __aarch64__ */ diff --git a/upatch/upatch-manage/arch/arm64/patch-syscall-impl.S b/upatch/upatch-manage/arch/arm64/patch-syscall-impl.S deleted file mode 100755 index c49ee8b..0000000 --- a/upatch/upatch-manage/arch/arm64/patch-syscall-impl.S +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * renoseven - * - */ - -#include -#include - -.text - -SYM_CODE_START(__execve_syscall) - mov x8, __NR_execve - svc #0 - mov x8, __NR_exit - svc #0 -SYM_CODE_END(__execve_syscall) - -SYM_CODE_START(__exit_syscall) - mov x8, __NR_exit - svc #0 -SYM_CODE_END(__exit_syscall) diff --git a/upatch/upatch-manage/arch/arm64/patch-syscall-register.c b/upatch/upatch-manage/arch/arm64/patch-syscall-register.c deleted file mode 100644 index d7e8aa5..0000000 --- a/upatch/upatch-manage/arch/arm64/patch-syscall-register.c +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * renoseven - * - */ - -#ifdef __aarch64__ - -#include "arch/patch-syscall.h" - -void set_execve_syscall_registers(struct pt_regs *regs, const void __user *code, - const char __user *pathname, const char __user *const __user *argv, const char __user *const __user *envp) -{ - regs->pc = (unsigned long)code; - regs->regs[0] = (unsigned long)pathname; - regs->regs[1] = (unsigned long)argv; - regs->regs[2] = (unsigned long)envp; -} - -void set_exit_syscall_registers(struct pt_regs *regs, const void __user *code, int exit_code) -{ - regs->pc = (unsigned long)code; - regs->regs[0] = exit_code; -} - -#endif /* __aarch64__ */ diff --git a/upatch/upatch-manage/arch/patch-load.h b/upatch/upatch-manage/arch/patch-load.h deleted file mode 100644 index 4ba8a70..0000000 --- a/upatch/upatch-manage/arch/patch-load.h +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * renoseven - * - */ - -#ifndef _ARCH_PATCH_LOAD_H -#define _ARCH_PATCH_LOAD_H - -#include - -#include "patch.h" -#include "common.h" - -struct upatch_load_info; -struct upatch_module; - -/* jmp table, solve limit for the jmp instruction, Used for both PLT/GOT */ -#if defined(__x86_64__) -struct upatch_jmp_table_entry { - unsigned long inst; - unsigned long addr; -}; -#elif defined(__aarch64__) -struct upatch_jmp_table_entry { - unsigned long inst[2]; - unsigned long addr[2]; -}; -#endif - -unsigned long insert_plt_table(struct upatch_load_info *info, unsigned long r_type, void __user *addr); -unsigned long insert_got_table(struct upatch_load_info *info, unsigned long r_type, void __user *addr); - -int apply_relocate_add(struct upatch_load_info *info, Elf64_Shdr *sechdrs, - const char *strtab, unsigned int symindex, - unsigned int relsec, struct upatch_module *me); - -void setup_parameters(struct pt_regs *regs, unsigned long para_a, - unsigned long para_b, unsigned long para_c); - -#endif /* _ARCH_PATCH_LOAD_H */ diff --git a/upatch/upatch-manage/arch/patch-syscall.h b/upatch/upatch-manage/arch/patch-syscall.h deleted file mode 100644 index b46ad39..0000000 --- a/upatch/upatch-manage/arch/patch-syscall.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * renoseven - * - */ - -#ifndef _ARCH_PATCH_SYSCALL_H -#define _ARCH_PATCH_SYSCALL_H - -#include - -asmlinkage int __execve_syscall(struct pt_regs *regs, const char __user *pathname, - const char __user *const __user *argv, const char __user *const __user *envp); -asmlinkage int __exit_syscall(struct pt_regs *regs, int exit_code); - -void set_execve_syscall_registers(struct pt_regs *regs, const void __user *code, const char __user *pathname, - const char __user *const __user *argv, const char __user *const __user *envp); -void set_exit_syscall_registers(struct pt_regs *regs, const void __user *code, int exit_code); - -#endif /* _ARCH_PATCH_SYSCALL_H */ diff --git a/upatch/upatch-manage/arch/x86/patch-load.c b/upatch/upatch-manage/arch/x86/patch-load.c deleted file mode 100644 index 906dcec..0000000 --- a/upatch/upatch-manage/arch/x86/patch-load.c +++ /dev/null @@ -1,242 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#ifdef __x86_64__ - -#include "arch/patch-load.h" - -#ifndef R_X86_64_DTPMOD64 -#define R_X86_64_DTPMOD64 16 -#endif - -#ifndef R_X86_64_TLSGD -#define R_X86_64_TLSGD 19 -#endif - -#ifndef R_X86_64_GOTTPOFF -#define R_X86_64_GOTTPOFF 22 -#endif - -#ifndef R_X86_64_TPOFF32 -#define R_X86_64_TPOFF32 23 -#endif - -#ifndef R_X86_64_GOTPCRELX -#define R_X86_64_GOTPCRELX 41 -#endif - -#ifndef R_X86_64_REX_GOTPCRELX -#define R_X86_64_REX_GOTPCRELX 42 -#endif - -#define X86_64_JUMP_TABLE_JMP 0x90900000000225ff /* jmp [rip+2]; nop; nop */ - -void setup_parameters(struct pt_regs *regs, unsigned long para_a, - unsigned long para_b, unsigned long para_c) -{ - regs->di = para_a; - regs->si = para_b; - regs->dx = para_c; -} - -static unsigned long setup_jmp_table(struct upatch_load_info *info, unsigned long jmp_addr) -{ - struct upatch_jmp_table_entry *table = info->mod->core_layout.kbase + info->jmp_offs; - unsigned int index = info->jmp_cur_entry; - if (index >= info->jmp_max_entry) { - pr_err("jmp table overflow \n"); - return 0; - } - - table[index].inst = X86_64_JUMP_TABLE_JMP; - table[index].addr = jmp_addr; - info->jmp_cur_entry ++; - return (unsigned long)(info->mod->core_layout.base + info->jmp_offs + - index * sizeof(struct upatch_jmp_table_entry)); -} - -/* - * Jmp tabale records address and used call instruction to execute it. - * So, we need 'Inst' and 'addr' - * GOT only need record address and resolve it by [got_addr]. - * To simplify design, use same table for both jmp table and GOT. - */ -static unsigned long setup_got_table(struct upatch_load_info *info, unsigned long jmp_addr, unsigned long tls_addr) -{ - struct upatch_jmp_table_entry *table = - info->mod->core_layout.kbase + info->jmp_offs; - unsigned int index = info->jmp_cur_entry; - if (index >= info->jmp_max_entry) { - pr_err("got table overflow \n"); - return 0; - } - - table[index].inst = jmp_addr; - table[index].addr = tls_addr; - info->jmp_cur_entry ++; - return (unsigned long)(info->mod->core_layout.base + info->jmp_offs - + index * sizeof(struct upatch_jmp_table_entry)); -} - -unsigned long insert_plt_table(struct upatch_load_info *info, unsigned long r_type, void __user *addr) -{ - unsigned long jmp_addr; - unsigned long elf_addr = 0; - - if (copy_from_user((void *)&jmp_addr, addr, sizeof(unsigned long))) { - pr_err("copy address failed \n"); - goto out; - } - - elf_addr = setup_jmp_table(info, jmp_addr); - - pr_debug("0x%lx: jmp_addr=0x%lx \n", elf_addr, jmp_addr); - -out: - return elf_addr; -} - - -unsigned long insert_got_table(struct upatch_load_info *info, unsigned long r_type, void __user *addr) -{ - unsigned long jmp_addr; - unsigned long tls_addr = 0xffffffff; - unsigned long elf_addr = 0; - - if (copy_from_user((void *)&jmp_addr, addr, sizeof(unsigned long))) { - pr_err("copy address failed \n"); - goto out; - } - - /* - * R_X86_64_TLSGD: allocate two contiguous entries in the GOT to hold a tls_index structure - * tls_index has two unsigned long, the first one is R_X86_64_DTPMOD64. - */ - if (r_type == R_X86_64_DTPMOD64 && - copy_from_user((void *)&tls_addr, addr + sizeof(unsigned long), sizeof(unsigned long))) { - pr_err("copy address failed \n"); - goto out; - } - - elf_addr = setup_got_table(info, jmp_addr, tls_addr); - - pr_debug("0x%lx: jmp_addr=0x%lx \n", elf_addr, jmp_addr); - -out: - return elf_addr; -} - -int apply_relocate_add(struct upatch_load_info *info, Elf64_Shdr *sechdrs, - const char *strtab, unsigned int symindex, - unsigned int relsec, struct upatch_module *me) -{ - unsigned int i; - Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; - Elf64_Sym *sym; - void *loc, *real_loc; - u64 val; - const char *name; - Elf64_Xword tls_size; - - pr_debug("Applying relocate section %u to %u\n", - relsec, sechdrs[relsec].sh_info); - - for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { - /* This is where to make the change, calculate it from kernel address */ - loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr - + rel[i].r_offset; - - real_loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addralign - + rel[i].r_offset; - - /* This is the symbol it is referring to. Note that all - undefined symbols have been resolved. */ - sym = (Elf64_Sym *)sechdrs[symindex].sh_addr - + ELF64_R_SYM(rel[i].r_info); - name = strtab + sym->st_name; - - pr_debug("type %d st_value %Lx r_addend %Lx loc %Lx\n", - (int)ELF64_R_TYPE(rel[i].r_info), - sym->st_value, rel[i].r_addend, (u64)loc); - - val = sym->st_value + rel[i].r_addend; - switch (ELF64_R_TYPE(rel[i].r_info)) { - case R_X86_64_NONE: - break; - case R_X86_64_64: - if (*(u64 *)loc != 0) - goto invalid_relocation; - memcpy(loc, &val, 8); - break; - case R_X86_64_32: - if (*(u32 *)loc != 0) - goto invalid_relocation; - memcpy(loc, &val, 4); - if (val != *(u32 *)loc - && (ELF_ST_TYPE(sym->st_info) != STT_SECTION)) - goto overflow; - break; - case R_X86_64_32S: - if (*(s32 *)loc != 0) - goto invalid_relocation; - memcpy(loc, &val, 4); - if ((s64)val != *(s32 *)loc - && (ELF_ST_TYPE(sym->st_info) != STT_SECTION)) - goto overflow; - break; - case R_X86_64_TLSGD: - case R_X86_64_GOTTPOFF: - case R_X86_64_GOTPCRELX: - case R_X86_64_REX_GOTPCRELX: - if (sym->st_value == 0) - goto overflow; - /* G + GOT + A*/ - val = sym->st_value + rel[i].r_addend; - fallthrough; - case R_X86_64_PC32: - case R_X86_64_PLT32: - if (*(u32 *)loc != 0) - goto invalid_relocation; - val -= (u64)real_loc; - memcpy(loc, &val, 4); - break; - case R_X86_64_PC64: - if (*(u64 *)loc != 0) - goto invalid_relocation; - val -= (u64)real_loc; - memcpy(loc, &val, 8); - break; - case R_X86_64_TPOFF32: - tls_size = ALIGN(info->running_elf.tls_size, info->running_elf.tls_align); - // %fs + val - tls_size - if (val >= tls_size) - goto overflow; - val -= (u64)tls_size; - memcpy(loc, &val, 4); - break; - default: - pr_err("Unknown rela relocation: %llu\n", ELF64_R_TYPE(rel[i].r_info)); - return -ENOEXEC; - } - } - return 0; - -invalid_relocation: - pr_err("upatch: Skipping invalid relocation target, \ - existing value is nonzero for type %d, loc %p, name %s\n", - (int)ELF64_R_TYPE(rel[i].r_info), loc, name); - return -ENOEXEC; - -overflow: - pr_err("upatch: overflow in relocation type %d name %s\n", - (int)ELF64_R_TYPE(rel[i].r_info), name); - return -ENOEXEC; -} - -#endif /* __x86_64__ */ diff --git a/upatch/upatch-manage/arch/x86/patch-syscall-impl.S b/upatch/upatch-manage/arch/x86/patch-syscall-impl.S deleted file mode 100755 index 4a3e820..0000000 --- a/upatch/upatch-manage/arch/x86/patch-syscall-impl.S +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#include -#include - - .text - .align PAGE_SIZE - -SYM_CODE_START_NOALIGN(__execve_syscall) - .code64 - mov $0x3b, %eax - syscall - /* if execve failed, exit with its return value */ - mov %eax, %edi - mov $0x3c, %eax - syscall -SYM_CODE_END(__execve_syscall) - -/* use exit syscall to pass errno */ -SYM_CODE_START_NOALIGN(__exit_syscall) - .code64 - mov $0x3c, %eax - syscall -SYM_CODE_END(__exit_syscall) \ No newline at end of file diff --git a/upatch/upatch-manage/arch/x86/patch-syscall-register.c b/upatch/upatch-manage/arch/x86/patch-syscall-register.c deleted file mode 100644 index c8c7e29..0000000 --- a/upatch/upatch-manage/arch/x86/patch-syscall-register.c +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * renoseven - * - */ - -#ifdef __x86_64__ - -#include "arch/patch-syscall.h" - -void set_execve_syscall_registers(struct pt_regs *regs, const void __user *code, const char __user *pathname, - const char __user *const __user *argv, const char __user *const __user *envp) -{ - regs->ip = (unsigned long)code; - regs->di = (unsigned long)pathname; - regs->si = (unsigned long)argv; - regs->dx = (unsigned long)envp; -} - -void set_exit_syscall_registers(struct pt_regs *regs, const void __user *code, int exit_code) -{ - regs->ip = (unsigned long)code; - regs->di = exit_code; -} - -#endif /* __x86_64__ */ diff --git a/upatch/upatch-manage/arch/x86_64/process.h b/upatch/upatch-manage/arch/x86_64/process.h new file mode 100644 index 0000000..394f64b --- /dev/null +++ b/upatch/upatch-manage/arch/x86_64/process.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#ifndef __PROCESS__ +#define __PROCESS__ + +#ifndef MAX_DISTANCE +#define MAX_DISTANCE 0x80000000 +#endif + +#endif \ No newline at end of file diff --git a/upatch/upatch-manage/arch/x86_64/ptrace.c b/upatch/upatch-manage/arch/x86_64/ptrace.c new file mode 100644 index 0000000..95f8d6a --- /dev/null +++ b/upatch/upatch-manage/arch/x86_64/ptrace.c @@ -0,0 +1,145 @@ +#include + +#include +#include +#include + +#include "upatch-ptrace.h" + +#define ORIGIN_INSN_LEN 5 + +int upatch_arch_syscall_remote(struct upatch_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; + + memset(®s, 0, sizeof(struct user_regs_struct)); + log_debug("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 = upatch_execute_remote(pctx, syscall, sizeof(syscall), ®s); + if (ret == 0) + *res = regs.rax; + + return ret; +} + +int upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, + const unsigned char *code, size_t codelen, + struct user_regs_struct *pregs, + int (*func)(struct upatch_ptrace_ctx *pctx, + const void *data), + const void *data) +{ + struct user_regs_struct orig_regs, regs; + unsigned char orig_code[codelen]; + int ret; + struct upatch_process *proc = pctx->proc; + unsigned long libc_base = proc->libc_base; + + ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, &orig_regs); + if (ret < 0) { + log_error("can't get regs - %d\n", pctx->pid); + return -1; + } + ret = upatch_process_mem_read(proc, libc_base, + (unsigned long *)orig_code, codelen); + if (ret < 0) { + log_error("can't peek original code - %d\n", pctx->pid); + return -1; + } + ret = upatch_process_mem_write(proc, (unsigned long *)code, libc_base, + codelen); + if (ret < 0) { + log_error("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) { + log_error("can't set regs - %d\n", pctx->pid); + goto poke_back; + } + + ret = func(pctx, data); + if (ret < 0) { + log_error("failed call to func\n"); + goto poke_back; + } + + ret = ptrace(PTRACE_GETREGS, pctx->pid, NULL, ®s); + if (ret < 0) { + log_error("can't get updated regs - %d\n", pctx->pid); + goto poke_back; + } + + ret = ptrace(PTRACE_SETREGS, pctx->pid, NULL, &orig_regs); + if (ret < 0) { + log_error("can't restore regs - %d\n", pctx->pid); + goto poke_back; + } + + *pregs = regs; + +poke_back: + upatch_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) +{ +#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 +} + +size_t get_origin_insn_len() +{ + return ORIGIN_INSN_LEN; +} + +unsigned long get_new_insn(struct object_file *obj, unsigned long old_addr, + unsigned long new_addr) +{ + char jmp_insn[] = { 0xe9, 0x00, 0x00, 0x00, 0x00 }; /* jmp IMM */ + + *(unsigned int *)(jmp_insn + 1) = + (unsigned int)(new_addr - old_addr - 5); + + return *(unsigned int *)jmp_insn; +} \ No newline at end of file diff --git a/upatch/upatch-manage/arch/x86_64/relocation.c b/upatch/upatch-manage/arch/x86_64/relocation.c new file mode 100644 index 0000000..207f6bb --- /dev/null +++ b/upatch/upatch-manage/arch/x86_64/relocation.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Longjun Luo + * Zongwu Li + * + */ + +#include +#include +#include + +#include "upatch-relocation.h" + +int apply_relocate_add(struct upatch_elf *uelf, unsigned int symindex, + unsigned int relsec) +{ + unsigned int i; + GElf_Sym *sym; + void *loc, *real_loc; + u64 val; + const char *sym_name; + GElf_Xword tls_size; + GElf_Shdr *shdrs = (void *)uelf->info.shdrs; + GElf_Rela *rel = (void *)shdrs[relsec].sh_addr; + + log_debug("Applying relocate section %u to %u\n", relsec, + shdrs[relsec].sh_info); + + for (i = 0; i < shdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change, calculate it from kernel address */ + loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + + real_loc = (void *)shdrs[shdrs[relsec].sh_info].sh_addralign + + rel[i].r_offset; + + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + sym = (GElf_Sym *)shdrs[symindex].sh_addr + + GELF_R_SYM(rel[i].r_info); + + if (GELF_ST_TYPE(sym[i].st_info) == STT_SECTION && + sym->st_shndx < uelf->info.hdr->e_shnum) + sym_name = uelf->info.shstrtab + + shdrs[sym->st_shndx].sh_name; + else + sym_name = uelf->strtab + sym->st_name; + + log_debug("type %d st_value %lx r_addend %lx loc %lx\n", + (int)GELF_R_TYPE(rel[i].r_info), sym->st_value, + rel[i].r_addend, (u64)loc); + + val = sym->st_value + rel[i].r_addend; + switch (GELF_R_TYPE(rel[i].r_info)) { + case R_X86_64_NONE: + break; + case R_X86_64_64: + if (*(u64 *)loc != 0) + goto invalid_relocation; + memcpy(loc, &val, 8); + break; + case R_X86_64_32: + if (*(u32 *)loc != 0) + goto invalid_relocation; + memcpy(loc, &val, 4); + if (val != *(u32 *)loc && + (GELF_ST_TYPE(sym->st_info) != STT_SECTION)) + goto overflow; + break; + case R_X86_64_32S: + if (*(s32 *)loc != 0) + goto invalid_relocation; + memcpy(loc, &val, 4); + if ((s64)val != *(s32 *)loc && + (GELF_ST_TYPE(sym->st_info) != STT_SECTION)) + goto overflow; + break; + case R_X86_64_TLSGD: + case R_X86_64_GOTTPOFF: + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: + if (sym->st_value == 0) + goto overflow; + /* G + GOT + A*/ + val = sym->st_value + rel[i].r_addend; + case R_X86_64_PC32: + case R_X86_64_PLT32: + if (*(u32 *)loc != 0) + goto invalid_relocation; + val -= (u64)real_loc; + memcpy(loc, &val, 4); + break; + case R_X86_64_PC64: + if (*(u64 *)loc != 0) + goto invalid_relocation; + val -= (u64)real_loc; + memcpy(loc, &val, 8); + break; + case R_X86_64_TPOFF32: + tls_size = ALIGN(uelf->relf->tls_size, + uelf->relf->tls_align); + // %fs + val - tls_size + if (val >= tls_size) + goto overflow; + val -= (u64)tls_size; + memcpy(loc, &val, 4); + break; + default: + log_error("Unknown rela relocation: %lu\n", + GELF_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; + +invalid_relocation: + log_error("upatch: Skipping invalid relocation target, \ + existing value is nonzero for type %d, loc %p, name %s\n", + (int)GELF_R_TYPE(rel[i].r_info), loc, sym_name); + return -ENOEXEC; + +overflow: + log_error("upatch: overflow in relocation type %d name %s\n", + (int)GELF_R_TYPE(rel[i].r_info), sym_name); + return -ENOEXEC; +} diff --git a/upatch/upatch-manage/arch/x86_64/resolve.c b/upatch/upatch-manage/arch/x86_64/resolve.c new file mode 100644 index 0000000..9d3e539 --- /dev/null +++ b/upatch/upatch-manage/arch/x86_64/resolve.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Longjun Luo + * Zongwu Li + * + */ + +#include + +#include "upatch-ptrace.h" +#include "upatch-resolve.h" + +#define X86_64_JUMP_TABLE_JMP 0x90900000000225ff /* jmp [rip+2]; nop; nop */ + +struct upatch_jmp_table_entry { + unsigned long inst; + unsigned long addr; +}; + +unsigned int get_jmp_table_entry() +{ + return sizeof(struct upatch_jmp_table_entry); +} + +static unsigned long setup_jmp_table(struct upatch_elf *uelf, + unsigned long jmp_addr) +{ + struct upatch_jmp_table_entry *table = + uelf->core_layout.kbase + uelf->jmp_offs; + unsigned int index = uelf->jmp_cur_entry; + if (index >= uelf->jmp_max_entry) { + log_error("jmp table overflow \n"); + return 0; + } + + table[index].inst = X86_64_JUMP_TABLE_JMP; + table[index].addr = jmp_addr; + uelf->jmp_cur_entry++; + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); +} + +/* + * Jmp tabale records address and used call instruction to execute it. + * So, we need 'Inst' and 'addr' + * GOT only need record address and resolve it by [got_addr]. + * To simplify design, use same table for both jmp table and GOT. + */ +static unsigned long setup_got_table(struct upatch_elf *uelf, + unsigned long jmp_addr, + unsigned long tls_addr) +{ + struct upatch_jmp_table_entry *table = + uelf->core_layout.kbase + uelf->jmp_offs; + unsigned int index = uelf->jmp_cur_entry; + if (index >= uelf->jmp_max_entry) { + log_error("got table overflow \n"); + return 0; + } + + table[index].inst = jmp_addr; + table[index].addr = tls_addr; + uelf->jmp_cur_entry++; + return (unsigned long)(uelf->core_layout.base + uelf->jmp_offs + + index * sizeof(struct upatch_jmp_table_entry)); +} + +unsigned long insert_plt_table(struct upatch_elf *uelf, struct object_file *obj, + unsigned long r_type, unsigned long addr) +{ + unsigned long jmp_addr; + unsigned long elf_addr = 0; + + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, + sizeof(jmp_addr))) { + log_error("copy address failed \n"); + goto out; + } + + elf_addr = setup_jmp_table(uelf, jmp_addr); + + log_debug("0x%lx: jmp_addr=0x%lx \n", elf_addr, jmp_addr); + +out: + return elf_addr; +} + +unsigned long insert_got_table(struct upatch_elf *uelf, struct object_file *obj, + unsigned long r_type, unsigned long addr) +{ + unsigned long jmp_addr; + unsigned long tls_addr = 0xffffffff; + unsigned long elf_addr = 0; + + if (upatch_process_mem_read(obj->proc, addr, &jmp_addr, + sizeof(jmp_addr))) { + log_error("copy address failed \n"); + goto out; + } + + /* + * R_X86_64_TLSGD: allocate two contiguous entries in the GOT to hold a + * tls_index structure tls_index has two unsigned long, the first one is + * R_X86_64_DTPMOD64. + */ + if (r_type == R_X86_64_DTPMOD64 && + upatch_process_mem_read(obj->proc, addr + sizeof(unsigned long), + &tls_addr, sizeof(tls_addr))) { + log_error("copy address failed \n"); + goto out; + } + + elf_addr = setup_got_table(uelf, jmp_addr, tls_addr); + + log_debug("0x%lx: jmp_addr=0x%lx \n", elf_addr, jmp_addr); + +out: + return elf_addr; +} \ No newline at end of file diff --git a/upatch/upatch-manage/common.c b/upatch/upatch-manage/common.c deleted file mode 100644 index e9de766..0000000 --- a/upatch/upatch-manage/common.c +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#include /* for MAX_ARG_STRLEN */ -#include -#include -#include -#include - -#include "common.h" - -/* Common used tool functions */ -inline int copy_para_from_user(unsigned long addr, char *buf, size_t buf_len) -{ - size_t len; - - if (!buf || addr == 0) - return -EINVAL; - - len = strnlen_user((void __user *)addr, MAX_ARG_STRLEN); - if (len < 0 || len > buf_len) - return -EOVERFLOW; - - if (copy_from_user(buf, (void __user *)addr, len)) - return -ENOMEM; - - return 0; -} - -struct file *get_binary_file_from_addr(struct task_struct *task, unsigned long addr) -{ - struct vm_area_struct *vma = NULL; - - vma = find_vma(task->mm, addr); - if (!vma) - return NULL; - - if (!vma->vm_file) - return NULL; - - return vma->vm_file; -} - -int obtain_parameter_addr(char __user **pointer_array, char *name, - unsigned long *addr, unsigned long *next_addr) -{ - int ret; - unsigned long tmp; - unsigned long addr_pointer, next_pointer; - - if (addr) - *addr = 0; - - if (next_addr) - *next_addr = 0; - - ret = obtain_parameter_pointer(pointer_array, name, &addr_pointer, &next_pointer); - if (ret) - return ret; - - if (addr && addr_pointer != 0 - && !get_user(tmp, (unsigned long __user *)addr_pointer)) - *addr = tmp; - - if (next_addr && next_pointer != 0 - && !get_user(tmp, (unsigned long __user *)next_pointer)) - *next_addr = tmp; - - return 0; -} - -int obtain_parameter_pointer(char __user **pointer_array, char *name, - unsigned long *addr_pointer, unsigned long *next_pointer) -{ - char *__buffer; - unsigned long pointer_addr; - size_t len = strlen(name); - - if (!pointer_array) - return -EINVAL; - - __buffer = kmalloc(len + 1, GFP_KERNEL); - if (!__buffer) - return -ENOMEM; - - __buffer[len] = '\0'; - - if (addr_pointer) - *addr_pointer = 0; - - if (next_pointer) - *next_pointer = 0; - - for (;;) { - /* get pointer address first */ - if (get_user(pointer_addr, (unsigned long __user *)pointer_array)) - break; - pointer_array ++; - - if (!(const char __user *)pointer_addr) - break; - - if (copy_from_user(__buffer, (void __user *)pointer_addr, len)) - break; - - /* if not matched, continue */ - if (strncmp(__buffer, name, len)) - continue; - - pointer_array --; - if (addr_pointer) - *addr_pointer = (unsigned long)(unsigned long __user *)pointer_array; - - pointer_array ++; - if (next_pointer) - *next_pointer = (unsigned long)(unsigned long __user *)pointer_array; - - break; - } - - if (__buffer) - kfree(__buffer); - - return 0; -} - -char __user **get_argv_from_regs(struct pt_regs *regs) -{ - unsigned long stack_pointer = user_stack_pointer(regs); - return (void *)(stack_pointer + sizeof(unsigned long)); -} - -char __user **get_env_from_regs(struct pt_regs *regs) -{ - int argc; - unsigned long stack_pointer = user_stack_pointer(regs); - char __user **argv = get_argv_from_regs(regs); - - if (get_user(argc, (int *)stack_pointer)) { - pr_err("unable to read argc from stack pointer \n"); - return NULL; - } - - return (void *)((unsigned long)argv + (argc + 1) * sizeof(unsigned long)); -} diff --git a/upatch/upatch-manage/common.h b/upatch/upatch-manage/common.h deleted file mode 100644 index fa5ff8e..0000000 --- a/upatch/upatch-manage/common.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#ifndef _UPATCH_COMMON_H -#define _UPATCH_COMMON_H - -#include /* for MAX_ARG_STRLEN */ -#include -#include -#include - -/* Common used tool functions */ -int copy_para_from_user(unsigned long, char *, size_t); - -struct file *get_binary_file_from_addr(struct task_struct *, unsigned long); - -static bool inline streql(const char *a, const char *b) -{ - return strlen(a) == strlen(b) && !strncmp(a, b, strlen(a)); -} - -int obtain_parameter_addr(char __user **, char *, unsigned long *, unsigned long *); - -int obtain_parameter_pointer(char __user **, char *, unsigned long *, unsigned long *); - -char __user **get_argv_from_regs(struct pt_regs *); - -char __user **get_env_from_regs(struct pt_regs *); - -#endif /* _UPATCH_COMMON_H */ diff --git a/upatch/upatch-manage/kernel-patch/0001-uprobe-add-UPROBE_ALTER_PC-flag-for-uprobe-handlers.patch b/upatch/upatch-manage/kernel-patch/0001-uprobe-add-UPROBE_ALTER_PC-flag-for-uprobe-handlers.patch deleted file mode 100755 index 77cc172..0000000 --- a/upatch/upatch-manage/kernel-patch/0001-uprobe-add-UPROBE_ALTER_PC-flag-for-uprobe-handlers.patch +++ /dev/null @@ -1,89 +0,0 @@ -From: Longjun Luo - -Within uprobe handlers, the pc register could be -modified. In this situation, there is no need to -do a single stepping. Just like the kprobe, we -skip it. - -Signed-off-by: Longjun Luo ---- - include/linux/uprobes.h | 5 +++-- - kernel/events/uprobes.c | 16 +++++++++++++--- - 2 files changed, 16 insertions(+), 5 deletions(-) - -diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h -index f46e0ca0169c..0670fecbe1ce 100644 ---- a/include/linux/uprobes.h -+++ b/include/linux/uprobes.h -@@ -22,8 +22,9 @@ struct inode; - struct notifier_block; - struct page; - --#define UPROBE_HANDLER_REMOVE 1 --#define UPROBE_HANDLER_MASK 1 -+#define UPROBE_HANDLER_REMOVE 0x1 -+#define UPROBE_ALTER_PC 0x2 -+#define UPROBE_HANDLER_MASK 0x3 /* (UPROBE_HANDLER_REMOVE | UPROBE_ALTER_PC) */ - - #define MAX_URETPROBE_DEPTH 64 - -diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c -index 2eaa327f8158..d01a668fecae 100644 ---- a/kernel/events/uprobes.c -+++ b/kernel/events/uprobes.c -@@ -2064,10 +2064,16 @@ static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp) - return uprobe; - } - --static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs) -+/* -+ * The return value of handler_chain tags events that happen during -+ * calling handlers. If UPROBE_ALTER_PC happens, we must skip the -+ * single stepping. -+ */ -+static int handler_chain(struct uprobe *uprobe, struct pt_regs *regs) - { - struct uprobe_consumer *uc; - int remove = UPROBE_HANDLER_REMOVE; -+ int all_events = 0; - bool need_prep = false; /* prepare return uprobe, when needed */ - - down_read(&uprobe->register_rwsem); -@@ -2084,6 +2090,7 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs) - need_prep = true; - - remove &= rc; -+ all_events |= rc; - } - - if (need_prep && !remove) -@@ -2094,6 +2101,7 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs) - unapply_uprobe(uprobe, current->mm); - } - up_read(&uprobe->register_rwsem); -+ return all_events; - } - - static void -@@ -2183,7 +2191,7 @@ static void handle_swbp(struct pt_regs *regs) - { - struct uprobe *uprobe; - unsigned long bp_vaddr; -- int is_swbp; -+ int is_swbp, all_events; - - bp_vaddr = uprobe_get_swbp_addr(regs); - if (bp_vaddr == get_trampoline_vaddr()) -@@ -2235,7 +2243,9 @@ static void handle_swbp(struct pt_regs *regs) - if (arch_uprobe_ignore(&uprobe->arch, regs)) - goto out; - -- handler_chain(uprobe, regs); -+ all_events = handler_chain(uprobe, regs); -+ if (all_events & UPROBE_ALTER_PC) -+ goto out; - - if (arch_uprobe_skip_sstep(&uprobe->arch, regs)) - goto out; --- -2.37.3 diff --git a/upatch/upatch-manage/kmod.c b/upatch/upatch-manage/kmod.c deleted file mode 100755 index 5e6375a..0000000 --- a/upatch/upatch-manage/kmod.c +++ /dev/null @@ -1,292 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#include -#include -#include -#include -#include -#include - -#include "kmod.h" -#include "patch.h" -#include "common.h" - -static int upatch_open(struct inode *inode, struct file *file) -{ - return 0; -} - -static int upatch_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static ssize_t upatch_read(struct file *filp, char __user *ubuf, - size_t usize, loff_t *off) -{ - return 0; -} - -static ssize_t upatch_write(struct file *filp, const char __user *ubuf, - size_t usize, loff_t *off) -{ - return 0; -} - -static struct file *open_user_path(unsigned long user_addr) -{ - char *elf_path = NULL; - struct file *elf_file = NULL; - - elf_path = kmalloc(PATH_MAX, GFP_KERNEL); - if (!elf_path) - goto out; - - if (copy_para_from_user(user_addr, elf_path, PATH_MAX)) - goto out; - - elf_file = filp_open(elf_path, O_RDONLY, 0); - -out: - if (elf_path) - kfree(elf_path); - return elf_file; -} - -static int update_status(unsigned long user_addr, - enum upatch_module_state status) -{ - int ret; - struct upatch_entity *entity; - struct file *elf_file = NULL; - - elf_file = open_user_path(user_addr); - if (!elf_file || IS_ERR(elf_file)) { - pr_err("open cmd file failed - %d \n", IS_ERR(elf_file)); - ret = -ENOEXEC; - goto out; - } - - entity = upatch_entity_get(file_inode(elf_file)); - if (!entity) { - pr_err("no entity found \n"); - ret = -ENOENT; - goto out; - } - - mutex_lock(&entity->entity_status_lock); - if (entity->set_patch == NULL) { - pr_err("set status for removed patched is forbidden \n"); - ret = -EPERM; - goto out_lock; - } else { - entity->set_status = status; - } - - if (entity->set_status == UPATCH_STATE_REMOVED) { - upatch_put_patch_entity(entity->patch_entity); - entity->patch_entity = NULL; - entity->set_patch = NULL; - } - - ret = 0; -out_lock: - mutex_unlock(&entity->entity_status_lock); -out: - if (elf_file && !IS_ERR(elf_file)) - filp_close(elf_file, NULL); - return ret; -} - -static int check_status(unsigned long user_addr) -{ - int ret; - struct upatch_entity *entity; - struct file *elf_file = NULL; - - elf_file = open_user_path(user_addr); - if (!elf_file || IS_ERR(elf_file)) { - pr_err("open cmd file failed - %d \n", IS_ERR(elf_file)); - ret = -ENOEXEC; - goto out; - } - - entity = upatch_entity_get(file_inode(elf_file)); - if (!entity) { - ret = -ENOENT; - goto out; - } - - mutex_lock(&entity->entity_status_lock); - ret = entity->set_status; - mutex_unlock(&entity->entity_status_lock); -out: - if (elf_file && !IS_ERR(elf_file)) - filp_close(elf_file, NULL); - return ret; -} - -int attach_upatch(unsigned long user_addr) -{ - int ret; - struct upatch_conmsg conmsg; - char *binary = NULL; - char *patch = NULL; - - patch = kmalloc(PATH_MAX, GFP_KERNEL); - if (!patch) { - ret = -ENOMEM; - goto out; - } - - binary = kmalloc(PATH_MAX, GFP_KERNEL); - if (!binary) { - ret = -ENOMEM; - goto out; - } - - if (copy_from_user(&conmsg, (const void __user *)user_addr, sizeof(struct upatch_conmsg))) { - ret = -ENOMEM; - goto out; - } - - ret = copy_para_from_user((unsigned long)conmsg.binary, binary, PATH_MAX); - if (ret) - goto out; - - ret = copy_para_from_user((unsigned long)conmsg.patch, patch, PATH_MAX); - if (ret) - goto out; - - pr_debug("patch %s with %s \n", binary, patch); - - ret = upatch_attach(binary, patch); - if (ret) - goto out; - - ret = 0; -out: - if (binary) - kfree(binary); - if (patch) - kfree(patch); - return ret; -} - -static long upatch_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - if (_IOC_TYPE(cmd) != UPATCH_IOCTL_MAGIC) - return -EINVAL; - - switch (cmd) { - case UPATCH_ATTACH_PATCH: - return attach_upatch(arg); - case UPATCH_ACTIVE_PATCH: - return update_status(arg, UPATCH_STATE_ACTIVED); - case UPATCH_DEACTIVE_PATCH: - return update_status(arg, UPATCH_STATE_RESOLVED); - case UPATCH_REMOVE_PATCH: - return update_status(arg, UPATCH_STATE_REMOVED); - case UPATCH_INFO_PATCH: - return check_status(arg); - default: - return -ENOTTY; - } - - return 0; -} - -static const struct file_operations upatch_ops = { - .owner = THIS_MODULE, - .open = upatch_open, - .release = upatch_release, - .read = upatch_read, - .write = upatch_write, - .unlocked_ioctl = upatch_ioctl, - .llseek = no_llseek, -}; - -static struct miscdevice upatch_dev = { - .minor = MISC_DYNAMIC_MINOR, - .name = UPATCH_DEV_NAME, - .fops = &upatch_ops, - .mode = 0666, -}; - -static unsigned long (*kallsyms_lookup_name_p)(const char *); -void **sys_call_table_p; - -static struct kprobe kp = { - .symbol_name = "kallsyms_lookup_name", - .flags = KPROBE_FLAG_DISABLED, - .post_handler = NULL, - .pre_handler = NULL, -}; - -static int kprobe_init(void) -{ - int ret = register_kprobe(&kp); - if (ret < 0) { - pr_err("register kprobe for kallsyms_lookup_name failed - %d \n", ret); - goto out; - } - - kallsyms_lookup_name_p = (void *)kp.addr; - sys_call_table_p = (void **)kallsyms_lookup_name_p("sys_call_table"); - if (!sys_call_table_p) { - ret = -EINVAL; - pr_err("get addr for syscall table failed \n"); - goto out; - } -out: - return ret; -} - -static void kprobe_exit(void) -{ - unregister_kprobe(&kp); -} - -static int __init upatch_module_init(void) -{ - int ret; - - ret = kprobe_init(); - if (ret < 0) { - pr_err("kprobe init failed - %d \n", ret); - return ret; - } - - ret = misc_register(&upatch_dev); - if (ret) { - pr_err("register misc device for %s failed\n", UPATCH_DEV_NAME); - return ret; - } - - pr_info("upatch - %s load successfully \n", UPATCH_VERSION); - - return 0; -} - -static void __exit upatch_module_exit(void) -{ - misc_deregister(&upatch_dev); - upatch_exit(); - kprobe_exit(); -} - -module_init(upatch_module_init); -module_exit(upatch_module_exit); - -MODULE_AUTHOR("Longjun Luo (luolongjuna@gmail.com)"); -MODULE_AUTHOR("Zongwu Li (lzw32321226@163.com)"); -MODULE_DESCRIPTION("kernel module for upatch(live-patch in userspace)"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(UPATCH_VERSION); diff --git a/upatch/upatch-manage/list.h b/upatch/upatch-manage/list.h new file mode 100644 index 0000000..6e23794 --- /dev/null +++ b/upatch/upatch-manage/list.h @@ -0,0 +1,516 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +#define container_of(ptr, type, member) \ + ((type *)(((char *)(ptr)) - offsetof(type, member))) + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) \ + { \ + &(name), &(name) \ + } + +#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) \ + do { \ + (ptr)->next = (ptr); \ + (ptr)->prev = (ptr); \ + } while (0) + +static inline void list_init(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add_head - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_head(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline void list_del(struct list_head *entry) +{ + __list_del_entry(entry); +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del_entry(list); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del_entry(list); + list_add(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +/** + * list_rotate_left - rotate the list to the left + * @head: the head of the list + */ +static inline void list_rotate_left(struct list_head *head) +{ + struct list_head *first; + + if (!list_empty(head)) { + first = head->next; + list_move_tail(first, head); + } +} + +/** + * list_is_singular - tests whether a list has just one entry. + * @head: the list to test. + */ +static inline int list_is_singular(const struct list_head *head) +{ + return !list_empty(head) && (head->next == head->prev); +} + +static inline void __list_cut_position(struct list_head *list, + struct list_head *head, + struct list_head *entry) +{ + struct list_head *new_first = entry->next; + list->next = head->next; + list->next->prev = list; + list->prev = entry; + entry->next = list; + head->next = new_first; + new_first->prev = head; +} + +static inline void __list_splice(const struct list_head *list, + struct list_head *prev, struct list_head *next) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + + first->prev = prev; + prev->next = first; + + last->next = next; + next->prev = last; +} + +/** + * list_splice - join two lists, this is designed for stacks + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(const struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head, head->next); +} + +/** + * list_splice_tail - join two lists, each list being a queue + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice_tail(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head->prev, head); +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + */ +#define list_entry(ptr, type, member) container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_last_entry - get the last element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_last_entry(ptr, type, member) list_entry((ptr)->prev, type, member) + +/** + * list_first_entry_or_null - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note that if the list is empty, it returns NULL. + */ +#define list_first_entry_or_null(ptr, type, member) \ + ({ \ + struct list_head *head__ = (ptr); \ + struct list_head *pos__ = READ_ONCE(head__->next); \ + pos__ != head__ ? list_entry(pos__, type, member) : NULL; \ + }) + +/** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +/** + * list_prev_entry - get the prev element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_prev_entry(pos, member) \ + list_entry((pos)->member.prev, typeof(*(pos)), member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal + * of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + &pos->member != (head); pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member); \ + &pos->member != (head); pos = list_prev_entry(pos, member)) + +/** + * list_prepare_entry - prepare a pos entry for use in + * list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_head within the struct. + * + * Prepares a pos entry for use as a start point in + * list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ?: list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_next_entry(pos, member); &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_prev_entry(pos, member); &pos->member != (head); \ + pos = list_prev_entry(pos, member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current + * point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_from_reverse - iterate backwards over list of given type + * from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate backwards over list of given type, continuing from current position. + */ +#define list_for_each_entry_from_reverse(pos, head, member) \ + for (; &pos->member != (head); pos = list_prev_entry(pos, member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against + * removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_continue - continue list iteration safe against + * removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_next_entry(pos, member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_from - iterate over list from current point safe + * against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_next_entry(pos, member); &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_reverse - iterate backwards over list safe against + * removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member), \ + n = list_prev_entry(pos, member); \ + &pos->member != (head); pos = n, n = list_prev_entry(n, member)) + +/** + * list_safe_reset_next - reset a stale list_for_each_entry_safe loop + * @pos: the loop cursor used in the list_for_each_entry_safe loop + * @n: temporary storage used in list_for_each_entry_safe + * @member: the name of the list_head within the struct. + * + * list_safe_reset_next is not safe to use in general if the list may be + * modified concurrently (eg. the lock is dropped in the loop body). An + * exception to this is if the cursor element (pos) is pinned in the list, + * and list_safe_reset_next is called after re-taking the lock and before + * completing the current iteration of the loop body. + */ +#define list_safe_reset_next(pos, n, member) n = list_next_entry(pos, member) + +#endif diff --git a/upatch/upatch-manage/log.h b/upatch/upatch-manage/log.h new file mode 100644 index 0000000..5f8b188 --- /dev/null +++ b/upatch/upatch-manage/log.h @@ -0,0 +1,75 @@ +/* + * log.h + * + * Copyright (C) 2014 Seth Jennings + * Copyright (C) 2013-2014 Josh Poimboeuf + * Copyright (C) 2022 Longjun Luo + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +#ifndef __UPATCH_LOG_H_ +#define __UPATCH_LOG_H_ + +#include +#include + +/* Files that include log.h must define loglevel and logprefix */ +extern enum loglevel loglevel; +extern char *logprefix; + +enum exit_status { + EXIT_STATUS_SUCCESS = 0, + EXIT_STATUS_ERROR = 1, + EXIT_STATUS_DIFF_FATAL = 2, + EXIT_STATUS_NO_CHANGE = 3, +}; + +/* Since upatch-build is an one-shot program, we do not care about failure + * handler */ +#define ERROR(format, ...) \ + error(EXIT_STATUS_ERROR, 0, "ERROR: %s: %s: %d: " format, logprefix, \ + __FUNCTION__, __LINE__, ##__VA_ARGS__) + +#define DIFF_FATAL(format, ...) \ + error(EXIT_STATUS_DIFF_FATAL, 0, "ERROR: %s: %s: %d: " format, \ + logprefix, __FUNCTION__, __LINE__, ##__VA_ARGS__) + +/* it is time cost */ +#define log_debug(format, ...) log(DEBUG, format, ##__VA_ARGS__) +#define log_normal(format, ...) log(NORMAL, format, ##__VA_ARGS__) +#define log_warn(format, ...) log(WARN, "%s: " format, logprefix, ##__VA_ARGS__) +#define log_error(format, ...) log(ERR, format, ##__VA_ARGS__) + +#define log(level, format, ...) \ + ({ \ + if (loglevel <= (level)) \ + printf(format, ##__VA_ARGS__); \ + }) + +#define REQUIRE(COND, message) \ + do \ + if (!(COND)) \ + ERROR(message); \ + while (0) + +enum loglevel { + DEBUG, + NORMAL, + WARN, + ERR, +}; +#endif \ No newline at end of file diff --git a/upatch/upatch-manage/patch-load.c b/upatch/upatch-manage/patch-load.c deleted file mode 100644 index 3d8ec2a..0000000 --- a/upatch/upatch-manage/patch-load.c +++ /dev/null @@ -1,1008 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "patch-uprobe.h" -#include "common.h" -#include "patch.h" -#include "arch/patch-load.h" -#include "kmod.h" - -#define GOT_RELA_NAME ".rela.dyn" -#define PLT_RELA_NAME ".rela.plt" -#define TDATA_NAME ".tdata" -#define TBSS_NAME ".tbss" - -#ifndef ARCH_SHF_SMALL -#define ARCH_SHF_SMALL 0 -#endif - -/* If this is set, the section belongs in the init part of the module */ -#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) - -static int patch_header_check(struct upatch_load_info *info) -{ - if (info->len < sizeof(*(info->hdr))) - return -ENOEXEC; - - if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0 - || info->hdr->e_type != ET_REL - || !elf_check_arch(info->hdr) - || info->hdr->e_shentsize != sizeof(Elf_Shdr)) - return -ENOEXEC; - - if (info->hdr->e_shoff >= info->len - || (info->hdr->e_shnum * sizeof(Elf_Shdr) > - info->len - info->hdr->e_shoff)) - return -ENOEXEC; - return 0; -} - -static struct upatch_module * -__upatch_module_get(struct upatch_entity *entity, pid_t pid) -{ - struct upatch_module *um; - list_for_each_entry(um, &entity->module_list, list) { - if (um->pid == pid) - return um; - } - return NULL; -} - -struct upatch_module *__upatch_module_new(struct upatch_entity *entity, pid_t pid) -{ - struct upatch_module *um; - um = kzalloc(sizeof(struct upatch_module), GFP_KERNEL); - if (!um) - return NULL; - - um->pid = pid; - um->real_state = UPATCH_STATE_ATTACHED; - um->real_patch = entity->set_patch; - mutex_init(&um->module_status_lock); - INIT_LIST_HEAD(&um->list); - list_add(&um->list, &entity->module_list); - return um; -} - -struct upatch_module *upatch_module_get_or_create(struct upatch_entity *entity, pid_t pid) -{ - struct upatch_module *um; - mutex_lock(&entity->entity_status_lock); - um = __upatch_module_get(entity, pid); - if (!um) - um = __upatch_module_new(entity, pid); - mutex_unlock(&entity->entity_status_lock); - return um; -} - -static int setup_load_info(struct upatch_load_info *info) -{ - unsigned int i; - - info->sechdrs = (void *)info->hdr + info->hdr->e_shoff; - info->secstrings = (void *)info->hdr - + info->sechdrs[info->hdr->e_shstrndx].sh_offset; - - for (i = 1; i < info->hdr->e_shnum; i++) { - if (info->sechdrs[i].sh_type == SHT_SYMTAB) { - info->index.sym = i; - info->index.str = info->sechdrs[i].sh_link; - info->strtab = (char *)info->hdr - + info->sechdrs[info->index.str].sh_offset; - break; - } - } - - if (!info->index.sym) { - pr_warn("patch has no symbols (stripped?)\n"); - return -ENOEXEC; - } - - return 0; -} - -static int rewrite_section_headers(struct upatch_load_info *info) -{ - unsigned int i; - - /* Handle SHF_ALLOC in this part */ - - /* This should always be true, but let's be sure. */ - info->sechdrs[0].sh_addr = 0; - info->sechdrs[0].sh_addralign = 0; - - for (i = 1; i < info->hdr->e_shnum; i++) { - Elf_Shdr *shdr = &info->sechdrs[i]; - if (shdr->sh_type != SHT_NOBITS - && info->len < shdr->sh_offset + shdr->sh_size) { - pr_err("upatch len %lu truncated\n", info->len); - return -ENOEXEC; - } - - /* Mark all sections sh_addr with their address in the - temporary image. */ - shdr->sh_addr = (size_t)info->hdr + shdr->sh_offset; - pr_debug("section %s at 0x%llx \n", info->secstrings + shdr->sh_name, - shdr->sh_addr); - } - - return 0; -} - -/* TODO: check meta data in this func */ -static int check_modinfo(void) -{ - return 0; -} - -/* Additional bytes needed by arch in front of individual sections */ -unsigned int __weak arch_mod_section_prepend(struct upatch_module *mod, - unsigned int section) -{ - /* default implementation just returns zero */ - return 0; -} - -static long get_offset(struct upatch_module *mod, unsigned int *size, - Elf_Shdr *sechdr, unsigned int section) -{ - long ret; - - *size += arch_mod_section_prepend(mod, section); - ret = ALIGN(*size, sechdr->sh_addralign ?: 1); - *size = ret + sechdr->sh_size; - return ret; -} - -static void layout_jmptable(struct upatch_module *mod, struct upatch_load_info *info) -{ - info->jmp_cur_entry = 0; - info->jmp_max_entry = JMP_TABLE_MAX_ENTRY; - info->jmp_offs = ALIGN(mod->core_layout.size, sizeof(unsigned long)); - mod->core_layout.size = info->jmp_offs - + info->jmp_max_entry * sizeof(struct upatch_jmp_table_entry); -} - -static void layout_sections(struct upatch_module *mod, struct upatch_load_info *info) -{ - static unsigned long const masks[][2] = { - /* NOTE: all executable code must be the first section - * in this array; otherwise modify the text_size - * finder in the two loops below */ - { SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL }, - { SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL }, - { SHF_RO_AFTER_INIT | SHF_ALLOC, ARCH_SHF_SMALL }, - { SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL }, - { ARCH_SHF_SMALL | SHF_ALLOC, 0 } - }; - unsigned int m, i; - - for (i = 0; i < info->hdr->e_shnum; i++) - info->sechdrs[i].sh_entsize = ~0UL; - - pr_debug("upatch section allocation order: \n"); - for (m = 0; m < ARRAY_SIZE(masks); ++m) { - for (i = 0; i < info->hdr->e_shnum; ++i) { - Elf_Shdr *s = &info->sechdrs[i]; - const char *sname = info->secstrings + s->sh_name; - - if ((s->sh_flags & masks[m][0]) != masks[m][0] - || (s->sh_flags & masks[m][1]) - || s->sh_entsize != ~0UL) - continue; - s->sh_entsize = get_offset(mod, &mod->core_layout.size, s, i); - pr_debug("\t%s\n", sname); - } - switch (m) { - case 0: /* executable */ - layout_jmptable(info->mod, info); - mod->core_layout.size = PAGE_ALIGN(mod->core_layout.size); - mod->core_layout.text_size = mod->core_layout.size; - break; - case 1: /* RO: text and ro-data */ - mod->core_layout.size = PAGE_ALIGN(mod->core_layout.size); - mod->core_layout.ro_size = mod->core_layout.size; - break; - case 2: /* RO after init */ - mod->core_layout.size = PAGE_ALIGN(mod->core_layout.size); - mod->core_layout.ro_after_init_size = mod->core_layout.size; - break; - case 4: /* whole core */ - mod->core_layout.size = PAGE_ALIGN(mod->core_layout.size); - break; - } - } -} - -/* TODO: only included used symbol */ -static bool is_upatch_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs, - unsigned int shnum) -{ - return true; -} -/* - * We only allocate and copy the strings needed by the parts of symtab - * we keep. This is simple, but has the effect of making multiple - * copies of duplicates. We could be more sophisticated, see - * linux-kernel thread starting with - * <73defb5e4bca04a6431392cc341112b1@localhost>. - */ -static void layout_symtab(struct upatch_module *mod, struct upatch_load_info *info) -{ - Elf_Shdr *symsect = info->sechdrs + info->index.sym; - Elf_Shdr *strsect = info->sechdrs + info->index.str; - /* TODO: only support same arch as kernel now */ - const Elf_Sym *src; - unsigned int i, nsrc, ndst, strtab_size = 0; - - /* Put symbol section at end of init part of module. */ - symsect->sh_flags |= SHF_ALLOC; - symsect->sh_entsize = get_offset(mod, &mod->init_layout.size, symsect, - info->index.sym) | INIT_OFFSET_MASK; - pr_debug("\t%s\n", info->secstrings + symsect->sh_name); - - src = (void *)info->hdr + symsect->sh_offset; - nsrc = symsect->sh_size / sizeof(*src); - - /* Compute total space required for the symbols' strtab. */ - for (ndst = i = 0; i < nsrc; i++) { - if (i == 0 || - is_upatch_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) { - strtab_size += strlen(&info->strtab[src[i].st_name])+1; - ndst++; - } - } - - /* Append room for core symbols at end of core part. */ - info->symoffs = ALIGN(mod->core_layout.size, symsect->sh_addralign ?: 1); - info->stroffs = mod->core_layout.size = info->symoffs + ndst * sizeof(Elf_Sym); - mod->core_layout.size += strtab_size; - info->core_typeoffs = mod->core_layout.size; - mod->core_layout.size += ndst * sizeof(char); - mod->core_layout.size = PAGE_ALIGN(mod->core_layout.size); - - /* Put string table section at end of init part of module. */ - strsect->sh_flags |= SHF_ALLOC; - strsect->sh_entsize = get_offset(mod, &mod->init_layout.size, strsect, - info->index.str) | INIT_OFFSET_MASK; - mod->init_layout.size = PAGE_ALIGN(mod->init_layout.size); - pr_debug("\t%s\n", info->secstrings + strsect->sh_name); -} - -/* TODO: lock for mm */ -unsigned long get_upatch_pole(unsigned long search, unsigned long size) -{ - struct vm_area_struct *vma = - find_vma_intersection(current->mm, search, search + size); - while (vma) { - search = vma->vm_end; - vma = find_vma_intersection(current->mm, search, search + size); - } - pr_debug("find search address at 0x%lx \n", search); - return search; -} - -/* alloc memory in userspace */ -static void __user *__upatch_module_alloc(unsigned long hint, unsigned long size) -{ - unsigned long mem_addr; - unsigned long addr = get_upatch_pole(hint, size); - if (!addr) - return NULL; - - mem_addr = vm_mmap(NULL, addr, size, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_ANONYMOUS | MAP_PRIVATE, 0); - if (IS_ERR((void *)mem_addr)) { - pr_err("mmap module memory faild with %ld \n", PTR_ERR((void *)mem_addr)); - return NULL; - } else if (mem_addr != addr) { - pr_err("find wrong place 0x%lx <- 0x%lx \n", mem_addr, addr); - vm_munmap(mem_addr, size); - return NULL; - } - - return (void *)mem_addr; -} - -int __upatch_module_memfree(void __user *addr, unsigned long size) -{ - return vm_munmap((unsigned long)addr, size); -} - -int upatch_module_memfree(struct upatch_module_layout *layout) -{ - if (layout->kbase) - vfree(layout->kbase); - layout->kbase = NULL; - return __upatch_module_memfree(layout->base, layout->size); -} - -void upatch_module_deallocate(struct upatch_module *mod) -{ - // if (mod->init_layout.base) - // upatch_module_memfree(&mod->init_layout); - mod->init_layout.base = NULL; - // if (mod->core_layout.base) - // upatch_module_memfree(&mod->core_layout); - mod->core_layout.base = NULL; - mod->upatch_funs = NULL; - mod->strtab = NULL; - mod->syms = NULL; -} - -static int upatch_module_alloc(struct upatch_load_info *info, - struct upatch_module_layout *layout, unsigned long user_limit) -{ - /* find moudle location from code start place */ - unsigned long hint = info->running_elf.load_start; - - layout->base = __upatch_module_alloc(hint, layout->size); - if (!layout->base) - return -ENOMEM; - - if ((unsigned long)layout->base - hint >= user_limit) { - pr_err("out of range limit \n"); - __upatch_module_memfree(layout->base, layout->size); - return -ENOMEM; - } - - pr_debug("upatch module at 0x%lx \n", (unsigned long)layout->base); - - layout->kbase = vmalloc(layout->size); - if (!layout->kbase) { - __upatch_module_memfree(layout->base, layout->size); - return -ENOMEM; - } - - memset(layout->kbase, 0, layout->size); - - return 0; -} - -static void load_info_clear(struct upatch_load_info *info) -{ - if (info->mod->core_layout.kbase) - vfree(info->mod->core_layout.kbase); - info->mod->core_layout.kbase = NULL; - if (info->mod->init_layout.kbase) - vfree(info->mod->init_layout.kbase); - info->mod->init_layout.kbase = NULL; - if (info->running_elf.hdr) - vfree(info->running_elf.hdr); - info->running_elf.hdr = NULL; - if (info->hdr) - vfree(info->hdr); - info->hdr = NULL; -} - -static int move_module(struct upatch_module *mod, struct upatch_load_info *info) -{ - int i, ret; - - /* Do the allocs. */ - ret = upatch_module_alloc(info, &mod->core_layout, 0xffffffff); - if (ret) { - pr_err("alloc upatch module memory failed: %d \n", ret); - return ret; - } - - /* TODO: we do not use this section now */ - if (mod->init_layout.size) { - ret = upatch_module_alloc(info, &mod->init_layout, 0xffffffff); - if (ret) { - upatch_module_memfree(&mod->core_layout); - return -ENOMEM; - } - } else { - mod->init_layout.base = NULL; - } - - /* Transfer each section which specifies SHF_ALLOC */ - pr_debug("final section addresses:\n"); - for (i = 0; i < info->hdr->e_shnum; i++) { - void __user *dest; - void *kdest; - Elf_Shdr *shdr = &info->sechdrs[i]; - - if (!(shdr->sh_flags & SHF_ALLOC)) - continue; - - if (shdr->sh_entsize & INIT_OFFSET_MASK) { - dest = mod->init_layout.base - + (shdr->sh_entsize & ~INIT_OFFSET_MASK); - kdest = mod->init_layout.kbase - + (shdr->sh_entsize & ~INIT_OFFSET_MASK); - } else { - dest = mod->core_layout.base + shdr->sh_entsize; - kdest = mod->core_layout.kbase + shdr->sh_entsize; - } - - if (shdr->sh_type != SHT_NOBITS) - memcpy(kdest, (void *)shdr->sh_addr, shdr->sh_size); - /* Update sh_addr to point to copy in image. */ - shdr->sh_addr = (unsigned long)kdest; - /* overuse this attr to record user address */ - shdr->sh_addralign = (unsigned long)dest; - pr_debug("\t0x%lx %s <- 0x%lx\n", - (long)dest, info->secstrings + shdr->sh_name, (long)kdest); - } - - return 0; -} - -static struct upatch_module *layout_and_allocate(struct upatch_load_info *info) -{ - int err; - - err = check_modinfo(); - if (err) - return ERR_PTR(err); - - layout_sections(info->mod, info); - layout_symtab(info->mod, info); - - err = move_module(info->mod, info); - if (err) - return ERR_PTR(err); - - return info->mod; -} - -static unsigned int find_sec(const struct upatch_load_info *info, const char *name) -{ - unsigned int i; - - for (i = 1; i < info->hdr->e_shnum; i++) { - Elf_Shdr *shdr = &info->sechdrs[i]; - /* Alloc bit cleared means "ignore it." */ - if ((shdr->sh_flags & SHF_ALLOC) - && strcmp(info->secstrings + shdr->sh_name, name) == 0) - return i; - } - return 0; -} - -static void *section_addr(const struct upatch_load_info *info, const char *name) -{ - /* Section 0 has sh_addr 0. */ - return (void *)info->sechdrs[find_sec(info, name)].sh_addralign; -} - -static void *section_objs(const struct upatch_load_info *info, - const char *name, - size_t object_size, - unsigned int *num) -{ - unsigned int sec = find_sec(info, name); - - /* Section 0 has sh_addr 0 and sh_size 0. */ - *num = info->sechdrs[sec].sh_size / object_size; - /* use address from user space */ - return (void __user *)info->sechdrs[sec].sh_addralign; -} - -/* handle special sections in this func */ -static int find_upatch_module_sections(struct upatch_module *mod, struct upatch_load_info *info) -{ - mod->syms = section_objs(info, ".symtab", - sizeof(*mod->syms), &mod->num_syms); - mod->upatch_funs = section_objs(info, ".upatch.funcs", - sizeof(*mod->syms), &mod->num_upatch_funcs); - mod->strtab = section_addr(info, ".strtab"); - return 0; -} - -static unsigned long -resolve_symbol(struct running_elf_info *elf_info, const char *name, Elf_Sym patch_sym) -{ - unsigned int i; - unsigned long elf_addr = 0; - char *sym_name, *tmp; - Elf_Shdr *sechdr; - Elf_Sym *sym; - Elf64_Rela *rela; - - if (ELF_ST_TYPE(patch_sym.st_info) == STT_IFUNC && - (elf_info->hdr->e_ident[EI_OSABI] == ELFOSABI_GNU || elf_info->hdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD)) - goto out_plt; - /* handle symbol table first, in most cases, symbol table does not exist */ - sechdr = &elf_info->sechdrs[elf_info->index.sym]; - sym = (void *)elf_info->hdr + sechdr->sh_offset; - for (i = 0; i < sechdr->sh_size / sizeof(Elf_Sym); i++) { - sym_name = elf_info->strtab + sym[i].st_name; - /* FIXME: handle version for external function */ - tmp = strchr(sym_name, '@'); - if (tmp != NULL) - *tmp = '\0'; - if (streql(sym_name, name) && sym[i].st_shndx != SHN_UNDEF) { - pr_debug("found resolved undefined symbol %s at 0x%llx \n", name, sym[i].st_value); - elf_addr = elf_info->load_bias + sym[i].st_value; - goto out; - } - } - - /* - * Handle external symbol, several possible solutions here: - * 1. use symbol address from .dynsym, but most of its address is still undefined - * 2. use address from PLT/GOT, problems are: - * 1) range limit(use jmp table?) - * 2) only support existed symbols - * 3. read symbol from library, combined with load_bias, calculate it directly - * and then worked with jmp table. - * - * Currently, we will try approach 1 and approach 2. - * Approach 3 is more general, but difficulty to implement. - */ -out_plt: - if (!elf_info->index.dynsym) - goto out; - - sechdr = &elf_info->sechdrs[elf_info->index.dynsym]; - sym = (void *)elf_info->hdr + sechdr->sh_offset; - - /* handle external function */ - if (!elf_info->index.relaplt) - goto out_got; - - sechdr = &elf_info->sechdrs[elf_info->index.relaplt]; - rela = (void *)elf_info->hdr + sechdr->sh_offset; - for (i = 0; i < sechdr->sh_size / sizeof(Elf64_Rela); i ++) { - unsigned long r_sym = ELF64_R_SYM (rela[i].r_info); - /* for executable file, r_offset is virtual address of PLT table */ - void __user *tmp_addr = (void *)(elf_info->load_bias + rela[i].r_offset); - - /* some rela don't have the symbol index, use the symbol's value and rela's addend to find the symbol. - * for example, R_X86_64_IRELATIVE. - */ - if (r_sym == 0) { - if (rela[i].r_addend != patch_sym.st_value) - continue; - sprintf(sym_name, "%llx", rela[i].r_addend); - } - else { - /* ATTENTION: should we consider the relocation type ? */ - sym_name = elf_info->dynstrtab + sym[r_sym].st_name; - /* FIXME: consider version of the library */ - tmp = strchr(sym_name, '@'); - if (tmp != NULL) - *tmp = '\0'; - - if (!(streql(sym_name, name) - && (ELF64_ST_TYPE(sym[r_sym].st_info) == STT_FUNC || ELF64_ST_TYPE(sym[r_sym].st_info) == STT_TLS))) - continue; - } - - elf_addr = insert_plt_table(elf_info->load_info, ELF64_R_TYPE(rela[i].r_info), tmp_addr); - pr_debug("found unresolved plt.rela %s at 0x%llx -> 0x%lx\n", - sym_name, rela[i].r_offset, elf_addr); - goto out; - } - -out_got: - /* handle external object, we need get it's address, used for R_X86_64_REX_GOTPCRELX */ - if (!elf_info->index.reladyn) - goto out; - - sechdr = &elf_info->sechdrs[elf_info->index.reladyn]; - rela = (void *)elf_info->hdr + sechdr->sh_offset; - for (i = 0; i < sechdr->sh_size / sizeof(Elf64_Rela); i ++) { - unsigned long r_sym = ELF64_R_SYM (rela[i].r_info); - /* for executable file, r_offset is virtual address of GOT table */ - void __user *tmp_addr = (void *)(elf_info->load_bias + rela[i].r_offset); - - if (r_sym == 0) { - if (rela[i].r_addend != patch_sym.st_value) - continue; - sprintf(sym_name, "%llx", rela[i].r_addend); - } - else { - sym_name = elf_info->dynstrtab + sym[r_sym].st_name; - /* TODO: don't care about its version here */ - tmp = strchr(sym_name, '@'); - if (tmp != NULL) - *tmp = '\0'; - - /* function could also be part of the GOT with the type R_X86_64_GLOB_DAT */ - if (!streql(sym_name, name)) - continue; - } - - elf_addr = insert_got_table(elf_info->load_info, ELF64_R_TYPE(rela[i].r_info), tmp_addr); - pr_debug("found unresolved .got %s at 0x%lx \n", sym_name, elf_addr); - goto out; - } - - // get symbol address from .dynsym - sechdr = &elf_info->sechdrs[elf_info->index.dynsym]; - sym = (void *)elf_info->hdr + sechdr->sh_offset; - for (i = 0; i < sechdr->sh_size / sizeof(Elf64_Sym); i ++) { - void __user *tmp_addr; - - /* only need the st_value that is not 0 */ - if (sym[i].st_value == 0) - continue; - - sym_name = elf_info->dynstrtab + sym[i].st_name; - /* TODO: don't care about its version here */ - tmp = strchr(sym_name, '@'); - if (tmp != NULL) - *tmp = '\0'; - - /* function could also be part of the GOT with the type R_X86_64_GLOB_DAT */ - if (!streql(sym_name, name)) - continue; - - tmp_addr = (void *)(elf_info->load_bias + sym[i].st_value); - elf_addr = insert_got_table(elf_info->load_info, 0, tmp_addr); - pr_debug("found unresolved .got %s at 0x%lx \n", sym_name, elf_addr); - goto out; - } - -out: - if (!elf_addr) { - pr_err("unable to found valid symbol %s \n", name); - } - return elf_addr; -} - -/* TODO: set timeout */ -static inline unsigned long resolve_symbol_wait(struct upatch_module *mod, - struct upatch_load_info *info, const char *name, Elf_Sym patch_sym) -{ - return resolve_symbol(&info->running_elf, name, patch_sym); -} - -static int simplify_symbols(struct upatch_module *mod, struct upatch_load_info *info) -{ - Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; - Elf_Sym *sym = (void *)symsec->sh_addr; - unsigned long secbase; - unsigned int i; - int ret = 0; - unsigned long elf_addr; - - for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) { - const char *name; - - if (ELF_ST_TYPE(sym[i].st_info) == STT_SECTION - && sym[i].st_shndx < info->hdr->e_shnum) - name = info->secstrings + info->sechdrs[sym[i].st_shndx].sh_name; - else - name = info->strtab + sym[i].st_name; - - switch (sym[i].st_shndx) { - case SHN_COMMON: - pr_warn("unsupported Common symbol: %s\n", name); - ret = -ENOEXEC; - break; - case SHN_ABS: - break; - case SHN_UNDEF: - elf_addr = resolve_symbol_wait(mod, info, name, sym[i]); - if (!elf_addr) - ret = -ENOEXEC; - sym[i].st_value = elf_addr; - pr_debug("resolved symbol %s at 0x%lx \n", - name, (unsigned long)sym[i].st_value); - break; - case SHN_LIVEPATCH: - sym[i].st_value += info->running_elf.load_bias; - pr_debug("resolved livepatch symbol %s at 0x%lx \n", - name, (unsigned long)sym[i].st_value); - break; - default: - /* use real address to calculate secbase */ - secbase = info->sechdrs[sym[i].st_shndx].sh_addralign; - sym[i].st_value += secbase; - pr_debug("normal symbol %s at 0x%lx \n", - name, (unsigned long)sym[i].st_value); - break; - } - } - - return ret; -} - -static int apply_relocations(struct upatch_module *mod, struct upatch_load_info *info) -{ - unsigned int i; - int err = 0; - - /* Now do relocations. */ - for (i = 1; i < info->hdr->e_shnum; i++) { - unsigned int infosec = info->sechdrs[i].sh_info; - const char *name = info->secstrings + info->sechdrs[i].sh_name; - - /* Not a valid relocation section? */ - if (infosec >= info->hdr->e_shnum) - continue; - - /* Don't bother with non-allocated sections */ - if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC)) - continue; - - if (info->sechdrs[i].sh_type == SHT_REL) { - pr_err("do rel relocations for %s \n", name); - return -EPERM; - } else if (info->sechdrs[i].sh_type == SHT_RELA) { - pr_debug("do rela relocations for %s \n", name); - err = apply_relocate_add(info, info->sechdrs, info->strtab, - info->index.sym, i, mod); - } - - if (err < 0) - break; - } - return err; -} - -static int move_to_user(struct upatch_module_layout *layout) -{ - pr_debug("mov content from 0x%lx to 0x%lx with 0x%x \n", - (unsigned long)layout->kbase, (unsigned long)layout->base, layout->size); - if (copy_to_user(layout->base, layout->kbase, layout->size)) - return -EPERM; - return 0; -} - -static int post_relocation(struct upatch_module *mod, struct upatch_load_info *info) -{ - int ret; - - mod->load_bias = info->running_elf.load_bias; - ret = move_to_user(&mod->core_layout); - if (ret) - return ret; - - ret = move_to_user(&mod->init_layout); - if (ret) - return ret; - - return 0; -} - -int load_binary_syms(struct file *binary_file, struct running_elf_info *elf_info) -{ - int ret; - loff_t offset; - unsigned int i; - void __user *sym_addr; - Elf_Shdr *symsec; - Elf_Sym *sym; - const char *name; - - elf_info->len = i_size_read(file_inode(binary_file)); - elf_info->hdr = vmalloc(elf_info->len); - if (!elf_info->hdr) { - ret = -ENOMEM; - goto out; - } - - /* TODO: no need to read the whole file */ - offset = 0; - ret = kernel_read(binary_file, elf_info->hdr, elf_info->len, &offset); - if (ret != elf_info->len) { - pr_err("read binary file failed - %d \n", ret); - ret = -ENOMEM; - goto out; - } - - elf_info->sechdrs = (void *)elf_info->hdr + elf_info->hdr->e_shoff; - elf_info->prohdrs = (void *)elf_info->hdr + elf_info->hdr->e_phoff; - elf_info->secstrings = (void *)elf_info->hdr - + elf_info->sechdrs[elf_info->hdr->e_shstrndx].sh_offset; - elf_info->tls_size = 0; - - /* check section header */ - for (i = 1; i < elf_info->hdr->e_shnum; i++) { - name = elf_info->secstrings + elf_info->sechdrs[i].sh_name; - if (elf_info->sechdrs[i].sh_type == SHT_SYMTAB) { - elf_info->index.sym = i; - elf_info->index.symstr = elf_info->sechdrs[i].sh_link; - elf_info->strtab = (char *)elf_info->hdr - + elf_info->sechdrs[elf_info->index.symstr].sh_offset; - } else if (elf_info->sechdrs[i].sh_type == SHT_DYNSYM) { - elf_info->index.dynsym = i; - elf_info->index.dynsymstr = elf_info->sechdrs[i].sh_link; - elf_info->dynstrtab = (char *)elf_info->hdr - + elf_info->sechdrs[elf_info->index.dynsymstr].sh_offset; - } else if (elf_info->sechdrs[i].sh_type == SHT_DYNAMIC) { - /* Currently, we don't utilize it */ - } else if (streql(name, PLT_RELA_NAME) - && elf_info->sechdrs[i].sh_type == SHT_RELA) { - elf_info->index.relaplt = i; - pr_debug("found %s with %d \n", PLT_RELA_NAME, i); - } else if (streql(name, GOT_RELA_NAME) - && elf_info->sechdrs[i].sh_type == SHT_RELA) { - elf_info->index.reladyn = i; - pr_debug("found %s with %d \n", GOT_RELA_NAME, i); - } - } - - for (i = 0; i < elf_info->hdr->e_phnum; i++) { - if (elf_info->prohdrs[i].p_type == PT_TLS) { - elf_info ->tls_size = elf_info->prohdrs[i].p_memsz; - elf_info ->tls_align = elf_info->prohdrs[i].p_align; - break; - } - } - - if (elf_info->index.dynsym) { - symsec = &elf_info->sechdrs[elf_info->index.dynsym]; - sym_addr = (void __user *)elf_info->load_bias - + symsec->sh_addr; - - sym = (void *)elf_info->hdr + symsec->sh_offset; - - pr_debug("dynamic symbol address at 0x%lx with 0x%llx \n", - (unsigned long)sym_addr, symsec->sh_size); - - /* read dynamic symtab from memory and copy it to the binary_hdr */ - if (copy_from_user(sym, sym_addr, symsec->sh_size)) { - pr_err("read dynsym failed \n"); - ret = -ENOMEM; - goto out; - } - } - - /* TODO: it is possible that no symbol resolve is needed */ - if (!elf_info->index.sym && !elf_info->index.dynsym) { - pr_err("no symtab/dynsym found \n"); - ret = -ENOEXEC; - goto out; - } - - ret = 0; -out: - return ret; -} - -/* works for 64-bit architecture */ -static long (*orig_mprotect) (const struct pt_regs *regs); - -static long krun_mprotect(unsigned long start, size_t len, unsigned long prot) -{ - struct pt_regs regs; - setup_parameters(®s, start, len, prot); - return orig_mprotect(®s); -} - -static void frob_text(const struct upatch_module_layout *layout) -{ - krun_mprotect((unsigned long)layout->base, layout->text_size, PROT_READ | PROT_EXEC); -} - -static void frob_rodata(const struct upatch_module_layout *layout) -{ - krun_mprotect((unsigned long)layout->base + layout->text_size, layout->ro_size - layout->text_size, PROT_READ); -} - -static void frob_writable_data(const struct upatch_module_layout *layout) -{ - krun_mprotect((unsigned long)layout->base + layout->ro_after_init_size, layout->size - layout->ro_after_init_size, PROT_READ | PROT_WRITE); -} - -static void set_memory_previliage(struct upatch_module *mod) -{ - orig_mprotect = (void *)sys_call_table_p[__NR_mprotect]; - frob_text(&mod->core_layout); - frob_rodata(&mod->core_layout); - frob_writable_data(&mod->core_layout); - frob_text(&mod->init_layout); - frob_rodata(&mod->init_layout); - frob_writable_data(&mod->init_layout); -} - -static int complete_formation(struct upatch_module *mod, struct inode *patch) -{ - set_memory_previliage(mod); - mod->real_state = UPATCH_STATE_RESOLVED; - mod->real_patch = patch; - return 0; -} - -/* The main idea is from insmod */ -int upatch_load(struct file *binary_file, struct inode *set_patch, - struct patch_entity *patch_entity, struct upatch_load_info *info) -{ - int err; - struct upatch_module *mod; - - if (patch_entity == NULL) { - pr_err("invalid patch entity \n"); - err = -EINVAL; - goto free_hdr; - } - - err = load_binary_syms(binary_file, &info->running_elf); - if (err) - goto free_hdr; - - info->running_elf.load_info = info; - - info->len = patch_entity->patch_size; - info->hdr = vmalloc(info->len); - if (!info->hdr) { - err = -ENOMEM; - goto free_hdr; - } - - /* read patch file into kernel memory */ - memcpy(info->hdr, patch_entity->patch_buff, info->len); - - err = patch_header_check(info); - if (err) { - pr_err("upatch has invalid ELF header"); - goto free_hdr; - } - - err = setup_load_info(info); - if (err) - goto free_hdr; - - /* update section address */ - err = rewrite_section_headers(info); - if (err) - goto free_hdr; - - mod = layout_and_allocate(info); - if (IS_ERR(mod)) { - err = PTR_ERR(mod); - goto free_hdr; - } - - /* after this step, everything should be in its final step */ - err = find_upatch_module_sections(mod, info); - if (err) - goto free_module; - - /* Fix up syms, so that st_value is a pointer to location. */ - err = simplify_symbols(mod, info); - if (err < 0) - goto free_module; - - /* upatch new address will be updated */ - err = apply_relocations(mod, info); - if (err < 0) - goto free_module; - - err = post_relocation(mod, info); - if (err < 0) - goto free_module; - - err = complete_formation(mod, set_patch); - if (err < 0) - goto free_module; - - pr_debug("patch load successfully \n"); - - err = 0; - - goto free_hdr; -free_module: - upatch_module_deallocate(mod); -free_hdr: - load_info_clear(info); - return err; -} diff --git a/upatch/upatch-manage/patch-syscall.c b/upatch/upatch-manage/patch-syscall.c deleted file mode 100644 index af2d227..0000000 --- a/upatch/upatch-manage/patch-syscall.c +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#include -#include - -#include "arch/patch-syscall.h" -#include "patch-syscall.h" - -#define MAX_CODE_SIZE PAGE_SIZE - -static void __user *alloc_code_mem(void *code, size_t len) -{ - void __user *code_buffer = NULL; - - code_buffer = (char __user *)vm_mmap(NULL, 0, len, - PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0); - if (IS_ERR(code_buffer)) { - pr_err("alloc user memory failed \n"); - goto out; - } - - if (copy_to_user(code_buffer, code, len)) { - pr_err("copy to user memory failed \n"); - vm_munmap((unsigned long)code_buffer, len); - goto out; - } - -out: - return code_buffer; -} - -int execve_syscall(struct pt_regs *regs, const char __user *pathname, - const char __user *const __user *argv, const char __user *const __user *envp) -{ - void __user *code = alloc_code_mem(__execve_syscall, MAX_CODE_SIZE); - if (!(const char __user *)code) - return -EFAULT; - - set_execve_syscall_registers(regs, code, pathname, argv, envp); - - return 0; -} - -int exit_syscall(struct pt_regs *regs, int exit_code) -{ - void __user *code = alloc_code_mem(__exit_syscall, MAX_CODE_SIZE); - if (!(const char __user *)code) - return -EFAULT; - - set_exit_syscall_registers(regs, code, exit_code); - - return 0; -} diff --git a/upatch/upatch-manage/patch-syscall.h b/upatch/upatch-manage/patch-syscall.h deleted file mode 100644 index 85dc160..0000000 --- a/upatch/upatch-manage/patch-syscall.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * renoseven - * - */ - -#ifndef _PATCH_SYSCALL_H -#define _PATCH_SYSCALL_H - -#include - -int execve_syscall(struct pt_regs *regs, const char __user *pathname, - const char __user *const __user *argv, const char __user *const __user *envp); - -int exit_syscall(struct pt_regs *regs, int exit_val); - -#endif /* _PATCH_SYSCALL_H */ diff --git a/upatch/upatch-manage/patch-uprobe.c b/upatch/upatch-manage/patch-uprobe.c deleted file mode 100644 index 13716c5..0000000 --- a/upatch/upatch-manage/patch-uprobe.c +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#include -#include -#include - -#include - -elf_addr_t calculate_load_address(struct file *file, bool check_code) -{ - int ret, size, i; - Elf_Ehdr elf_header; - Elf_Phdr *phdr = NULL; - elf_addr_t min_addr = -1; - loff_t offset = 0; - - ret = kernel_read(file, &elf_header, sizeof(elf_header), &offset); - if (ret != sizeof(elf_header)) { - pr_err("read elf header failed - %d \n", ret); - goto out; - } - - if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0) { - pr_err("provided path is not an ELF \n"); - goto out; - } - - /* TODO: for ET_DYN, consider check PIE */ - if (elf_header.e_type != ET_EXEC && elf_header.e_type != ET_DYN) { - pr_err("invalid elf type, it should be ET_EXEC or ET_DYN\n"); - goto out; - } - - size = sizeof(Elf_Phdr) * elf_header.e_phnum; - phdr = kmalloc(size, GFP_KERNEL); - if (!phdr) { - pr_err("kmalloc failed for load address calculate \n"); - goto out; - } - - ret = kernel_read(file, phdr, size, &elf_header.e_phoff); - if (ret < 0) { - pr_err("kernel read failed - %d \n", ret); - goto out; - } - - for (i = 0; i < elf_header.e_phnum; i ++) { - if (phdr[i].p_type != PT_LOAD) - continue; - if (!check_code || - (check_code && (phdr[i].p_flags & PF_X))) - min_addr = min(min_addr, phdr[i].p_vaddr); - } - -out: - if (phdr) - kfree(phdr); - return min_addr; -} diff --git a/upatch/upatch-manage/patch-uprobe.h b/upatch/upatch-manage/patch-uprobe.h deleted file mode 100644 index 19216c6..0000000 --- a/upatch/upatch-manage/patch-uprobe.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#ifndef _UPATCH_UPROBE_H -#define _UPATCH_UPROBE_H - -#include - -/* Common used functions for uprobe */ - -/* We don't utilize filter now */ -static inline bool uprobe_default_filter(struct uprobe_consumer *self, - enum uprobe_filter_ctx ctx, struct mm_struct *mm) -{ - return true; -} - -elf_addr_t calculate_load_address(struct file *, bool); - -#endif /* _UPATCH_UPROBE_H */ diff --git a/upatch/upatch-manage/patch.c b/upatch/upatch-manage/patch.c deleted file mode 100644 index c930e2e..0000000 --- a/upatch/upatch-manage/patch.c +++ /dev/null @@ -1,829 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#include -#include -#include -#include /* for MAX_ARG_STRLEN */ -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "patch.h" -#include "patch-uprobe.h" -#include "upatch-ioctl.h" -#include "upatch-patch.h" - -static DEFINE_MUTEX(upatch_entity_lock); -static LIST_HEAD(upatch_entity_list); - -/* lock for all patch entity, since we need free its memory */ -static DEFINE_SPINLOCK(patch_entity_lock); - -static struct upatch_entity *__get_upatch_entity(struct inode *uinode) -{ - struct upatch_entity *entity; - list_for_each_entry(entity, &upatch_entity_list, list) - /* binary / patch all can be the master key */ - if (entity->binary == uinode || entity->set_patch == uinode) - return entity; - return NULL; -} - -struct upatch_entity *upatch_entity_get(struct inode *uinode) -{ - struct upatch_entity *entity; - mutex_lock(&upatch_entity_lock); - entity = __get_upatch_entity(uinode); - mutex_unlock(&upatch_entity_lock); - return entity; -} - -void __remove_patch_entity(struct patch_entity *patch_entity) -{ - if (patch_entity->patch_buff) - vm_munmap((unsigned long)patch_entity->patch_buff, patch_entity->patch_size); - patch_entity->patch_buff = NULL; - patch_entity->patch_size = 0; - kfree(patch_entity); -} - -/* After put, holder should never use this memory */ -void upatch_put_patch_entity(struct patch_entity *patch_entity) -{ - if (patch_entity == NULL) - return; - - spin_lock(&patch_entity_lock); - patch_entity->ref --; - if (patch_entity->ref == 0) - __remove_patch_entity(patch_entity); - spin_unlock(&patch_entity_lock); -} - -void upatch_get_patch_entity(struct patch_entity *patch_entity) -{ - if (patch_entity == NULL) - return; - - spin_lock(&patch_entity_lock); - patch_entity->ref ++; - spin_unlock(&patch_entity_lock); -} - -int upatch_init_patch_entity(struct patch_entity *patch_entity, struct file *patch) -{ - int ret; - loff_t offset = 0; - - if (patch == NULL) - return 0; - - patch_entity->ref = 1; - patch_entity->patch_size = i_size_read(file_inode(patch)); - patch_entity->patch_buff = vmalloc(patch_entity->patch_size); - if (!patch_entity->patch_buff) - return -ENOMEM; - - ret = kernel_read(patch, patch_entity->patch_buff, patch_entity->patch_size, &offset); - if (ret != patch_entity->patch_size) { - pr_err("read patch file for entity failed. \n"); - vm_munmap((unsigned long)patch_entity->patch_buff, patch_entity->patch_size); - return -ENOEXEC; - } - return 0; -} - -static int __insert_upatch_entity(struct file *binary, struct file *patch) -{ - struct upatch_entity *entity = NULL; - int err; - - entity = kzalloc(sizeof(*entity), GFP_KERNEL); - if (!entity) - return -ENOMEM; - - entity->patch_entity = kzalloc(sizeof(*entity->patch_entity), GFP_KERNEL); - if (!entity->patch_entity) { - err = -ENOMEM; - goto err_out; - } - - err = upatch_init_patch_entity(entity->patch_entity, patch); - if (err) { - err = -ENOMEM; - goto err_out; - } - - entity->set_patch = file_inode(patch); - entity->set_status = UPATCH_STATE_ATTACHED; - entity->binary = file_inode(binary); - - mutex_init(&entity->entity_status_lock); - INIT_LIST_HEAD(&entity->offset_list); - INIT_LIST_HEAD(&entity->module_list); - - /* when everything is ok, add it to the list */ - list_add(&entity->list, &upatch_entity_list); - return 0; - -err_out: - if (entity && entity->patch_entity) - __remove_patch_entity(entity->patch_entity); - if (entity && entity->patch_entity) - kfree(entity->patch_entity); - if (entity) - kfree(entity); - return err; -} - -static int __update_upatch_entity(struct upatch_entity *entity, struct file *patch) -{ - int ret; - - if (patch == NULL) - return -EINVAL; - - /* upatch_entity_lock > entity_status_lock > patch_entity_lock */ - mutex_lock(&entity->entity_status_lock); - - upatch_put_patch_entity(entity->patch_entity); - entity->patch_entity = NULL; - - entity->patch_entity = kzalloc(sizeof(*entity->patch_entity), GFP_KERNEL); - ret = upatch_init_patch_entity(entity->patch_entity, patch); - if (ret != 0) { - kfree(entity->patch_entity); - goto out; - } - - entity->set_patch = file_inode(patch); - entity->set_status = UPATCH_STATE_ATTACHED; -out: - mutex_unlock(&entity->entity_status_lock); - return ret; -} - -static int update_upatch_entity(struct file *binary, struct file *patch) -{ - int ret; - struct upatch_entity *entity; - - if (!binary || !patch) - return -EINVAL; - - mutex_lock(&upatch_entity_lock); - entity = __get_upatch_entity(file_inode(binary)); - if (entity) - ret = __update_upatch_entity(entity, patch); - else - ret = __insert_upatch_entity(binary, patch); - mutex_unlock(&upatch_entity_lock); - return ret; -} - -/* no check for offset */ -static int __insert_uprobe_offset(struct upatch_entity *entity, loff_t offset) -{ - struct uprobe_offset *uo; - - uo = kzalloc(sizeof(*uo), GFP_KERNEL); - if (!uo) - return -ENOMEM; - - uo->offset = offset; - list_add(&uo->list, &entity->offset_list); - return 0; -} - -static int insert_uprobe_offset(struct upatch_entity *entity, loff_t offset) -{ - int ret = 0; - struct uprobe_offset *uo; - - mutex_lock(&entity->entity_status_lock); - list_for_each_entry(uo, &entity->offset_list, list) { - if (uo->offset == offset) { - ret = -EEXIST; - break; - } - } - - if (ret == 0) - ret = __insert_uprobe_offset(entity, offset); - mutex_unlock(&entity->entity_status_lock); - return ret; -} - -static int read_build_id(struct file *file, struct elf_build_id *build_id) -{ - int ret = 0; - int index; - loff_t offset; - int buf_len; - Elf_Ehdr ehdr; - Elf_Shdr *eshdrs = NULL; - char* shstr = NULL; - char* name = NULL; - - offset = 0; - buf_len = sizeof(Elf_Ehdr); - ret = kernel_read(file, &ehdr, buf_len, &offset); - if (ret != buf_len) { - pr_err("read file failed - %d \n", ret); - ret = -EINVAL; - goto out; - } - - buf_len = sizeof(Elf_Shdr) * ehdr.e_shnum; - eshdrs = kmalloc(buf_len, GFP_KERNEL); - if (!eshdrs) { - ret = -ENOMEM; - goto out; - } - - offset = ehdr.e_shoff; - ret = kernel_read(file, eshdrs, buf_len, &offset); - if (ret != buf_len) { - pr_err("read patch section header failed - %d \n", ret); - ret = -EINVAL; - goto out; - } - - pr_debug("section string table index %d at %lld \n", ehdr.e_shstrndx, eshdrs[ehdr.e_shstrndx].sh_offset); - - /* read string table for section header table */ - buf_len = eshdrs[ehdr.e_shstrndx].sh_size; - shstr = kmalloc(buf_len, GFP_KERNEL); - if (!shstr) { - ret = -ENOMEM; - goto out; - } - - offset = eshdrs[ehdr.e_shstrndx].sh_offset; - ret = kernel_read(file, shstr, buf_len, &offset); - if (ret != buf_len) { - pr_err("read string table failed - %d \n", ret); - ret = -EINVAL; - goto out; - } - - pr_debug("total section number : %d \n", ehdr.e_shnum); - for (index = 0; index < ehdr.e_shnum; index ++) { - if (eshdrs[index].sh_name == 0) - continue; - - name = shstr + eshdrs[index].sh_name; - if (!strcmp(name, ".note.gnu.build-id") != 0) - break; - } - - if (index == ehdr.e_shnum) { - ret = -EINVAL; - goto out; - } - - offset = eshdrs[index].sh_offset; - buf_len = sizeof(build_id->head); - if (buf_len >= eshdrs[index].sh_size) { - pr_err(".note.gnu.build-id section is failed \n"); - ret = -EINVAL; - goto out; - } - - ret = kernel_read(file, &build_id->head, buf_len, &offset); - if (ret != buf_len) { - pr_err("read .note.gnu.build-id failed - %d \n", ret); - ret = -EINVAL; - goto out; - } - - buf_len = build_id->head.nhdr.n_descsz; - offset = eshdrs[index].sh_offset + sizeof(build_id->head); - build_id->id = kmalloc(buf_len, GFP_KERNEL); - if (!build_id->id) { - ret = -ENOMEM; - goto out; - } - - ret = kernel_read(file, build_id->id, buf_len, &offset); - if (ret != buf_len) { - pr_err("read .build-id failed - %d \n", ret); - ret = -EINVAL; - goto out; - } - ret = 0; -out: - if (shstr) - kfree(shstr); - if (eshdrs) - kfree(eshdrs); - return ret; -} - -static int check_upatch(Elf_Ehdr *ehdr, struct file *patch_file, struct file *binary_file) -{ - int ret = -EINVAL; - struct elf_build_id patch_id; - struct elf_build_id binary_id; - - memset(&patch_id, 0, sizeof(patch_id)); - memset(&binary_id, 0, sizeof(binary_id)); - - if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) - goto out; - - if (ehdr->e_type != ET_REL) - goto out; - - if (ehdr->e_shentsize != sizeof(Elf_Shdr)) - goto out; - - ret = read_build_id(patch_file, &patch_id); - if (ret) { - pr_err("read patch's build id failed - %d \n", ret); - goto out; - } - - ret = read_build_id(binary_file, &binary_id); - if (ret) { - pr_err("read binary's build id failed - %d \n", ret); - goto out; - } - - if (memcmp(patch_id.id, binary_id.id, binary_id.head.nhdr.n_descsz) != 0) { - pr_err("build id is different.\n"); - ret = -EINVAL; - goto out; - } - - ret = 0; -out: - if (patch_id.id) - kfree(patch_id.id); - if (binary_id.id) - kfree(binary_id.id); - return ret; -} - -static int do_module_active(struct upatch_module *module, struct pt_regs *regs) -{ - struct upatch_patch_func *upatch_funs; - unsigned int i; - unsigned long pc; - bool set_pc = false; - int ret = 0; - - upatch_funs = kzalloc(sizeof(struct upatch_patch_func) * module->num_upatch_funcs, - GFP_KERNEL); - if (!upatch_funs) { - pr_err("malloc upatch funcs failed \n"); - goto out; - } - - if (copy_from_user(upatch_funs, module->upatch_funs, - sizeof(struct upatch_patch_func) * module->num_upatch_funcs)) { - pr_err("copy from user failed \n"); - goto out; - } - - pc = instruction_pointer(regs); - for (i = 0; i < module->num_upatch_funcs; i ++) { - if (pc == upatch_funs[i].old_addr + module->load_bias) { - pc = upatch_funs[i].new_addr; - instruction_pointer_set(regs, pc); - pr_debug("jmp to patched address 0x%lx \n", pc); - set_pc = true; - break; - } - } - - if (!set_pc) { - pr_err("unable to activate the patch, no address found \n"); - goto out; - } - - ret = UPROBE_ALTER_PC; - -out: - if (upatch_funs) - kfree(upatch_funs); - return ret; -} - -/* TODO: check modules that doesn't live anymore */ -static int do_module_remove(struct upatch_entity *entity, - struct upatch_module *module) -{ - if (module->real_state == UPATCH_STATE_REMOVED) - return 0; - - module->real_state = UPATCH_STATE_REMOVED; - module->real_patch = NULL; - /* TODO: when remove, check function stack!!!!! */ - upatch_module_deallocate(module); - - /* TODO: currently, remove is only a mark flag */ - // upatch_module_remove(entity, module); - // upatch_entity_try_remove(entity); - - return 0; -} - -static int uprobe_patch_handler(struct uprobe_consumer *self, struct pt_regs *regs) -{ - unsigned long pc; - elf_addr_t min_addr; - - int ret = 0; - struct upatch_load_info info; - struct upatch_entity *entity = NULL; - struct upatch_module *upatch_mod = NULL; - struct file *binary_file = NULL; - struct vm_area_struct *vma_binary = NULL; - - bool need_resolve = false; - bool need_active = false; - - enum upatch_module_state set_status; - struct inode *set_patch; - struct patch_entity *patch_entity = NULL; - - pc = instruction_pointer(regs); - - pr_debug("patch handler works in 0x%lx \n", pc); - - memset(&info, 0, sizeof(info)); - - vma_binary = find_vma(current->mm, pc); - if (!vma_binary || !vma_binary->vm_file) { - pr_err("no exe file found for upatch \n"); - goto out; - } - - binary_file = vma_binary->vm_file; - /* calculate load bias for binary file */ - min_addr = calculate_load_address(binary_file, true); - if (min_addr == -1) { - pr_err("unable to obtain minimal execuatable address \n"); - goto out; - } - - /* Attention: it could be wrong when it has mutiple executable vma */ - info.running_elf.load_start = vma_binary->vm_start; - info.running_elf.load_bias = vma_binary->vm_start - min_addr; - pr_debug("load bias for pid %d is 0x%lx \n", - task_pid_nr(current), info.running_elf.load_bias); - - entity = upatch_entity_get(file_inode(binary_file)); - if (!entity) { - pr_err("No entity found in patch handler \n"); - goto out; - } - - mutex_lock(&entity->entity_status_lock); - set_status = entity->set_status; - set_patch = entity->set_patch; - patch_entity = entity->patch_entity; - upatch_get_patch_entity(patch_entity); - mutex_unlock(&entity->entity_status_lock); - - upatch_mod = upatch_module_get_or_create(entity, task_pid_nr(current)); - if (!upatch_mod) { - pr_err("found module failed \n"); - goto out; - } - - /* Now, all actions happens within module lock */ - mutex_lock(&upatch_mod->module_status_lock); - - /* if not set_patch, clear exist patch */ - if (upatch_mod->real_patch != set_patch) - do_module_remove(entity, upatch_mod); - - pr_debug("status from %d to %d \n", upatch_mod->real_state, set_status); - - info.mod = upatch_mod; - switch (set_status) - { - case UPATCH_STATE_ACTIVED: - if (upatch_mod->real_state < UPATCH_STATE_ACTIVED) - need_active = true; - fallthrough; - case UPATCH_STATE_RESOLVED: - case UPATCH_STATE_ATTACHED: - if (upatch_mod->real_state < UPATCH_STATE_RESOLVED) - need_resolve = true; - break; - case UPATCH_STATE_REMOVED: - do_module_remove(entity, upatch_mod); - goto out_unlock; - default: - pr_err("invalid upatch status \n"); - break; - } - - /* we can be sure module->real_patch == set_patch/NULL */ - if (need_resolve && upatch_load(binary_file, set_patch, patch_entity, &info)) { - pr_err("load patch failed \n"); - goto out_unlock; - } - - if (need_active) { - ret = do_module_active(upatch_mod, regs); - goto out_unlock; - } - -out_unlock: - mutex_unlock(&upatch_mod->module_status_lock); -out: - upatch_put_patch_entity(patch_entity); - return ret; -} - -static struct uprobe_consumer patch_consumber = { - .handler = uprobe_patch_handler, - .ret_handler = NULL, - .filter = uprobe_default_filter, -}; - -/* - * shoule we check if it is the entry of the function ? - */ -static int register_patch_uprobe(struct file *binary_file, loff_t offset) -{ - int ret; - struct inode *inode; - struct upatch_entity *entity; - - inode = file_inode(binary_file); - entity = upatch_entity_get(inode); - if (!entity) - return -ENOENT; - - ret = insert_uprobe_offset(entity, offset); - if (ret) - return ret; - - ret = uprobe_register(inode, offset, &patch_consumber); - if (ret) { - pr_err("patch uprobe register failed - %d \n", ret); - return ret; - } - - pr_info("register patch uprobe for %ld, offset: %lx\n", inode->i_ino, (unsigned long)offset); - return 0; -} - -/* - * find valid entry points for applying patch. - * no matter which point hits, it will active the whole patch. - */ -static int handle_upatch_funcs(struct file *binary_file, struct file *patch_file, - Elf_Shdr *upatch_shdr) -{ - int buf_len; - int ret; - int index; - loff_t offset; - unsigned long old_addr; - elf_addr_t min_addr; - struct upatch_patch_func *upatch_funs = NULL; - - /* TODO: sh_entsize becomes 0 after ld -r, skip this problem now */ - upatch_shdr->sh_entsize = sizeof(struct upatch_patch_func); - - if (upatch_shdr->sh_entsize != sizeof(struct upatch_patch_func)) { - pr_err("invalid section size for upatch func section %llu - %lu \n", - upatch_shdr->sh_entsize, sizeof(struct upatch_patch_func)); - return -EINVAL; - } - - buf_len = upatch_shdr->sh_size; - upatch_funs = kmalloc(buf_len, GFP_KERNEL); - if (!upatch_funs) - return -ENOMEM; - - offset = upatch_shdr->sh_offset; - ret = kernel_read(patch_file, upatch_funs, buf_len, &offset); - if (ret != buf_len) { - pr_err("read upatch funcs failed- %d \n", ret); - ret = -EINVAL; - goto out; - } - - min_addr = calculate_load_address(binary_file, false); - if (min_addr == -1) { - ret = -EINVAL; - goto out; - } - - /* TODO: if failed, we need clean this entity */ - /* TODO: check if other patch has taken effect */ - /* before uprobe works, we must set upatch entity first */ - ret = update_upatch_entity(binary_file, patch_file); - if (ret) { - pr_err("update upatch entity failed - %d \n", ret); - goto out; - } - - pr_debug("load address is 0x%llx \n", min_addr); - for (index = 0; index < upatch_shdr->sh_size / upatch_shdr->sh_entsize; index ++) { - old_addr = upatch_funs[index].old_addr; - ret = register_patch_uprobe(binary_file, old_addr - min_addr); - if (ret && ret != -EEXIST) - goto out; - } - -out: - if (upatch_funs) - kfree(upatch_funs); - return 0; -} - -int check_entity(struct upatch_entity *entity) -{ - int ret = 0; - mutex_lock(&entity->entity_status_lock); - if (entity->set_patch != NULL || entity->set_status != UPATCH_STATE_REMOVED) - ret = -EPERM; - mutex_unlock(&entity->entity_status_lock); - return ret; -} - -/* - * TODO: - * 1. handle SHN_LORESERVE - * 2. check elf arch and abi - */ -int upatch_attach(const char *binary, const char *patch) -{ - int ret = 0; - int index; - loff_t offset; - int buf_len; - Elf_Ehdr ehdr; - Elf_Shdr *eshdrs = NULL; - char *shstr = NULL; - char *name = NULL; - struct file *binary_file = NULL; - struct file *patch_file = NULL; - struct upatch_entity *entity = NULL; - - binary_file = filp_open(binary, O_RDONLY, 0); - if (IS_ERR(binary_file)) { - ret = PTR_ERR(binary_file); - pr_err("open binary failed - %d \n", ret); - goto out; - } - - patch_file = filp_open(patch, O_RDONLY, 0); - if (IS_ERR(patch_file)) { - ret = PTR_ERR(patch_file); - pr_err("open patch failed - %d \n", ret); - goto out; - } - - entity = upatch_entity_get(file_inode(binary_file)); - /* not first time to handle this binary */ - if (entity && check_entity(entity)) { - pr_err("need to remove exist patch first \n"); - ret = -EPERM; - goto out; - } - - offset = 0; - buf_len = sizeof(Elf_Ehdr); - ret = kernel_read(patch_file, &ehdr, buf_len, &offset); - if (ret != buf_len) { - pr_err("read patch header failed - %d \n", ret); - ret = -EINVAL; - goto out; - } - - ret = check_upatch(&ehdr, patch_file, binary_file); - if (ret) { - pr_err("check upatch failed - %d \n", ret); - ret = -EINVAL; - goto out; - } - - pr_debug("patch has %d sections at %lld \n", ehdr.e_shnum, ehdr.e_shoff); - /* read section header table */ - buf_len = sizeof(Elf_Shdr) * ehdr.e_shnum; - eshdrs = kmalloc(buf_len, GFP_KERNEL); - if (!eshdrs) { - ret = -ENOMEM; - goto out; - } - - offset = ehdr.e_shoff; - ret = kernel_read(patch_file, eshdrs, buf_len, &offset); - if (ret != buf_len) { - pr_err("read patch section header failed - %d \n", ret); - ret = -EINVAL; - goto out; - } - - pr_debug("section string table index %d at %lld \n", ehdr.e_shstrndx, eshdrs[ehdr.e_shstrndx].sh_offset); - - /* read string table for section header table */ - buf_len = eshdrs[ehdr.e_shstrndx].sh_size; - shstr = kmalloc(buf_len, GFP_KERNEL); - if (!shstr) { - ret = -ENOMEM; - goto out; - } - - offset = eshdrs[ehdr.e_shstrndx].sh_offset; - ret = kernel_read(patch_file, shstr, buf_len, &offset); - if (ret != buf_len) { - pr_err("read string table failed - %d \n", ret); - ret = -EINVAL; - goto out; - } - - pr_debug("total section number : %d \n", ehdr.e_shnum); - for (index = 0; index < ehdr.e_shnum; index ++) { - if (eshdrs[index].sh_name == 0) - continue; - - name = shstr + eshdrs[index].sh_name; - if (strncmp(name, ".upatch.funcs", 13) != 0) - continue; - - pr_debug("upatch section index is %d \n", index); - ret = handle_upatch_funcs(binary_file, patch_file, &eshdrs[index]); - if (ret) - pr_err("handle upatch failed - %d \n", ret); - goto out; - } - - ret = 0; -out: - if (shstr) - kfree(shstr); - if (eshdrs) - kfree(eshdrs); - if (patch_file && !IS_ERR(patch_file)) - filp_close(patch_file, NULL); - if (binary_file && !IS_ERR(binary_file)) - filp_close(binary_file, NULL); - return ret; -} - -static void upatch_entity_remove(struct upatch_entity *entity) { - struct uprobe_offset *uprobe_offset, *uprobe_tmp; - struct upatch_module *upatch_module, *upatch_module_tmp; - - if (!entity) - return; - - mutex_lock(&entity->entity_status_lock); - - list_for_each_entry_safe(upatch_module, upatch_module_tmp, &entity->module_list, list) { - do_module_remove(entity, upatch_module); - list_del(&upatch_module->list); - kfree(upatch_module); - } - - list_for_each_entry_safe(uprobe_offset, uprobe_tmp, &entity->offset_list, list) { - uprobe_unregister(entity->binary, uprobe_offset->offset, &patch_consumber); - pr_info("unregister patch uprobe for %ld, offset: %lx\n", entity->binary->i_ino, (unsigned long)uprobe_offset->offset); - list_del(&uprobe_offset->list); - kfree(uprobe_offset); - } - - upatch_put_patch_entity(entity->patch_entity); - - entity->patch_entity = NULL; - entity->binary = NULL; - entity->set_patch = NULL; - entity->set_status = UPATCH_STATE_REMOVED; - - mutex_unlock(&entity->entity_status_lock); -} - -void upatch_exit() { - struct upatch_entity *upatch_entity, *upatch_entity_tmp; - mutex_lock(&upatch_entity_lock); - - list_for_each_entry_safe(upatch_entity, upatch_entity_tmp, &upatch_entity_list, list) { - upatch_entity_remove(upatch_entity); - list_del(&upatch_entity->list); - kfree(upatch_entity); - } - - mutex_unlock(&upatch_entity_lock); -} diff --git a/upatch/upatch-manage/patch.h b/upatch/upatch-manage/patch.h deleted file mode 100644 index fce133b..0000000 --- a/upatch/upatch-manage/patch.h +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#ifndef _UPATCH_PATCH_H -#define _UPATCH_PATCH_H - -#include - -#include - -#include "upatch-patch.h" -#include "upatch-manage.h" -#include "arch/patch-load.h" - -#define STT_IFUNC 0xa // when e_ident[EI_OSABI] == ELFOSABI_GNU/ELFOSABI_FREEBSD -#define ELFOSABI_GNU 0x3 -#define ELFOSABI_FREEBSD 0x9 - -/* - * When patch works, it will no longer be controlled by the uprobe. - * But we still need uprobe works in this situation to handler further threads. - * Status definiations for threads: - * None: original status - * ----------------------- - * Attached: register the uprobe handler - * Hacked: finish relocations - * Actived: jmp instructions works (release hack) --> wait for futher command + safety check - * Unactived: no jmp instructions (re-gain hack) --> register again ? - * Removed: unregister the uprobe handler -> actived threads will be restored? - * - * used for the binary: apply / remove - * middle status: mmap - * used for the thread: actived / unactived - * - * limit: self-modifications for funcs are forbidden. - */ - -#define JMP_TABLE_MAX_ENTRY 100 - -/* memory layout for module */ -struct upatch_module_layout { - /* The actual code + data. */ - void *kbase; - void __user *base; - /* Total size. */ - unsigned int size; - /* The size of the executable code. */ - unsigned int text_size; - /* Size of RO section of the module (text+rodata) */ - unsigned int ro_size; - /* Size of RO after init section, not use it now */ - unsigned int ro_after_init_size; -}; - -/* information to manage a patch module */ -struct upatch_module { - pid_t pid; - struct list_head list; - - struct mutex module_status_lock; - unsigned long load_bias; - - /* state changes happens asynchronously */ - enum upatch_module_state real_state; - struct inode *real_patch; - - /* memory layout for patch */ - struct upatch_module_layout core_layout; - /* drop after init, we use it to store symtab and strtab */ - struct upatch_module_layout init_layout; - - /* address from module layout, consider record in memory */ - struct upatch_patch_func __user *upatch_funs; - unsigned int num_upatch_funcs; - char __user *strtab; - Elf_Sym __user *syms; - unsigned int num_syms; -}; - -struct uprobe_offset { - loff_t offset; - struct list_head list; -}; - -struct patch_entity { - unsigned int ref; - void *patch_buff; - size_t patch_size; -}; - -struct upatch_entity { - struct inode *binary; - struct list_head list; - - /* protect any modification for this entity */ - struct mutex entity_status_lock; - - /* used to handle command */ - enum upatch_module_state set_status; - struct inode *set_patch; - - /* sync with set_patch */ - struct patch_entity *patch_entity; - - struct list_head offset_list; - struct list_head module_list; -}; - -struct upatch_load_info; -/* information needed to load running binary */ -struct running_elf_info { - unsigned long len; - Elf_Ehdr *hdr; - Elf_Shdr *sechdrs; - Elf_Phdr *prohdrs; - Elf64_Xword tls_size; - Elf64_Xword tls_align; - char *secstrings, *strtab, *dynstrtab; - /* start address of this block, used to find the pole */ - unsigned long load_start; - /* load bias, used to handle ASLR */ - unsigned long load_bias; - struct { - unsigned int sym, symstr; - unsigned int dynsym, dynsymstr; - unsigned int relaplt, reladyn; - } index; - struct upatch_load_info *load_info; -}; - -/* information for loading */ -struct upatch_load_info { - unsigned long len; - Elf_Ehdr *hdr; - Elf_Shdr *sechdrs; - char *secstrings, *strtab; - unsigned long symoffs, stroffs, core_typeoffs; - unsigned long jmp_offs; - unsigned int jmp_cur_entry, jmp_max_entry; - struct { - unsigned int sym, str; - } index; - struct upatch_module *mod; - struct running_elf_info running_elf; -}; - -struct elf_build_id { - struct { - Elf64_Nhdr nhdr; - char name[4]; - } head; - uint8_t *id; -}; - -/* entity/module releated */ -struct upatch_entity *upatch_entity_get(struct inode *); -struct upatch_module *upatch_module_get_or_create(struct upatch_entity *, pid_t); -void upatch_module_deallocate(struct upatch_module *); -void upatch_put_patch_entity(struct patch_entity *); -void upatch_get_patch_entity(struct patch_entity *); -void upatch_exit(void); - -/* management releated */ -int upatch_attach(const char *, const char *); -int upatch_load(struct file *, struct inode *, struct patch_entity *, - struct upatch_load_info *); - -#endif /* _UPATCH_PATCH_H */ diff --git a/upatch/upatch-manage/upatch-common.c b/upatch/upatch-manage/upatch-common.c new file mode 100644 index 0000000..54e3845 --- /dev/null +++ b/upatch/upatch-manage/upatch-common.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#include + +#include "upatch-common.h" + +bool streql(const char *a, const char *b) +{ + return strlen(a) == strlen(b) && !strncmp(a, b, strlen(a)); +} \ No newline at end of file diff --git a/upatch/upatch-manage/upatch-common.h b/upatch/upatch-manage/upatch-common.h new file mode 100644 index 0000000..5a216d3 --- /dev/null +++ b/upatch/upatch-manage/upatch-common.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#ifndef __UPATCH_COMMON__ +#define __UPATCH_COMMON__ + +#include + +#define ALLOC_LINK(_new, _list) \ + { \ + (_new) = calloc(1, sizeof(*(_new))); \ + if (!(_new)) \ + ERROR("calloc"); \ + INIT_LIST_HEAD(&(_new)->list); \ + if (_list) \ + list_add(&(_new)->list, (_list)); \ + } + +static inline int page_shift(int n) +{ + int res = -1; + + while (n) { + res++; + n >>= 1; + } + + return res; +} + +#ifndef PAGE_SIZE +#define PAGE_SIZE sysconf(_SC_PAGE_SIZE) +#define PAGE_MASK (~(PAGE_SIZE - 1)) +#define PAGE_SHIFT page_shift(PAGE_SIZE) +#endif +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define ALIGN(x, a) (((x) + (a)-1) & (~((a)-1))) +#define PAGE_ALIGN(x) ALIGN((x), PAGE_SIZE) + +#define ROUND_DOWN(x, m) ((x) & ~((m)-1)) +#define ROUND_UP(x, m) (((x) + (m)-1) & ~((m)-1)) + +#define BIT(x) (1UL << (x)) + +bool streql(const char *, const char *); + +#endif \ No newline at end of file diff --git a/upatch/upatch-manage/upatch-elf.c b/upatch/upatch-manage/upatch-elf.c new file mode 100644 index 0000000..edcafe0 --- /dev/null +++ b/upatch/upatch-manage/upatch-elf.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "upatch-common.h" +#include "upatch-elf.h" +#include "upatch-ptrace.h" + +static int read_from_offset(int fd, void **buf, int len, off_t offset) +{ + int ret = -1; + size_t size; + + *buf = malloc(len); + if (*buf == NULL) { + printf("malloc failed \n"); + goto out; + } + + size = pread(fd, *buf, len, offset); + if (size == -1) { + ret = -errno; + printf("read file failed - %d \n", -ret); + goto out; + } + + ret = 0; +out: + return ret; +} + +static int open_elf(struct elf_info *einfo, const char *name) +{ + int ret = 0, fd = -1, i; + char *sec_name; + struct stat st; + + // TODO: check ELF + fd = open(name, O_RDONLY); + if (fd == -1) + ERROR("open %s failed with errno %d \n", name, errno); + + ret = stat(name, &st); + if (ret) + ERROR("get %s stat failed with errno %d \n", name, errno); + + ret = read_from_offset(fd, (void **)&einfo->patch_buff, st.st_size, 0); + if (ret) + goto out; + + einfo->name = name; + einfo->inode = st.st_ino; + einfo->patch_size = st.st_size; + einfo->hdr = (void *)einfo->patch_buff; + einfo->shdrs = (void *)einfo->hdr + einfo->hdr->e_shoff; + einfo->shstrtab = (void *)einfo->hdr + + einfo->shdrs[einfo->hdr->e_shstrndx].sh_offset; + + for (i = 0; i < einfo->hdr->e_shnum; ++i) { + sec_name = einfo->shstrtab + einfo->shdrs[i].sh_name; + if (streql(sec_name, BUILD_ID_NAME) && + einfo->shdrs[i].sh_type == SHT_NOTE) { + einfo->num_build_id = i; + break; + } + } + + if (einfo->num_build_id == 0) { + ret = -EINVAL; + log_error("no %s found \n", BUILD_ID_NAME); + goto out; + } + + log_error("no %ld found \n", einfo->inode); + + ret = 0; +out: + if (fd != -1) + close(fd); + return ret; +} + +int upatch_init(struct upatch_elf *uelf, const char *name) +{ + int ret = 0, i; + char *sec_name; + + memset(uelf, 0, sizeof(struct upatch_elf)); + + ret = open_elf(&uelf->info, name); + if (ret) + goto out; + + for (i = 1; i < uelf->info.hdr->e_shnum; ++i) { + sec_name = uelf->info.shstrtab + uelf->info.shdrs[i].sh_name; + if (uelf->info.shdrs[i].sh_type == SHT_SYMTAB) { + uelf->num_syms = + uelf->info.shdrs[i].sh_size / sizeof(GElf_Sym); + uelf->index.sym = i; + uelf->index.str = uelf->info.shdrs[i].sh_link; + uelf->strtab = + (char *)uelf->info.hdr + + uelf->info.shdrs[uelf->info.shdrs[i].sh_link] + .sh_offset; + } else if (streql(sec_name, UPATCH_FUNC_NAME)) { + uelf->index.upatch_funcs = i; + } + } + + ret = 0; + +out: + return ret; +} + +int binary_init(struct running_elf *relf, const char *name) +{ + int ret = 0, i; + char *sec_name; + + memset(relf, 0, sizeof(struct running_elf)); + + ret = open_elf(&relf->info, name); + if (ret) + goto out; + + relf->phdrs = (void *)relf->info.hdr + relf->info.hdr->e_phoff; + + for (i = 1; i < relf->info.hdr->e_shnum; i++) { + sec_name = relf->info.shstrtab + relf->info.shdrs[i].sh_name; + if (relf->info.shdrs[i].sh_type == SHT_SYMTAB) { + relf->num_syms = + relf->info.shdrs[i].sh_size / sizeof(GElf_Sym); + relf->index.sym = i; + relf->index.str = relf->info.shdrs[i].sh_link; + relf->strtab = + (char *)relf->info.hdr + + relf->info.shdrs[relf->info.shdrs[i].sh_link] + .sh_offset; + } else if (relf->info.shdrs[i].sh_type == SHT_DYNSYM) { + relf->index.dynsym = i; + relf->index.dynstr = relf->info.shdrs[i].sh_link; + relf->dynstrtab = + (char *)relf->info.hdr + + relf->info.shdrs[relf->info.shdrs[i].sh_link] + .sh_offset; + log_debug("found dynsym with %d \n", i); + } else if (relf->info.shdrs[i].sh_type == SHT_DYNAMIC) { + /* Currently, we don't utilize it */ + } else if (streql(sec_name, PLT_RELA_NAME) && + relf->info.shdrs[i].sh_type == SHT_RELA) { + relf->index.rela_plt = i; + log_debug("found %s with %d \n", PLT_RELA_NAME, i); + } else if (streql(sec_name, GOT_RELA_NAME) && + relf->info.shdrs[i].sh_type == SHT_RELA) { + relf->index.rela_dyn = i; + log_debug("found %s with %d \n", GOT_RELA_NAME, i); + } + } + + for (i = 0; i < relf->info.hdr->e_phnum; i++) { + if (relf->phdrs[i].p_type == PT_TLS) { + relf->tls_size = relf->phdrs[i].p_memsz; + relf->tls_align = relf->phdrs[i].p_align; + log_debug("found TLS size = %ld, memsz = %ld \n", + relf->tls_size, relf->tls_align); + break; + } + } + + ret = 0; + +out: + return ret; +} + +bool check_build_id(struct elf_info *uelf, struct elf_info *relf) +{ + return uelf->shdrs[uelf->num_build_id].sh_size == + relf->shdrs[relf->num_build_id].sh_size && + !memcmp(uelf->hdr + uelf->shdrs[uelf->num_build_id].sh_offset, + relf->hdr + relf->shdrs[relf->num_build_id].sh_offset, + uelf->shdrs[uelf->num_build_id].sh_size); +} + +void binary_close(struct running_elf *relf) +{ + // TODO: free relf + if (relf->info.patch_buff) + free(relf->info.patch_buff); +} + +void upatch_close(struct upatch_elf *uelf) +{ + // TODO: free uelf + if (uelf->info.patch_buff) + free(uelf->info.patch_buff); + + if (uelf->core_layout.kbase) + free(uelf->core_layout.kbase); +} + +bool is_upatch_section(const char *name) +{ + return !strncmp(name, ".upatch.", strlen(".upatch.")); +} + +bool is_note_section(GElf_Word type) +{ + return type == SHT_NOTE; +} diff --git a/upatch/upatch-manage/upatch-elf.h b/upatch/upatch-manage/upatch-elf.h new file mode 100644 index 0000000..c776c39 --- /dev/null +++ b/upatch/upatch-manage/upatch-elf.h @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#ifndef __UPATCH_FILE__ +#define __UPATCH_FILE__ + +#include +#include +#include +#include + +#include "list.h" + +#define GOT_RELA_NAME ".rela.dyn" +#define PLT_RELA_NAME ".rela.plt" +#define BUILD_ID_NAME ".note.gnu.build-id" +#define UPATCH_FUNC_NAME ".upatch.funcs" +#define TDATA_NAME ".tdata" +#define TBSS_NAME ".tbss" + +#define JMP_TABLE_MAX_ENTRY 100 +#define UPATCH_HEADER "UPATCH" +#define UPATCH_HEADER_LEN 6 +#define UPATCH_ID_LEN 40 + +struct upatch_info_func { + unsigned long old_addr; + unsigned long new_addr; + unsigned long old_insn; + unsigned long new_insn; +}; + +struct upatch_info { + char magic[7]; // upatch magic + char id[UPATCH_ID_LEN + 1]; // upatch id + unsigned long size; // upatch_info and upatch_info_func size + unsigned long start; // upatch vma start + unsigned long end; // upatch vma end + unsigned int changed_func_num; + // upatch_header_func +}; + +struct upatch_layout { + /* The actual code + data. */ + void *kbase; + void *base; + /* Total size. */ + unsigned int size; + /* The size of the executable code. */ + unsigned int text_size; + /* Size of RO section of the module (text+rodata) */ + unsigned int ro_size; + /* Size of RO after init section, not use it now */ + unsigned int ro_after_init_size; + /* The size of the info. */ + unsigned int info_size; +}; + +struct upatch_patch_func { + unsigned long new_addr; + unsigned long new_size; + unsigned long old_addr; + unsigned long old_size; + unsigned long sympos; /* handle local symbols */ + char *name; +}; + +struct elf_info { + const char *name; + ino_t inode; + void *patch_buff; + size_t patch_size; + + GElf_Ehdr *hdr; + GElf_Shdr *shdrs; + char *shstrtab; + + unsigned int num_build_id; +}; + +struct running_elf { + struct elf_info info; + + unsigned long num_syms; + char *strtab; + char *dynstrtab; + + GElf_Phdr *phdrs; + GElf_Xword tls_size; + GElf_Xword tls_align; + + struct { + unsigned int sym, str; + unsigned int rela_dyn, rela_plt; + unsigned int dynsym, dynstr; + } index; + + /* load bias, used to handle ASLR */ + unsigned long load_bias; + unsigned long load_start; +}; + +struct upatch_elf { + struct elf_info info; + + unsigned long num_syms; + char *strtab; + + struct { + unsigned int sym, str; + unsigned int upatch_funcs; + } index; + + unsigned long symoffs, stroffs, core_typeoffs; + unsigned long jmp_offs; + unsigned int jmp_cur_entry, jmp_max_entry; + + /* memory layout for patch */ + struct upatch_layout core_layout; + + struct running_elf *relf; +}; + +int upatch_init(struct upatch_elf *, const char *); +int binary_init(struct running_elf *, const char *); +void upatch_close(struct upatch_elf *); +void binary_close(struct running_elf *); + +bool check_build_id(struct elf_info *, struct elf_info *); + +bool is_upatch_section(const char *); + +bool is_note_section(GElf_Word); + +#endif diff --git a/upatch/upatch-manage/upatch-ioctl.h b/upatch/upatch-manage/upatch-ioctl.h deleted file mode 100644 index dd4974e..0000000 --- a/upatch/upatch-manage/upatch-ioctl.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#ifndef _UPATCH_IOCTL_H -#define _UPATCH_IOCTL_H - -/* ATTENTION: This head file is exported to userspace */ -#include -#include - -struct upatch_conmsg { - const char *binary; - const char *patch; -}; - -#define UPATCH_DEV_NAME "upatch" - -#define UPATCH_IOCTL_MAGIC 0xE5 - -/* when apply: patch information will be recored to the context of the process */ -#define UPATCH_ATTACH_PATCH _IOW(UPATCH_IOCTL_MAGIC, 0x7, const struct upatch_conmsg *) - -#define UPATCH_REMOVE_PATCH _IOW(UPATCH_IOCTL_MAGIC, 0x8, const char *) - -#define UPATCH_ACTIVE_PATCH _IOW(UPATCH_IOCTL_MAGIC, 0x9, const char *) - -/* deactive the jmp instruction but do not remove */ -#define UPATCH_DEACTIVE_PATCH _IOW(UPATCH_IOCTL_MAGIC, 0xA, const char *) - -#define UPATCH_INFO_PATCH _IOW(UPATCH_IOCTL_MAGIC, 0xB, const char *) - -#endif /* _UPATCH_IOCTL_H */ diff --git a/upatch/upatch-manage/upatch-manage.c b/upatch/upatch-manage/upatch-manage.c new file mode 100644 index 0000000..92105d8 --- /dev/null +++ b/upatch/upatch-manage/upatch-manage.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "upatch-elf.h" +#include "upatch-patch.h" + +enum loglevel loglevel = NORMAL; +char *logprefix; + +#define COMMAND_SIZE 4 +char *command[COMMAND_SIZE] = { "", "patch", "unpatch", "info" }; +enum Command { + DEFAULT, + PATCH, + UNPATCH, + INFO, +}; + +struct arguments { + int cmd; + int pid; + char *upatch; + char *binary; + bool verbose; +}; + +static struct argp_option options[] = { + { "verbose", 'v', NULL, 0, "Show verbose output" }, + { "pid", 'p', "pid", 0, "the pid of the user-space process" }, + { "upatch", 'u', "upatch", 0, "the upatch file" }, + { "binary", 'b', "binary", 0, "the binary file" }, + { "cmd", 0, "patch", 0, "Apply a upatch file to a user-space process" }, + { "cmd", 0, "unpatch", 0, + "Unapply a upatch file to a user-space process" }, + { NULL } +}; + +static char program_doc[] = "Operate a upatch file on the user-space process"; + +static char args_doc[] = + " --pid --upatch --binary "; + +const char *argp_program_version = "UPATCH_VERSION"; + +static error_t check_opt(struct argp_state *state) +{ + struct arguments *arguments = state->input; + + if (arguments->cmd == DEFAULT) { + argp_usage(state); + return ARGP_ERR_UNKNOWN; + } + switch (arguments->cmd) { + case PATCH: + case UNPATCH: + case INFO: + if (!arguments->pid || arguments->upatch == NULL || + arguments->binary == NULL) { + argp_usage(state); + return ARGP_ERR_UNKNOWN; + } + default: + break; + } + return 0; +} + +static error_t parse_opt(int key, char *arg, struct argp_state *state) +{ + struct arguments *arguments = state->input; + + switch (key) { + case 'v': + arguments->verbose = true; + break; + case 'p': + arguments->pid = atoi(arg); + break; + case 'u': + arguments->upatch = arg; + break; + case 'b': + arguments->binary = arg; + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 1) + argp_usage(state); + if (arguments->cmd != DEFAULT) + argp_usage(state); + for (int i = 1; i < COMMAND_SIZE; ++i) { + if (!strcmp(arg, command[i])) { + arguments->cmd = i; + break; + } + } + break; + case ARGP_KEY_END: + return check_opt(state); + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { options, parse_opt, args_doc, program_doc }; + +static void show_program_info(struct arguments *arguments) +{ + log_debug("pid: %d\n", arguments->pid); + log_debug("upatch object: %s\n", arguments->upatch); + log_debug("binary object: %s\n", arguments->binary); +} + +int patch_upatch(const char *binary_path, const char *upatch_path, int pid) +{ + int ret; + struct upatch_elf uelf; + struct running_elf relf; + + ret = upatch_init(&uelf, upatch_path); + if (ret) { + log_error("upatch_init failed %d \n", ret); + goto out; + } + + ret = binary_init(&relf, binary_path); + if (ret) { + log_error("binary_init failed %d \n", ret); + goto out; + } + + uelf.relf = &relf; + + // ret = check_build_id(&uelf.info, &relf.info); + // if (ret) { + // log_error("check build id failed %d \n", ret); + // goto out; + // } + + ret = process_patch(pid, &uelf, &relf); + if (ret) { + log_error("process patch failed %d \n", ret); + goto out; + } + +out: + upatch_close(&uelf); + binary_close(&relf); + if (ret) + log_normal("FAIL\n"); + else + log_normal("SUCCESS\n"); + return ret; +} + +int unpatch_upatch(const char *binary_path, const char *upatch_path, int pid) +{ + int ret = 0; + + ret = process_unpatch(pid); + if (ret) { + log_error("process patch failed %d \n", ret); + goto out; + } + +out: + if (ret) + log_normal("FAIL\n"); + else + log_normal("SUCCESS\n"); + return ret; +} + +int info_upatch(const char *binary_path, const char *upatch_path, int pid) +{ + int ret = 0; + + ret = process_info(pid); + if (ret) { + log_error("process patch failed %d \n", ret); + goto out; + } + +out: + return ret; +} + +int main(int argc, char *argv[]) +{ + struct arguments arguments; + + memset(&arguments, 0, sizeof(arguments)); + argp_parse(&argp, argc, argv, 0, NULL, &arguments); + if (arguments.verbose) + loglevel = DEBUG; + + logprefix = basename(arguments.upatch); + show_program_info(&arguments); + switch (arguments.cmd) { + case PATCH: + return patch_upatch(arguments.binary, arguments.upatch, + arguments.pid); + case UNPATCH: + return unpatch_upatch(arguments.binary, arguments.upatch, + arguments.pid); + case INFO: + return info_upatch(arguments.binary, arguments.upatch, + arguments.pid); + default: + ERROR("unknown command"); + } +} \ No newline at end of file diff --git a/upatch/upatch-manage/upatch-manage.h b/upatch/upatch-manage/upatch-manage.h deleted file mode 100644 index 32d2edd..0000000 --- a/upatch/upatch-manage/upatch-manage.h +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2022 HUAWEI, Inc. - * - * Authors: - * Longjun Luo - * - */ - -#ifndef _UPATCH_MANAGE_H -#define _UPATCH_MANAGE_H - -#ifndef UPATCH_VERSION -#define UPATCH_VERSION "1.0.2" -#endif - -enum upatch_module_state { - UPATCH_STATE_REMOVED = 0x1, /* Original status - No patch */ - UPATCH_STATE_ATTACHED, /* Attach patch to the binary */ - UPATCH_STATE_RESOLVED, /* Resolve the patch */ - UPATCH_STATE_ACTIVED, /* Activate the patch */ -}; - -#endif /* _UPATCH_MANAGE_H */ diff --git a/upatch/upatch-manage/upatch-patch.c b/upatch/upatch-manage/upatch-patch.c new file mode 100644 index 0000000..64ed34f --- /dev/null +++ b/upatch/upatch-manage/upatch-patch.c @@ -0,0 +1,904 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#include +#include +#include +#include + +#include +#include + +#include "log.h" +#include "upatch-common.h" +#include "upatch-patch.h" +#include "upatch-ptrace.h" +#include "upatch-relocation.h" +#include "upatch-resolve.h" + +#define GET_MICROSECONDS(a, b) \ + ((a.tv_sec - b.tv_sec) * 1000000 + (a.tv_usec - b.tv_usec)) + +#ifndef ARCH_SHF_SMALL +#define ARCH_SHF_SMALL 0 +#endif +#ifndef SHF_RO_AFTER_INIT +#define SHF_RO_AFTER_INIT 0x00200000 +#endif + +/* If this is set, the section belongs in the init part of the module */ +#define BITS_PER_LONG sizeof(unsigned long) * 8 + +static GElf_Off calculate_load_address(struct running_elf *relf, + bool check_code) +{ + int i; + GElf_Off min_addr = -1; + + /* TODO: for ET_DYN, consider check PIE */ + if (relf->info.hdr->e_type != ET_EXEC && + relf->info.hdr->e_type != ET_DYN) { + log_error("invalid elf type, it should be ET_EXEC or ET_DYN\n"); + goto out; + } + + for (i = 0; i < relf->info.hdr->e_phnum; ++i) { + if (relf->phdrs[i].p_type != PT_LOAD) + continue; + if (!check_code || + (check_code && (relf->phdrs[i].p_flags & PF_X))) + min_addr = (min_addr > relf->phdrs[i].p_vaddr) ? + relf->phdrs[i].p_vaddr : + min_addr; + // min_addr = min(min_addr, relf->phdrs[i].p_vaddr); + } + +out: + return min_addr; +} + +static unsigned long calculate_mem_load(struct object_file *obj) +{ + struct obj_vm_area *ovma; + unsigned long load_addr = -1; + + list_for_each_entry(ovma, &obj->vma, list) { + if (ovma->inmem.prot & PROT_EXEC) { + load_addr = (load_addr > ovma->inmem.start) ? + ovma->inmem.start : + load_addr; + } + } + + return load_addr; +} + +static int rewrite_section_headers(struct upatch_elf *uelf) +{ + unsigned int i; + + /* Handle SHF_ALLOC in this part */ + + /* This should always be true, but let's be sure. */ + uelf->info.shdrs[0].sh_addr = 0; + uelf->info.shdrs[0].sh_addralign = 0; + + for (i = 1; i < uelf->info.hdr->e_shnum; i++) { + GElf_Shdr *shdr = &uelf->info.shdrs[i]; + if (shdr->sh_type != SHT_NOBITS && + uelf->info.patch_size < shdr->sh_offset + shdr->sh_size) { + log_error("upatch len %lu truncated\n", + uelf->info.patch_size); + return -ENOEXEC; + } + + /* Mark all sections sh_addr with their address in the + temporary image. */ + shdr->sh_addr = (size_t)uelf->info.hdr + shdr->sh_offset; + log_debug("section %s at 0x%lx \n", + uelf->info.shstrtab + shdr->sh_name, shdr->sh_addr); + } + + return 0; +} + +/* Additional bytes needed by arch in front of individual sections */ +unsigned int arch_mod_section_prepend(struct upatch_elf *uelf, + unsigned int section) +{ + /* default implementation just returns zero */ + return 0; +} + +static long get_offset(struct upatch_elf *uelf, unsigned int *size, + GElf_Shdr *sechdr, unsigned int section) +{ + long ret; + + *size += arch_mod_section_prepend(uelf, section); + ret = ALIGN(*size, sechdr->sh_addralign ?: 1); + *size = ret + sechdr->sh_size; + return ret; +} + +static void layout_upatch_info(struct upatch_elf *uelf) +{ + GElf_Shdr *upatch_func = uelf->info.shdrs + uelf->index.upatch_funcs; + int num = upatch_func->sh_size / sizeof(struct upatch_patch_func); + + uelf->core_layout.info_size = uelf->core_layout.size; + uelf->core_layout.size += sizeof(struct upatch_info) + + num * sizeof(struct upatch_info_func); + uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); +} + +static void layout_jmptable(struct upatch_elf *uelf) +{ + uelf->jmp_cur_entry = 0; + uelf->jmp_max_entry = JMP_TABLE_MAX_ENTRY; + uelf->jmp_offs = ALIGN(uelf->core_layout.size, sizeof(unsigned long)); + uelf->core_layout.size = + uelf->jmp_offs + uelf->jmp_max_entry * get_jmp_table_entry(); + uelf->core_layout.text_size = uelf->core_layout.size; +} + +static void layout_sections(struct upatch_elf *uelf) +{ + static unsigned long const masks[][2] = { + /* NOTE: all executable code must be the last section + * in this array; otherwise modify the text_size + * finder in the two loops below */ + { SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL }, + { SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL }, + { SHF_RO_AFTER_INIT | SHF_ALLOC, ARCH_SHF_SMALL }, + { SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL }, + { ARCH_SHF_SMALL | SHF_ALLOC, 0 } + }; + unsigned int m, i; + + for (i = 0; i < uelf->info.hdr->e_shnum; i++) + uelf->info.shdrs[i].sh_entsize = ~0UL; + + log_debug("upatch section allocation order: \n"); + for (m = 0; m < ARRAY_SIZE(masks); ++m) { + for (i = 0; i < uelf->info.hdr->e_shnum; ++i) { + GElf_Shdr *s = &uelf->info.shdrs[i]; + const char *sname = uelf->info.shstrtab + s->sh_name; + + if ((s->sh_flags & masks[m][0]) != masks[m][0] || + (s->sh_flags & masks[m][1]) || + s->sh_entsize != ~0UL) + continue; + + s->sh_entsize = + get_offset(uelf, &uelf->core_layout.size, s, i); + log_debug("\tm = %d; %s: sh_entsize: 0x%lx\n", m, sname, + s->sh_entsize); + } + switch (m) { + case 0: /* executable */ + uelf->core_layout.size = + PAGE_ALIGN(uelf->core_layout.size); + uelf->core_layout.text_size = uelf->core_layout.size; + break; + case 1: /* RO: text and ro-data */ + uelf->core_layout.size = + PAGE_ALIGN(uelf->core_layout.size); + uelf->core_layout.ro_size = uelf->core_layout.size; + break; + case 2: /* RO after init */ + uelf->core_layout.size = + PAGE_ALIGN(uelf->core_layout.size); + uelf->core_layout.ro_after_init_size = + uelf->core_layout.size; + break; + case 3: /* whole core */ + uelf->core_layout.size = + PAGE_ALIGN(uelf->core_layout.size); + break; + } + } +} + +/* TODO: only included used symbol */ +static bool is_upatch_symbol(const GElf_Sym *src, const GElf_Shdr *sechdrs, + unsigned int shnum) +{ + return true; +} +/* + * We only allocate and copy the strings needed by the parts of symtab + * we keep. This is simple, but has the effect of making multiple + * copies of duplicates. We could be more sophisticated, see + * linux-kernel thread starting with + * <73defb5e4bca04a6431392cc341112b1@localhost>. + */ +static void layout_symtab(struct upatch_elf *uelf) +{ + GElf_Shdr *symsect = uelf->info.shdrs + uelf->index.sym; + GElf_Shdr *strsect = uelf->info.shdrs + uelf->index.str; + /* TODO: only support same arch as kernel now */ + const GElf_Sym *src; + unsigned int i, nsrc, ndst, strtab_size = 0; + + /* Put symbol section at end of init part of module. */ + symsect->sh_flags |= SHF_ALLOC; + symsect->sh_entsize = get_offset(uelf, &uelf->core_layout.size, symsect, + uelf->index.sym); + log_debug("\t%s\n", uelf->info.shstrtab + symsect->sh_name); + + src = (void *)uelf->info.hdr + symsect->sh_offset; + nsrc = symsect->sh_size / sizeof(*src); + + /* Compute total space required for the symbols' strtab. */ + for (ndst = i = 0; i < nsrc; i++) { + if (i == 0 || is_upatch_symbol(src + i, uelf->info.shdrs, + uelf->info.hdr->e_shnum)) { + strtab_size += + strlen(&uelf->strtab[src[i].st_name]) + 1; + ndst++; + } + } + + /* Append room for core symbols at end of core part. */ + uelf->symoffs = + ALIGN(uelf->core_layout.size, symsect->sh_addralign ?: 1); + uelf->stroffs = uelf->core_layout.size = + uelf->symoffs + ndst * sizeof(GElf_Sym); + uelf->core_layout.size += strtab_size; + uelf->core_typeoffs = uelf->core_layout.size; + uelf->core_layout.size += ndst * sizeof(char); + uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); + + /* Put string table section at end of init part of module. */ + strsect->sh_flags |= SHF_ALLOC; + strsect->sh_entsize = get_offset(uelf, &uelf->core_layout.size, strsect, + uelf->index.str); + uelf->core_layout.size = PAGE_ALIGN(uelf->core_layout.size); + log_debug("\t%s\n", uelf->info.shstrtab + strsect->sh_name); +} + +static void *upatch_alloc(struct object_file *obj, size_t sz) +{ + int ret; + unsigned long addr; + struct vm_hole *hole = NULL; + + addr = object_find_patch_region(obj, sz, &hole); + if (!addr) + return NULL; + + addr = upatch_mmap_remote(proc2pctx(obj->proc), addr, sz, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, + 0); + if (addr == 0) { + log_error("remote alloc memory for patch failed\n"); + return NULL; + } + + log_debug("allocated 0x%lx bytes at 0x%lx for '%s' patch\n", sz, addr, + obj->name); + + // log_debug("Marking this space as busy\n"); + ret = vm_hole_split(hole, addr, addr + sz); + if (ret) { + // TODO: clear + log_error("vm_hole_split failed\n"); + return NULL; + } + + return (void *)addr; +} + +static void __upatch_memfree(struct object_file *obj, void *base, + unsigned int size) +{ + log_debug("munmap upatch memory at: %p\n", base); + if (upatch_munmap_remote(proc2pctx(obj->proc), (unsigned long)base, + size)) { + log_error("Failed to munmap upatch memory at: %p\n", base); + } +} + +static int __alloc_memory(struct object_file *obj_file, + struct upatch_layout *layout) +{ + /* Do the allocs. */ + layout->base = upatch_alloc(obj_file, layout->size); + if (!layout->base) { + log_error("alloc upatch core_layout memory failed: %p \n", + layout->base); + return -ENOMEM; + } + + layout->kbase = malloc(layout->size); + if (!layout->kbase) { + __upatch_memfree(obj_file, layout->base, layout->size); + return -ENOMEM; + } + + memset(layout->kbase, 0, layout->size); + + return 0; +} + +static int alloc_memory(struct upatch_elf *uelf, struct object_file *obj) +{ + int i, ret; + + /* Do the allocs. */ + ret = __alloc_memory(obj, &uelf->core_layout); + if (ret) { + log_error("alloc upatch module memory failed: %d \n", ret); + return ret; + } + + /* Transfer each section which specifies SHF_ALLOC */ + log_debug("final section addresses:\n"); + for (i = 0; i < uelf->info.hdr->e_shnum; i++) { + void *kdest; + void *dest; + GElf_Shdr *shdr = &uelf->info.shdrs[i]; + + if (!(shdr->sh_flags & SHF_ALLOC)) + continue; + + kdest = uelf->core_layout.kbase + shdr->sh_entsize; + dest = uelf->core_layout.base + shdr->sh_entsize; + + if (shdr->sh_type != SHT_NOBITS) + memcpy(kdest, (void *)shdr->sh_addr, shdr->sh_size); + shdr->sh_addr = (unsigned long)kdest; + /* overuse this attr to record user address */ + shdr->sh_addralign = (unsigned long)dest; + log_debug("\t0x%lx %s <- 0x%lx\n", (long)kdest, + uelf->info.shstrtab + shdr->sh_name, (long)dest); + } + + return 0; +} + +static int post_memory(struct upatch_elf *uelf, struct object_file *obj) +{ + int ret = 0; + + log_debug("post kbase %lx(%x) to base %lx\n", + (unsigned long)uelf->core_layout.kbase, + uelf->core_layout.size, + (unsigned long)uelf->core_layout.base); + ret = upatch_process_mem_write(obj->proc, uelf->core_layout.kbase, + (unsigned long)uelf->core_layout.base, + uelf->core_layout.size); + if (ret) { + log_error("can't move kbase to base - %d\n", ret); + goto out; + } + +out: + return ret; +} + +static int complete_info(struct upatch_elf *uelf, struct object_file *obj) +{ + int ret = 0, i; + struct upatch_info *uinfo = + (void *)uelf->core_layout.kbase + uelf->core_layout.info_size; + struct upatch_patch_func *upatch_funcs_addr = + (void *)uelf->info.shdrs[uelf->index.upatch_funcs].sh_addr; + + // TODO: uinfo->id + memcpy(uinfo, UPATCH_HEADER, strlen(UPATCH_HEADER)); + uinfo->size = uelf->core_layout.size - uelf->core_layout.info_size; + uinfo->start = (unsigned long)uelf->core_layout.base; + uinfo->end = + (unsigned long)uelf->core_layout.base + uelf->core_layout.size; + uinfo->changed_func_num = + uelf->info.shdrs[uelf->index.upatch_funcs].sh_size / + sizeof(struct upatch_patch_func); + + log_debug("change insn:\n"); + for (i = 0; i < uinfo->changed_func_num; ++i) { + struct upatch_info_func *upatch_func = + (void *)uelf->core_layout.kbase + + uelf->core_layout.info_size + + sizeof(struct upatch_info) + + i * sizeof(struct upatch_info_func); + + upatch_func->old_addr = + upatch_funcs_addr[i].old_addr + uelf->relf->load_bias; + upatch_func->new_addr = upatch_funcs_addr[i].new_addr; + ret = upatch_process_mem_read(obj->proc, upatch_func->old_addr, + &upatch_func->old_insn, + get_origin_insn_len()); + if (ret) { + log_error("can't read origin insn at 0x%lx - %d\n", + upatch_func->old_addr, ret); + goto out; + } + + upatch_func->new_insn = get_new_insn(obj, upatch_func->old_addr, + upatch_func->new_addr); + + log_debug("\t0x%lx(0x%lx -> 0x%lx)\n", upatch_func->old_addr, + upatch_func->old_insn, upatch_func->new_insn); + } + +out: + return ret; +} + +static int unapply_patch(struct object_file *obj, + struct upatch_info_func *funcs, + unsigned int changed_func_num) +{ + int ret = 0, i; + + log_debug("change insn:\n"); + for (i = 0; i < changed_func_num; ++i) { + log_debug("\t0x%lx(0x%lx -> 0x%lx)\n", funcs[i].old_addr, + funcs[i].new_insn, funcs[i].old_insn); + + ret = upatch_process_mem_write(obj->proc, &funcs[i].old_insn, + (unsigned long)funcs[i].old_addr, + get_origin_insn_len()); + + if (ret) { + log_error("can't write old insn at 0x%lx - %d\n", + funcs[i].old_addr, ret); + goto out; + } + } + +out: + return ret; +} + +static int apply_patch(struct upatch_elf *uelf, struct object_file *obj) +{ + int ret = 0, i; + struct upatch_info *uinfo = + (void *)uelf->core_layout.kbase + uelf->core_layout.info_size; + + for (i = 0; i < uinfo->changed_func_num; ++i) { + struct upatch_info_func *upatch_func = + (void *)uelf->core_layout.kbase + + uelf->core_layout.info_size + + sizeof(struct upatch_info) + + i * sizeof(struct upatch_info_func); + + ret = upatch_process_mem_write( + obj->proc, &upatch_func->new_insn, + (unsigned long)upatch_func->old_addr, + get_origin_insn_len()); + if (ret) { + log_error( + "can't ptrace upatch func at 0x%lx(0x%lx) - %d\n", + upatch_func->old_addr, upatch_func->new_insn, + ret); + goto out; + } + } + +out: + if (ret) { + unapply_patch(obj, + (void *)uelf->core_layout.kbase + + uelf->core_layout.info_size + + sizeof(struct upatch_info), + i); + } + return ret; +} + +static int upatch_mprotect(struct upatch_elf *uelf, struct object_file *obj) +{ + int ret; + + if (uelf->core_layout.text_size > 0) { + ret = upatch_mprotect_remote( + proc2pctx(obj->proc), + (unsigned long)uelf->core_layout.base, + uelf->core_layout.text_size, PROT_READ | PROT_EXEC); + if (ret < 0) { + log_error( + "Failed to change upatch text protection to r-x"); + return ret; + } + } + + if (uelf->core_layout.ro_size > uelf->core_layout.text_size) { + ret = upatch_mprotect_remote( + proc2pctx(obj->proc), + (unsigned long)uelf->core_layout.base + + uelf->core_layout.text_size, + uelf->core_layout.ro_size - uelf->core_layout.text_size, + PROT_READ); + if (ret < 0) { + log_error( + "Failed to change upatch ro protection to r--"); + return ret; + } + } + + if (uelf->core_layout.ro_after_init_size > uelf->core_layout.ro_size) { + ret = upatch_mprotect_remote( + proc2pctx(obj->proc), + (unsigned long)uelf->core_layout.base + + uelf->core_layout.ro_size, + uelf->core_layout.ro_after_init_size - + uelf->core_layout.ro_size, + PROT_READ); + if (ret < 0) { + log_error( + "Failed to change upatch ro init protection to r--"); + return ret; + } + } + + if (uelf->core_layout.info_size > + uelf->core_layout.ro_after_init_size) { + ret = upatch_mprotect_remote( + proc2pctx(obj->proc), + (unsigned long)uelf->core_layout.base + + uelf->core_layout.ro_after_init_size, + uelf->core_layout.info_size - + uelf->core_layout.ro_after_init_size, + PROT_READ | PROT_WRITE); + if (ret < 0) { + log_error( + "Failed to change upatch rw protection to rw-"); + return ret; + } + } + + if (uelf->core_layout.size > uelf->core_layout.info_size) { + ret = upatch_mprotect_remote( + proc2pctx(obj->proc), + (unsigned long)uelf->core_layout.base + + uelf->core_layout.info_size, + uelf->core_layout.size - uelf->core_layout.info_size, + PROT_READ); + if (ret < 0) { + log_error( + "Failed to change upatch info protection to r--"); + return ret; + } + } + + return 0; +} + +static int upatch_apply_patches(struct upatch_process *proc, + struct upatch_elf *uelf) +{ + int ret = 0; + struct object_file *obj = NULL; + GElf_Off min_addr; + bool found = false; + + list_for_each_entry(obj, &proc->objs, list) { + if (obj->inode == uelf->relf->info.inode) { + found = true; + break; + } + } + + if (!found) { + ret = -1; + log_debug("can't found inode %lu in pid %d\n", + uelf->relf->info.inode, proc->pid); + goto out; + } + + min_addr = calculate_load_address(uelf->relf, true); + uelf->relf->load_start = calculate_mem_load(obj); + uelf->relf->load_bias = uelf->relf->load_start - min_addr; + log_debug("load_bias = %lx\n", uelf->relf->load_bias); + + ret = rewrite_section_headers(uelf); + if (ret) + goto free; + + // Caculate upatch mem size + layout_jmptable(uelf); + layout_sections(uelf); + layout_symtab(uelf); + layout_upatch_info(uelf); + + log_debug("calculate core_layout = %x \n", uelf->core_layout.size); + log_debug( + "core_layout: text_size = %x, ro_size = %x, ro_after_init_size = " + "%x, info = %x, size = %x\n", + uelf->core_layout.text_size, uelf->core_layout.ro_size, + uelf->core_layout.ro_after_init_size, + uelf->core_layout.info_size, uelf->core_layout.size); + + /* + * Map patch as close to the original code as possible. + * Otherwise we can't use 32-bit jumps. + */ + ret = alloc_memory(uelf, obj); + if (ret) + goto free; + + ret = upatch_mprotect(uelf, obj); + if (ret) + goto free; + + /* Fix up syms, so that st_value is a pointer to location. */ + ret = simplify_symbols(uelf, obj); + if (ret) + goto free; + + /* upatch new address will be updated */ + ret = apply_relocations(uelf); + if (ret) + goto free; + + /* upatch upatch info */ + ret = complete_info(uelf, obj); + if (ret) + goto free; + + ret = post_memory(uelf, obj); + if (ret) + goto free; + + ret = apply_patch(uelf, obj); + if (ret) + goto free; + + ret = 0; + goto out; + +// TODO: clear +free: + __upatch_memfree(obj, uelf->core_layout.base, uelf->core_layout.size); +out: + return ret; +} + +int process_patch(int pid, struct upatch_elf *uelf, struct running_elf *relf) +{ + int ret; + bool is_calc_time = false; + struct timeval start_tv, end_tv; + unsigned long frozen_time; + struct upatch_process proc; + + // 查看process的信息,pid: maps, mem, cmdline, exe + ret = upatch_process_init(&proc, pid); + if (ret < 0) { + log_error("cannot init process %d\n", pid); + goto out; + } + + upatch_process_print_short(&proc); + + ret = upatch_process_mem_open(&proc, MEM_READ); + if (ret < 0) + goto out_free; + + // use uprobe to hack function. the program has been executed to the entry + // point + + /* + * 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. + */ + // 解析process的mem-maps,获得各个块的内存映射以及phdr + ret = upatch_process_map_object_files(&proc, NULL); + if (ret < 0) + goto out_free; + + is_calc_time = true; + gettimeofday(&start_tv, NULL); + + /* Finally, attach to process */ + ret = upatch_process_attach(&proc); + if (ret < 0) + goto out_free; + + // TODO: 栈解析 + // 应用 + ret = upatch_apply_patches(&proc, uelf); + if (ret < 0) + goto out_free; + + ret = 0; + +out_free: + upatch_process_memfree(&proc); +out: + if (is_calc_time) { + gettimeofday(&end_tv, NULL); + frozen_time = GET_MICROSECONDS(end_tv, start_tv); + log_normal( + "PID '%d' process patch frozen_time is %ld microsecond\n", + pid, frozen_time); + } + return ret; +} + +static int upatch_unapply_patches(struct upatch_process *proc) +{ + int ret = 0; + struct object_file *obj = NULL; + struct object_patch *patch = NULL; + bool found = false; + + list_for_each_entry(obj, &proc->objs, list) { + if (obj->is_patch) { + found = true; + break; + } + } + + if (!found) { + ret = -1; + log_debug("can't found patch info memory\n"); + goto out; + } + + found = false; + list_for_each_entry(patch, &obj->applied_patch, list) { + // TODO: check upatch_info id + found = true; + break; + } + + if (!found) { + ret = -1; + log_debug("can't found patch info memory\n"); + goto out; + } + + ret = unapply_patch(obj, patch->funcs, patch->uinfo->changed_func_num); + if (ret) + goto out; + + log_debug("munmap upatch layout core:\n"); + __upatch_memfree(obj, (void *)patch->uinfo->start, + patch->uinfo->end - patch->uinfo->start); +out: + return ret; +} + +int process_unpatch(int pid) +{ + int ret; + bool is_calc_time = false; + struct timeval start_tv, end_tv; + unsigned long frozen_time; + struct upatch_process proc; + + // TODO: check build id + // TODO: 栈解析 + // 查看process的信息,pid: maps, mem, cmdline, exe + ret = upatch_process_init(&proc, pid); + if (ret < 0) { + log_error("cannot init process %d\n", pid); + goto out; + } + + upatch_process_print_short(&proc); + + ret = upatch_process_mem_open(&proc, MEM_READ); + if (ret < 0) + goto out_free; + + // use uprobe to hack function. the program has been executed to the entry + // point + + /* + * 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. + */ + // 解析process的mem-maps,获得各个块的内存映射以及phdr + ret = upatch_process_map_object_files(&proc, NULL); + if (ret < 0) + goto out_free; + + is_calc_time = true; + gettimeofday(&start_tv, NULL); + + /* Finally, attach to process */ + ret = upatch_process_attach(&proc); + if (ret < 0) + goto out_free; + + // 应用 + ret = upatch_unapply_patches(&proc); + if (ret < 0) + goto out_free; + + ret = 0; + +out_free: + upatch_process_memfree(&proc); +out: + if (is_calc_time) { + gettimeofday(&end_tv, NULL); + frozen_time = GET_MICROSECONDS(end_tv, start_tv); + log_normal( + "PID '%d' process patch frozen_time is %ld microsecond\n", + pid, frozen_time); + } + return ret; +} + +static int upatch_info(struct upatch_process *proc) +{ + struct object_file *obj = NULL; + struct object_patch *patch = NULL; + bool found = false; + + list_for_each_entry(obj, &proc->objs, list) { + if (obj->is_patch) { + found = true; + break; + } + } + + if (!found) + return found; + + found = false; + list_for_each_entry(patch, &obj->applied_patch, list) { + // TODO: check upatch_info id + found = true; + break; + } + + return found; +} + +int process_info(int pid) +{ + int ret; + struct upatch_process proc; + char *status = "error"; + + // TODO: check build id + // TODO: 栈解析 + // 查看process的信息,pid: maps, mem, cmdline, exe + ret = upatch_process_init(&proc, pid); + if (ret < 0) { + log_error("cannot init process %d\n", pid); + goto out; + } + + ret = upatch_process_mem_open(&proc, MEM_READ); + if (ret < 0) + goto out_free; + + ret = upatch_process_map_object_files(&proc, NULL); + if (ret < 0) + goto out_free; + + // 应用 + ret = upatch_info(&proc); + if (ret) + status = "active"; + else + status = "removed"; + + ret = 0; + +out_free: + upatch_process_memfree(&proc); +out: + log_normal("%s\n", status); + return ret; +} diff --git a/upatch/upatch-manage/upatch-patch.h b/upatch/upatch-manage/upatch-patch.h new file mode 100644 index 0000000..e699daf --- /dev/null +++ b/upatch/upatch-manage/upatch-patch.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#ifndef __UPATCH_PATCH__ +#define __UPATCH_PATCH__ + +#include "upatch-elf.h" +#include "upatch-process.h" +#include "list.h" + +int process_patch(int, struct upatch_elf *, struct running_elf *); + +int process_unpatch(int); + +int process_info(int); + +#endif \ No newline at end of file diff --git a/upatch/upatch-manage/upatch-process.c b/upatch/upatch-manage/upatch-process.c new file mode 100644 index 0000000..e4b2332 --- /dev/null +++ b/upatch/upatch-manage/upatch-process.c @@ -0,0 +1,823 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "log.h" +#include "process.h" +#include "upatch-common.h" +#include "upatch-elf.h" +#include "upatch-process.h" +#include "upatch-ptrace.h" + +/* + * Locks process by opening /proc//maps + * This ensures that task_struct will not be + * deleted in the kernel while we are working with + * the process + */ +static int lock_process(int pid) +{ + int fd; + char path[128]; + + log_debug("Locking PID %d...", pid); + snprintf(path, sizeof(path), "/proc/%d/maps", pid); + fd = open(path, O_RDONLY); + if (fd < 0) { + log_error("cannot open '/proc/%d/maps'\n", pid); + return -1; + } + log_debug("OK\n"); + return fd; +} + +// TODO: get addr_space +int upatch_coroutines_init(struct upatch_process *proc) +{ + INIT_LIST_HEAD(&proc->coro.coros); + + return 0; +} + +static int process_get_comm(struct upatch_process *proc) +{ + char path[128]; + char realpath[PATH_MAX]; + char *bn, *c; + ssize_t ret; + + log_debug("process_get_comm %d...", proc->pid); + snprintf(path, sizeof(path), "/proc/%d/exe", proc->pid); + + ret = readlink(path, realpath, sizeof(realpath)); + if (ret < 0) + return -1; + realpath[ret] = '\0'; + bn = basename(realpath); + strncpy(path, bn, sizeof(path) - 1); + if ((c = strstr(path, " (deleted)"))) + *c = '\0'; + + proc->comm[sizeof(proc->comm) - 1] = '\0'; + memcpy(proc->comm, path, sizeof(proc->comm) - 1); + // TODO: the comm is ldxxx + log_debug("OK\n"); + + return 0; +} + +static void unlock_process(int pid, int fdmaps) +{ + int errsv = errno; + close(fdmaps); + errno = errsv; +} + +int upatch_process_init(struct upatch_process *proc, int pid) +{ + int fdmaps; + + fdmaps = lock_process(pid); + if (fdmaps < 0) + goto out_err; + + memset(proc, 0, sizeof(*proc)); + + proc->pid = pid; + proc->fdmaps = fdmaps; + proc->memfd = -1; + + INIT_LIST_HEAD(&proc->ptrace.pctxs); + INIT_LIST_HEAD(&proc->objs); + INIT_LIST_HEAD(&proc->vmaholes); + proc->num_objs = 0; + + if (upatch_coroutines_init(proc)) + goto out_unlock; + if (process_get_comm(proc)) + goto out_unlock; + + return 0; + +out_unlock: + unlock_process(pid, fdmaps); +out_err: + return -1; +} + +static void process_print_cmdline(struct upatch_process *proc) +{ + char buf[1024]; + int fd; + ssize_t i, rv; + + snprintf(buf, sizeof("/proc/0123456789/cmdline"), "/proc/%d/cmdline", + proc->pid); + fd = open(buf, O_RDONLY); + if (fd == -1) { + log_error("open\n"); + return; + } + + while (1) { + rv = read(fd, buf, sizeof(buf)); + + if (rv == -1 && errno == EINTR) + continue; + + if (rv == -1) { + log_error("read\n"); + goto err_close; + } + + if (rv == 0) + break; + + for (i = 0; i < rv; i++) { + if (buf[i] != '\n' && isprint(buf[i])) + putchar(buf[i]); + else + printf("\\x%02x", (unsigned char)buf[i]); + } + } + +err_close: + close(fd); +} + +void upatch_process_print_short(struct upatch_process *proc) +{ + printf("upatch target pid %d, cmdline:", proc->pid); + process_print_cmdline(proc); + printf("\n"); +} + +int upatch_process_mem_open(struct upatch_process *proc, int mode) +{ + char path[sizeof("/proc/0123456789/mem")]; + + if (proc->memfd >= 0) { + close(proc->memfd); + } + + snprintf(path, sizeof(path), "/proc/%d/mem", proc->pid); + proc->memfd = open(path, mode == MEM_WRITE ? O_RDWR : O_RDONLY); + if (proc->memfd < 0) { + log_error("can't open /proc/%d/mem", proc->pid); + return -1; + } + + return 0; +} + +static unsigned int perms2prot(const char *perms) +{ + unsigned int prot = 0; + + if (perms[0] == 'r') + prot |= PROT_READ; + if (perms[1] == 'w') + prot |= PROT_WRITE; + if (perms[2] == 'x') + prot |= PROT_EXEC; + /* Ignore 'p'/'s' flag, we don't need it */ + return prot; +} + +static struct vm_hole *process_add_vm_hole(struct upatch_process *proc, + unsigned long hole_start, + unsigned long hole_end) +{ + struct vm_hole *hole; + + hole = malloc(sizeof(*hole)); + if (hole == NULL) + return NULL; + + memset(hole, 0, sizeof(*hole)); + hole->start = hole_start; + hole->end = hole_end; + + list_add(&hole->list, &proc->vmaholes); + + return hole; +} + +static int process_get_object_type(struct upatch_process *proc, + struct vm_area *vma, char *name, + unsigned char *buf, size_t bufsize) +{ + int ret, type = OBJECT_UNKNOWN; + + ret = upatch_process_mem_read(proc, vma->start, buf, bufsize); + if (ret < 0) + return -1; + + if (vma->prot == PROT_READ && + !strncmp(name, "[anonymous]", strlen("[anonymous]")) && + !memcmp(buf, UPATCH_HEADER, UPATCH_HEADER_LEN)) { + type = OBJECT_UPATCH; + } else if (!memcmp(buf, ELFMAG, SELFMAG)) { + type = OBJECT_ELF; + } else { + type = OBJECT_UNKNOWN; + } + + return type; +} + +static int vm_area_same(struct vm_area *a, struct vm_area *b) +{ + return ((a->start == b->start) && (a->end == b->end) && + (a->prot == b->prot)); +} + +static int object_add_vm_area(struct object_file *o, struct vm_area *vma, + struct vm_hole *hole) +{ + struct obj_vm_area *ovma; + + if (o->previous_hole == NULL) + o->previous_hole = hole; + list_for_each_entry(ovma, &o->vma, list) { + if (vm_area_same(vma, &ovma->inmem)) + return 0; + } + ovma = malloc(sizeof(*ovma)); + if (!ovma) + return -1; + memset(ovma, 0, sizeof(*ovma)); + ovma->inmem = *vma; + list_add(&ovma->list, &o->vma); + return 0; +} + +static struct object_file * +process_new_object(struct upatch_process *proc, dev_t dev, int inode, + const char *name, struct vm_area *vma, struct vm_hole *hole) +{ + struct object_file *o; + + log_debug("Creating object file '%s' for %lx:%d...", name, dev, inode); + + o = malloc(sizeof(*o)); + if (!o) { + log_error("FAIL\n"); + return NULL; + } + memset(o, 0, sizeof(struct object_file)); + + INIT_LIST_HEAD(&o->list); + INIT_LIST_HEAD(&o->vma); + INIT_LIST_HEAD(&o->applied_patch); + o->num_applied_patch = 0; + o->proc = proc; + o->dev = dev; + o->inode = inode; + o->is_patch = 0; + + o->previous_hole = hole; + if (object_add_vm_area(o, vma, hole) < 0) { + log_error("can't add vm_area for %s\n", name); + free(o); + return NULL; + } + + o->name = strdup(name); + o->is_elf = 0; + + list_add(&o->list, &proc->objs); + proc->num_objs++; + log_debug("OK\n"); + return o; +} + +/** + * Returns: 0 if everything is ok, -1 on error. + */ +static int process_add_object_vma(struct upatch_process *proc, dev_t dev, + int inode, char *name, struct vm_area *vma, + struct vm_hole *hole) +{ + int object_type; + unsigned char header_buf[1024]; + struct object_file *o; + + /* Event though process_get_object_type() return -1, + * we still need continue process. */ + object_type = process_get_object_type(proc, vma, name, header_buf, + sizeof(header_buf)); + + if (object_type != OBJECT_UPATCH) { + /* Is not a upatch, look if this is a vm_area of an already + * enlisted object. + */ + list_for_each_entry_reverse(o, &proc->objs, list) { + if ((dev && inode && o->dev == dev && + o->inode == inode) || + (dev == 0 && !strcmp(o->name, name))) { + return object_add_vm_area(o, vma, hole); + } + } + } + + o = process_new_object(proc, dev, inode, name, vma, hole); + if (o == NULL) + return -1; + + if (object_type == OBJECT_UPATCH) { + struct object_patch *opatch; + + opatch = malloc(sizeof(struct object_patch)); + if (opatch == NULL) + return -1; + + opatch->uinfo = malloc(sizeof(struct upatch_info)); + if (opatch->uinfo == NULL) + return -1; + + memcpy(opatch->uinfo, header_buf, sizeof(struct upatch_info)); + opatch->funcs = malloc(opatch->uinfo->changed_func_num * + sizeof(struct upatch_info_func)); + if (upatch_process_mem_read( + proc, vma->start + sizeof(struct upatch_info), + opatch->funcs, + opatch->uinfo->changed_func_num * + sizeof(struct upatch_info_func))) { + log_error("can't read patch funcs at 0x%lx\n", + vma->start + sizeof(struct upatch_info)); + return -1; + } + list_add(&opatch->list, &o->applied_patch); + o->num_applied_patch++; + o->is_patch = 1; + } + if (object_type == OBJECT_ELF) { + o->is_elf = 1; + } + + return 0; +} + +int upatch_process_parse_proc_maps(struct upatch_process *proc) +{ + FILE *f; + int ret, fd, is_libc_base_set = 0; + unsigned long hole_start = 0; + struct vm_hole *hole = NULL; + + /* + * 1. Create the list of all objects in the process + * 2. Check whether we have patch for any of them + * 3. If we have at least one patch, create files for all + * of the object (we might have references to them + * in the patch). + */ + fd = dup(proc->fdmaps); + if (fd < 0) { + log_error("unable to dup fd %d", proc->fdmaps); + return -1; + } + + lseek(fd, 0, SEEK_SET); + f = fdopen(fd, "r"); + if (f == NULL) { + log_error("unable to fdopen %d", fd); + close(fd); + return -1; + } + + do { + struct vm_area vma; + char line[1024]; + unsigned long start, end, offset; + unsigned int maj, min, inode; + char perms[5], name_[256], *name = name_; + int r; + + if (!fgets(line, sizeof(line), f)) + break; + r = sscanf(line, "%lx-%lx %s %lx %x:%x %d %255s", &start, &end, + perms, &offset, &maj, &min, &inode, name_); + + if (r == EOF) { + log_error("sscanf failed: end of file"); + goto error; + } + if (r != 8) + strcpy(name, "[anonymous]"); + + vma.start = start; + vma.end = end; + vma.offset = offset; + vma.prot = perms2prot(perms); + + /* Hole must be at least 2 pages for guardians */ + if (start - hole_start > 2 * PAGE_SIZE) { + hole = process_add_vm_hole(proc, hole_start + PAGE_SIZE, + start - PAGE_SIZE); + if (hole == NULL) { + log_error("Failed to add vma hole"); + goto error; + } + } + hole_start = end; + + name = name[0] == '/' ? basename(name) : name; + + ret = process_add_object_vma(proc, makedev(maj, min), inode, + name, &vma, hole); + if (ret < 0) { + log_error("Failed to add object vma"); + goto error; + } + + if (!is_libc_base_set && !strncmp(basename(name), "libc", 4) && + vma.prot & PROT_EXEC) { + proc->libc_base = start; + is_libc_base_set = 1; + } + + } while (1); + fclose(f); + + log_debug("Found %d object file(s) \n", proc->num_objs); + + if (!is_libc_base_set) { + log_error("Can't find libc_base required for manipulations: %d", + proc->pid); + return -1; + } + + return 0; + +error: + fclose(f); + return -1; +} + +int upatch_process_map_object_files(struct upatch_process *proc, + const char *patch_id) +{ + int ret; + + ret = upatch_process_parse_proc_maps(proc); + if (ret < 0) + return -1; + + // we can get plt/got table from mem's elf_segments + // Now we read them from the running file + + return ret; +} + +// static int process_has_thread_pid(struct upatch_proces *proc, int pid) +// { +// struct upatch_ptrace_ctx *pctx; + +// list_for_each_entry(pctx, &proc->ptrace.pctxs, list) +// if (pctx->pid == pid) +// return 1; + +// return 0; +// } + +static int process_list_threads(struct upatch_process *proc, int **ppids, + size_t *npids, size_t *alloc) +{ + DIR *dir = NULL; + struct dirent *de; + char path[128]; + int *pids = *ppids; + + snprintf(path, sizeof(path), "/proc/%d/task", proc->pid); + dir = opendir(path); + if (!dir) { + log_error("can't open '%s' directory\n", path); + goto dealloc; + } + + *npids = 0; + while ((de = readdir(dir))) { + int *t; + if (de->d_name[0] == '.') + continue; + + if (*npids >= *alloc) { + *alloc = *alloc ? *alloc * 2 : 1; + + t = realloc(pids, *alloc * sizeof(*pids)); + if (t == NULL) { + log_error( + "Failed to (re)allocate memory for pids\n"); + goto dealloc; + } + + pids = t; + } + + pids[*npids] = atoi(de->d_name); + (*npids)++; + } + closedir(dir); + + *ppids = pids; + + return *npids; + +dealloc: + if (dir) + closedir(dir); + free(pids); + *ppids = NULL; + *alloc = *npids = 0; + return -1; +} + +static void process_detach(struct upatch_process *proc) +{ + struct upatch_ptrace_ctx *p, *ptmp; + int status; + pid_t pid; + + if (proc->memfd >= 0 && close(proc->memfd) < 0) + log_error("can't close memfd"); + proc->memfd = -1; + + list_for_each_entry_safe(p, ptmp, &proc->ptrace.pctxs, list) { + /** + * If upatch_ptrace_detach(p) return -ESRCH, there are two situations, + * as described below: + * 1. the specified thread does not exist, it means the thread dead + * during the attach processing, so we need to wait for the thread + * to exit; + * 2. the specified thread is not currently being traced by us, + * or is not stopped, so we just ignore it; + * + * We using the running variable of the struct upatch_ptrace_ctx to + * distinguish them: + * 1. if pctx->running = 0, it means the thread is traced by us, we + * will wait for the thread to exit; + * 2. if pctx->running = 1, it means we can not sure about the status of + * the thread, we just ignore it; + */ + if (upatch_ptrace_detach(p) == -ESRCH && !p->running) { + do { + pid = waitpid(p->pid, &status, __WALL); + } while (pid > 0 && !WIFEXITED(status)); + } + // upatch_ptrace_ctx_destroy(p); + } + log_debug("Finished ptrace detaching.\n"); +} + +static const int max_attach_attempts = 3; + +int upatch_process_attach(struct upatch_process *proc) +{ + int *pids = NULL, ret; + size_t i, npids = 0, alloc = 0, prevnpids = 0, nattempts; + + if (upatch_process_mem_open(proc, MEM_WRITE) < 0) + return -1; + + for (nattempts = 0; nattempts < max_attach_attempts; nattempts++) { + ret = process_list_threads(proc, &pids, &npids, &alloc); + if (ret == -1) + goto detach; + + if (nattempts == 0) { + log_debug("Found %lu thread(s), attaching...\n", npids); + } else { + /* + * FIXME(pboldin): This is wrong, amount of threads can + * be the same because some new spawned and some old + * died + */ + if (prevnpids == npids) + break; + + log_debug("Found %lu new thread(s), attaching...\n", + prevnpids - npids); + } + + for (i = prevnpids; i < npids; i++) { + int pid = pids[i]; + + // if (process_has_thread_pid(proc, pid)) { + // log_debug("already have pid %d\n", pid); + // continue; + // } + + ret = upatch_ptrace_attach_thread(proc, pid); + if (ret < 0) + goto detach; + } + + prevnpids = npids; + } + + if (nattempts == max_attach_attempts) { + log_error("unable to catch up with process, bailing\n"); + goto detach; + } + + log_debug("attached to %lu thread(s): %d", npids, pids[0]); + for (i = 1; i < npids; i++) + log_debug(", %d", pids[i]); + log_debug("\n"); + + free(pids); + return 0; + +detach: + process_detach(proc); + free(pids); + return -1; +} + +static inline struct vm_hole *next_hole(struct vm_hole *hole, + struct list_head *head) +{ + if (hole == NULL || hole->list.next == head) + return NULL; + + return list_entry(hole->list.next, struct vm_hole, list); +} + +static inline struct vm_hole *prev_hole(struct vm_hole *hole, + struct list_head *head) +{ + if (hole == NULL || hole->list.prev == head) + return NULL; + + return list_entry(hole->list.prev, struct vm_hole, list); +} + +static inline unsigned long hole_size(struct vm_hole *hole) +{ + if (hole == NULL) + return 0; + return hole->end - hole->start; +} + +int vm_hole_split(struct vm_hole *hole, unsigned long alloc_start, + unsigned long alloc_end) +{ + alloc_start = ROUND_DOWN(alloc_start, PAGE_SIZE) - PAGE_SIZE; + alloc_end = ROUND_UP(alloc_end, PAGE_SIZE) + PAGE_SIZE; + + if (alloc_start > hole->start) { + struct vm_hole *left = NULL; + + left = malloc(sizeof(*hole)); + if (left == NULL) { + log_error("Failed to malloc for vm hole"); + return -1; + } + + left->start = hole->start; + left->end = alloc_start; + + list_add(&left->list, &hole->list); + } + + /* Reuse hole pointer as the right hole since it is pointed to by + * the `previous_hole` of some `object_file`. */ + hole->start = alloc_end; + hole->end = hole->end > alloc_end ? hole->end : alloc_end; + + return 0; +} + +/* + * 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; + struct vm_hole *right_hole = next_hole(left_hole, head); + unsigned long max_distance = MAX_DISTANCE; + struct obj_vm_area *sovma; + + unsigned long obj_start, obj_end; + unsigned long region_start = 0, region_end = 0; + + log_debug("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 = (obj_end - left_hole->start) <= + 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) { + log_error("can't find suitable region for patch on '%s'\n", + obj->name); + return -1UL; + } + + region_start = (region_start >> PAGE_SHIFT) << PAGE_SHIFT; + log_debug("Found patch region for '%s' at %lx\n", obj->name, + region_start); + + return region_start; +} + +static void upatch_object_memfree(struct object_file *obj) +{ + struct object_patch *opatch, *opatch_safe; + struct obj_vm_area *ovma, *ovma_safe; + + if (obj->name) + free(obj->name); + + list_for_each_entry_safe(opatch, opatch_safe, &obj->applied_patch, + list) { + if (opatch->uinfo) + free(opatch->uinfo); + if (opatch->funcs) + free(opatch->funcs); + free(opatch); + } + + list_for_each_entry_safe(ovma, ovma_safe, &obj->vma, list) { + free(ovma); + } +} + +void upatch_process_memfree(struct upatch_process *proc) +{ + struct upatch_ptrace_ctx *p, *p_safe; + struct object_file *obj, *obj_safe; + struct vm_hole *hole, *hole_safe; + + list_for_each_entry_safe(p, p_safe, &proc->ptrace.pctxs, list) { + free(p); + } + + list_for_each_entry_safe(hole, hole_safe, &proc->vmaholes, list) { + free(hole); + } + + list_for_each_entry_safe(obj, obj_safe, &proc->objs, list) { + upatch_object_memfree(obj); + free(obj); + } + + unlock_process(proc->pid, proc->fdmaps); + process_detach(proc); +} diff --git a/upatch/upatch-manage/upatch-process.h b/upatch/upatch-manage/upatch-process.h new file mode 100644 index 0000000..c780c51 --- /dev/null +++ b/upatch/upatch-manage/upatch-process.h @@ -0,0 +1,125 @@ +#ifndef __UPATCH_PROCESS__ +#define __UPATCH_PROCESS__ + +#include + +#include "list.h" +#include "upatch-patch.h" + +#define OBJECT_UNKNOWN 0 +#define OBJECT_ELF 1 +#define OBJECT_UPATCH 2 + +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +enum { + MEM_READ, + MEM_WRITE, +}; + +struct object_file { + struct list_head list; + struct upatch_process *proc; + + /* Device the object resides on */ + dev_t dev; + ino_t inode; + + /* Object name (as seen in /proc//maps) */ + char *name; + + /* List of object's VM areas */ + struct list_head vma; + + /* Pointer to the previous hole in the patient's mapping */ + struct vm_hole *previous_hole; + + /* Pointer to the applied patch list, if any */ + struct list_head applied_patch; + /* The number of applied patch */ + size_t num_applied_patch; + + /* Is that a patch for some object? */ + unsigned int is_patch; + + /* Is it an ELF or a mmap'ed regular file? */ + unsigned int is_elf; +}; + +struct vm_area { + unsigned long start; + unsigned long end; + unsigned long offset; + unsigned int prot; +}; + +struct vm_hole { + unsigned long start; + unsigned long end; + struct list_head list; +}; + +struct obj_vm_area { + struct list_head list; + struct vm_area inmem; +}; + +struct object_patch { + struct list_head list; + struct upatch_info *uinfo; + struct upatch_info_func *funcs; +}; + +struct upatch_process { + /* Pid of target process */ + int pid; + + /* memory fd of /proc//mem */ + int memfd; + + /* /proc//maps FD, also works as lock */ + int fdmaps; + + /* Process name */ + char comm[16]; + + /* List of process objects */ + struct list_head objs; + int num_objs; + + /* List ptrace contexts (one per each thread) */ + struct { + struct list_head pctxs; + } ptrace; + + struct { + struct list_head coros; + } coro; + + /* List of free VMA areas */ + struct list_head vmaholes; + + // TODO: other base? + /* libc's base address to use as a worksheet */ + unsigned long libc_base; +}; + +int upatch_process_init(struct upatch_process *, int); + +void upatch_process_print_short(struct upatch_process *); + +int upatch_process_mem_open(struct upatch_process *, int); + +int upatch_process_map_object_files(struct upatch_process *, const char *); + +int upatch_process_attach(struct upatch_process *); + +int vm_hole_split(struct vm_hole *, unsigned long, unsigned long); + +unsigned long object_find_patch_region(struct object_file *, size_t, + struct vm_hole **); + +void upatch_process_memfree(struct upatch_process *); + +#endif \ No newline at end of file diff --git a/upatch/upatch-manage/upatch-ptrace.c b/upatch/upatch-manage/upatch-ptrace.c new file mode 100644 index 0000000..4144fbe --- /dev/null +++ b/upatch/upatch-manage/upatch-ptrace.c @@ -0,0 +1,266 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "upatch-common.h" +#include "upatch-ptrace.h" + +/* process's memory access */ +int upatch_process_mem_read(struct upatch_process *proc, unsigned long src, + void *dst, size_t size) +{ + ssize_t r; + + r = pread(proc->memfd, dst, size, (off_t)src); + + return r != size ? -1 : 0; +} + +static int upatch_process_mem_write_ptrace(struct upatch_process *proc, + void *src, unsigned long dst, + size_t size) +{ + int ret; + + while (ROUND_DOWN(size, sizeof(long)) != 0) { + ret = ptrace(PTRACE_POKEDATA, proc->pid, dst, + *(unsigned long *)src); + if (ret) + return -1; + + dst += sizeof(long); + src += sizeof(long); + size -= sizeof(long); + } + + if (size) { + unsigned long tmp; + + tmp = ptrace(PTRACE_PEEKDATA, proc->pid, dst, NULL); + if (tmp == (unsigned long)-1 && errno) + return -1; + memcpy(&tmp, src, size); + + ret = ptrace(PTRACE_POKEDATA, proc->pid, dst, tmp); + if (ret) + return -1; + } + + return 0; +} + +int upatch_process_mem_write(struct upatch_process *proc, void *src, + unsigned long dst, size_t size) +{ + static int use_pwrite = 1; + ssize_t w; + + if (use_pwrite) + w = pwrite(proc->memfd, src, size, (off_t)dst); + if (!use_pwrite || (w == -1 && errno == EINVAL)) { + use_pwrite = 0; + return upatch_process_mem_write_ptrace(proc, src, dst, size); + } + + return w != size ? -1 : 0; +} + +static struct upatch_ptrace_ctx * +upatch_ptrace_ctx_alloc(struct upatch_process *proc) +{ + struct upatch_ptrace_ctx *p; + + p = malloc(sizeof(*p)); + if (!p) + return NULL; + memset(p, 0, sizeof(*p)); + + p->execute_until = 0UL; + p->running = 1; + p->proc = proc; + + INIT_LIST_HEAD(&p->list); + list_add(&p->list, &proc->ptrace.pctxs); + return p; +} + +int upatch_ptrace_attach_thread(struct upatch_process *proc, int tid) +{ + long ret; + int status; + struct upatch_ptrace_ctx *pctx; + + pctx = upatch_ptrace_ctx_alloc(proc); + if (pctx == NULL) { + log_error("Can't alloc upatch_ptrace_ctx"); + return -1; + } + + pctx->pid = tid; + log_debug("Attaching to %d...", pctx->pid); + + ret = ptrace(PTRACE_ATTACH, pctx->pid, NULL, NULL); + if (ret < 0) { + log_error("can't attach to %d\n", pctx->pid); + return -1; + } + + do { + ret = waitpid(tid, &status, __WALL); + if (ret < 0) { + log_error("can't wait for thread\n"); + return -1; + } + + /* We are expecting SIGSTOP */ + if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) + break; + + /* If we got SIGTRAP because we just got out of execve, wait + * for the SIGSTOP + */ + if (WIFSTOPPED(status)) + status = (WSTOPSIG(status) == SIGTRAP) ? + 0 : + WSTOPSIG(status); + else if (WIFSIGNALED(status)) + /* Resend signal */ + status = WTERMSIG(status); + + ret = ptrace(PTRACE_CONT, pctx->pid, NULL, + (void *)(uintptr_t)status); + if (ret < 0) { + log_error("can't cont tracee\n"); + return -1; + } + } while (1); + + pctx->running = 0; + + log_debug("OK\n"); + return 0; +} + +int wait_for_stop(struct upatch_ptrace_ctx *pctx, const void *data) +{ + int ret, status = 0, pid = (int)(uintptr_t)data ?: pctx->pid; + log_debug("wait_for_stop(pctx->pid=%d, pid=%d)\n", pctx->pid, pid); + + while (1) { + ret = ptrace(PTRACE_CONT, pctx->pid, NULL, + (void *)(uintptr_t)status); + if (ret < 0) { + log_error("can't start tracee %d\n", pctx->pid); + return -1; + } + + ret = waitpid(pid, &status, __WALL); + if (ret < 0) { + log_error("can't wait tracee %d\n", pid); + return -1; + } + + if (WIFSTOPPED(status)) { + if (WSTOPSIG(status) == SIGSTOP || + WSTOPSIG(status) == SIGTRAP) + break; + status = WSTOPSIG(status); + continue; + } + + status = WIFSIGNALED(status) ? WTERMSIG(status) : 0; + } + + return 0; +} + +int upatch_ptrace_detach(struct upatch_ptrace_ctx *pctx) +{ + long ret; + + if (!pctx->pid) + return 0; + log_debug("Detaching from %d...\n", pctx->pid); + ret = ptrace(PTRACE_DETACH, pctx->pid, NULL, NULL); + if (ret < 0) { + log_error("can't detach from %d\n", pctx->pid); + return -errno; + } + + log_debug("OK\n"); + + pctx->running = 1; + pctx->pid = 0; + return 0; +} + +int upatch_execute_remote(struct upatch_ptrace_ctx *pctx, + const unsigned char *code, size_t codelen, + struct user_regs_struct *pregs) +{ + return upatch_arch_execute_remote_func(pctx, code, codelen, pregs, + wait_for_stop, NULL); +} + +unsigned long upatch_mmap_remote(struct upatch_ptrace_ctx *pctx, + unsigned long addr, size_t length, int prot, + int flags, int fd, off_t offset) +{ + int ret; + unsigned long res = 0; + + log_debug("mmap_remote: 0x%lx+%lx, %x, %x, %d, %lx\n", addr, length, + prot, flags, fd, offset); + ret = upatch_arch_syscall_remote(pctx, __NR_mmap, (unsigned long)addr, + length, prot, flags, fd, offset, &res); + if (ret < 0) + return 0; + if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { + errno = -(long)res; + return 0; + } + return res; +} + +int upatch_mprotect_remote(struct upatch_ptrace_ctx *pctx, unsigned long addr, + size_t length, int prot) +{ + int ret; + unsigned long res; + + log_debug("mprotect_remote: 0x%lx+%lx\n", addr, length); + ret = upatch_arch_syscall_remote(pctx, __NR_mprotect, + (unsigned long)addr, length, prot, 0, + 0, 0, &res); + if (ret < 0) + return -1; + if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { + errno = -(long)res; + return -1; + } + + return 0; +} + +int upatch_munmap_remote(struct upatch_ptrace_ctx *pctx, unsigned long addr, + size_t length) +{ + int ret; + unsigned long res; + + log_debug("munmap_remote: 0x%lx+%lx\n", addr, length); + ret = upatch_arch_syscall_remote(pctx, __NR_munmap, (unsigned long)addr, + length, 0, 0, 0, 0, &res); + if (ret < 0) + return -1; + if (ret == 0 && res >= (unsigned long)-MAX_ERRNO) { + errno = -(long)res; + return -1; + } + return 0; +} diff --git a/upatch/upatch-manage/upatch-ptrace.h b/upatch/upatch-manage/upatch-ptrace.h new file mode 100644 index 0000000..2df8f78 --- /dev/null +++ b/upatch/upatch-manage/upatch-ptrace.h @@ -0,0 +1,63 @@ +#ifndef __UPATCH_PTRACE__ +#define __UPATCH_PTRACE__ + +#include "upatch-process.h" +#include + +#include "list.h" +#include "log.h" + +#define MAX_ERRNO 4095 + +struct upatch_ptrace_ctx { + int pid; + int running; + unsigned long execute_until; + struct upatch_process *proc; + struct list_head list; +}; + +#define proc2pctx(proc) \ + list_first_entry(&(proc)->ptrace.pctxs, struct upatch_ptrace_ctx, list) + +int upatch_process_mem_read(struct upatch_process *proc, unsigned long src, + void *dst, size_t size); + +int upatch_process_mem_write(struct upatch_process *, void *, unsigned long, + size_t); + +int upatch_ptrace_attach_thread(struct upatch_process *, int); + +int upatch_ptrace_detach(struct upatch_ptrace_ctx *); + +int wait_for_stop(struct upatch_ptrace_ctx *, const void *); + +void copy_regs(struct user_regs_struct *, struct user_regs_struct *); + +int upatch_arch_execute_remote_func(struct upatch_ptrace_ctx *pctx, + const unsigned char *code, size_t codelen, + struct user_regs_struct *pregs, + int (*func)(struct upatch_ptrace_ctx *pctx, + const void *data), + const void *data); + +int upatch_arch_syscall_remote(struct upatch_ptrace_ctx *, int, unsigned long, + unsigned long, unsigned long, unsigned long, + unsigned long, unsigned long, unsigned long *); + +unsigned long upatch_mmap_remote(struct upatch_ptrace_ctx *, unsigned long, + size_t, int, int, int, off_t); + +int upatch_mprotect_remote(struct upatch_ptrace_ctx *, unsigned long, size_t, + int); + +int upatch_munmap_remote(struct upatch_ptrace_ctx *, unsigned long, size_t); + +int upatch_execute_remote(struct upatch_ptrace_ctx *, const unsigned char *, + size_t, struct user_regs_struct *); + +size_t get_origin_insn_len(); + +unsigned long get_new_insn(struct object_file *, unsigned long, unsigned long); + +#endif \ No newline at end of file diff --git a/upatch/upatch-manage/upatch-relocation.c b/upatch/upatch-manage/upatch-relocation.c new file mode 100644 index 0000000..4c9c360 --- /dev/null +++ b/upatch/upatch-manage/upatch-relocation.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#include "upatch-relocation.h" +#include + +#include "log.h" + +int apply_relocations(struct upatch_elf *uelf) +{ + unsigned int i; + int err = 0; + + /* Now do relocations. */ + for (i = 1; i < uelf->info.hdr->e_shnum; i++) { + unsigned int infosec = uelf->info.shdrs[i].sh_info; + const char *name = + uelf->info.shstrtab + uelf->info.shdrs[i].sh_name; + + /* Not a valid relocation section? */ + if (infosec >= uelf->info.hdr->e_shnum) + continue; + + /* Don't bother with non-allocated sections */ + if (!(uelf->info.shdrs[infosec].sh_flags & SHF_ALLOC)) + continue; + + if (uelf->info.shdrs[i].sh_type == SHT_REL) { + log_error("do rel relocations for %s \n", name); + return -EPERM; + } else if (uelf->info.shdrs[i].sh_type == SHT_RELA) { + log_debug("do rela relocations for %s \n", name); + err = apply_relocate_add(uelf, uelf->index.sym, i); + } + + if (err < 0) + break; + } + return err; +} \ No newline at end of file diff --git a/upatch/upatch-manage/upatch-relocation.h b/upatch/upatch-manage/upatch-relocation.h new file mode 100644 index 0000000..f5ebdd5 --- /dev/null +++ b/upatch/upatch-manage/upatch-relocation.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#ifndef __UPATCH_RELOCATION__ +#define __UPATCH_RELOCATION__ + +#include + +#include "log.h" +#include "upatch-common.h" +#include "upatch-elf.h" + +// TODO: change define +#define s64 int64_t +#define u64 uint64_t +#define u32 uint32_t +#define s32 int32_t +#define u16 uint16_t +#define s16 int16_t + +int apply_relocate_add(struct upatch_elf *, unsigned int, unsigned int); + +int apply_relocations(struct upatch_elf *); + +#endif \ No newline at end of file diff --git a/upatch/upatch-manage/upatch-resolve.c b/upatch/upatch-manage/upatch-resolve.c new file mode 100644 index 0000000..f5d3523 --- /dev/null +++ b/upatch/upatch-manage/upatch-resolve.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#include +#include + +#include "log.h" +#include "upatch-common.h" +#include "upatch-elf.h" +#include "upatch-resolve.h" + +static unsigned long resolve_symbol(struct upatch_elf *uelf, + struct object_file *obj, const char *name, + GElf_Sym patch_sym) +{ + unsigned int i; + unsigned long elf_addr = 0; + char *sym_name = NULL, *tmp; + GElf_Shdr *sechdr; + GElf_Sym *sym; + GElf_Rela *rela; + struct running_elf *relf = uelf->relf; + + if (GELF_ST_TYPE(patch_sym.st_info) == STT_GNU_IFUNC && + (relf->info.hdr->e_ident[EI_OSABI] == ELFOSABI_GNU || + relf->info.hdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD)) + goto out_plt; + + /* + * In a shared library with position-independent code (PIC) (no pie), + * Such code accesses all constant addresses through a global offset table + * (GOT). + * TODO: consider check PIE + */ + if (relf->info.hdr->e_type == ET_DYN && + GELF_ST_BIND(patch_sym.st_info) == STB_GLOBAL && + (GELF_ST_TYPE(patch_sym.st_info) == STT_OBJECT || + GELF_ST_TYPE(patch_sym.st_info) == STT_FUNC)) + goto out_plt; + + /* handle symbol table first, in most cases, symbol table does not exist */ + sechdr = &relf->info.shdrs[relf->index.sym]; + sym = (void *)relf->info.hdr + sechdr->sh_offset; + for (i = 0; i < sechdr->sh_size / sizeof(GElf_Sym); i++) { + sym_name = relf->strtab + sym[i].st_name; + /* FIXME: handle version for external function */ + tmp = strchr(sym_name, '@'); + if (tmp != NULL) + *tmp = '\0'; + if (streql(sym_name, name) && sym[i].st_shndx != SHN_UNDEF) { + log_debug( + "found resolved undefined symbol %s at 0x%lx \n", + name, sym[i].st_value); + elf_addr = relf->load_bias + sym[i].st_value; + goto out; + } + } + + /* + * Handle external symbol, several possible solutions here: + * 1. use symbol address from .dynsym, but most of its address is still + * undefined + * 2. use address from PLT/GOT, problems are: + * 1) range limit(use jmp table?) + * 2) only support existed symbols + * 3. read symbol from library, combined with load_bias, calculate it + * directly and then worked with jmp table. + * + * Currently, we will try approach 1 and approach 2. + * Approach 3 is more general, but difficulty to implement. + */ +out_plt: + if (!relf->index.dynsym) + goto out; + + sechdr = &relf->info.shdrs[relf->index.dynsym]; + sym = (void *)relf->info.hdr + sechdr->sh_offset; + + /* handle external function */ + if (!relf->index.rela_plt) + goto out_got; + + sechdr = &relf->info.shdrs[relf->index.rela_plt]; + rela = (void *)relf->info.hdr + sechdr->sh_offset; + for (i = 0; i < sechdr->sh_size / sizeof(GElf_Rela); i++) { + unsigned long r_sym = GELF_R_SYM(rela[i].r_info); + /* for executable file, r_offset is virtual address of PLT table */ + unsigned long tmp_addr = relf->load_bias + rela[i].r_offset; + + /* some rela don't have the symbol index, use the symbol's value and + * rela's addend to find the symbol. for example, R_X86_64_IRELATIVE. + */ + if (r_sym == 0) { + if (rela[i].r_addend != patch_sym.st_value) + continue; + sprintf(sym_name, "%lx", rela[i].r_addend); + } else { + /* ATTENTION: should we consider the relocation type ? */ + sym_name = relf->dynstrtab + sym[r_sym].st_name; + /* FIXME: consider version of the library */ + tmp = strchr(sym_name, '@'); + if (tmp != NULL) + *tmp = '\0'; + + if (!(streql(sym_name, name) && + (GELF_ST_TYPE(sym[r_sym].st_info) == STT_FUNC || + GELF_ST_TYPE(sym[r_sym].st_info) == STT_TLS))) + continue; + } + + elf_addr = insert_plt_table( + uelf, obj, GELF_R_TYPE(rela[i].r_info), tmp_addr); + log_debug("found unresolved plt.rela %s at 0x%lx -> 0x%lx\n", + sym_name, rela[i].r_offset, elf_addr); + goto out; + } + +out_got: + /* handle external object, we need get it's address, used for + * R_X86_64_REX_GOTPCRELX */ + if (!relf->index.rela_dyn) + goto out; + + sechdr = &relf->info.shdrs[relf->index.rela_dyn]; + rela = (void *)relf->info.hdr + sechdr->sh_offset; + for (i = 0; i < sechdr->sh_size / sizeof(GElf_Rela); i++) { + unsigned long r_sym = GELF_R_SYM(rela[i].r_info); + /* for executable file, r_offset is virtual address of GOT table */ + unsigned long tmp_addr = relf->load_bias + rela[i].r_offset; + + if (r_sym == 0) { + if (rela[i].r_addend != patch_sym.st_value) + continue; + sprintf(sym_name, "%lx", rela[i].r_addend); + } else { + sym_name = relf->dynstrtab + sym[r_sym].st_name; + /* TODO: don't care about its version here */ + tmp = strchr(sym_name, '@'); + if (tmp != NULL) + *tmp = '\0'; + + /* function could also be part of the GOT with the type + * R_X86_64_GLOB_DAT */ + if (!streql(sym_name, name)) + continue; + } + + elf_addr = insert_got_table( + uelf, obj, GELF_R_TYPE(rela[i].r_info), tmp_addr); + log_debug("found unresolved .got %s at 0x%lx \n", sym_name, + elf_addr); + goto out; + } + + // get symbol address from .dynsym + sechdr = &relf->info.shdrs[relf->index.dynsym]; + sym = (void *)relf->info.hdr + sechdr->sh_offset; + for (i = 0; i < sechdr->sh_size / sizeof(GElf_Sym); i++) { + unsigned long tmp_addr; + + /* only need the st_value that is not 0 */ + if (sym[i].st_value == 0) + continue; + + sym_name = relf->dynstrtab + sym[i].st_name; + /* TODO: don't care about its version here */ + tmp = strchr(sym_name, '@'); + if (tmp != NULL) + *tmp = '\0'; + + /* function could also be part of the GOT with the type + * R_X86_64_GLOB_DAT */ + if (!streql(sym_name, name)) + continue; + + tmp_addr = relf->load_bias + sym[i].st_value; + elf_addr = insert_got_table(uelf, obj, 0, tmp_addr); + log_debug("found unresolved .got %s at 0x%lx \n", sym_name, + elf_addr); + goto out; + } + +out: + if (!elf_addr) { + log_error("unable to found valid symbol %s \n", name); + } + return elf_addr; +} + +int simplify_symbols(struct upatch_elf *uelf, struct object_file *obj) +{ + GElf_Sym *sym = (void *)uelf->info.shdrs[uelf->index.sym].sh_addr; + unsigned long secbase; + unsigned int i; + int ret = 0; + unsigned long elf_addr; + + for (i = 1; i < uelf->num_syms; i++) { + const char *name; + + if (GELF_ST_TYPE(sym[i].st_info) == STT_SECTION && + sym[i].st_shndx < uelf->info.hdr->e_shnum) + name = uelf->info.shstrtab + + uelf->info.shdrs[sym[i].st_shndx].sh_name; + else + name = uelf->strtab + sym[i].st_name; + + switch (sym[i].st_shndx) { + case SHN_COMMON: + log_debug("unsupported Common symbol: %s\n", name); + ret = -ENOEXEC; + break; + case SHN_ABS: + break; + case SHN_UNDEF: + elf_addr = resolve_symbol(uelf, obj, name, sym[i]); + if (!elf_addr) + ret = -ENOEXEC; + sym[i].st_value = elf_addr; + log_debug("resolved symbol %s at 0x%lx \n", name, + (unsigned long)sym[i].st_value); + break; + case SHN_LIVEPATCH: + sym[i].st_value += uelf->relf->load_bias; + log_debug("resolved livepatch symbol %s at 0x%lx \n", + name, (unsigned long)sym[i].st_value); + break; + default: + /* use real address to calculate secbase */ + secbase = + uelf->info.shdrs[sym[i].st_shndx].sh_addralign; + sym[i].st_value += secbase; + log_debug("normal symbol %s at 0x%lx \n", name, + (unsigned long)sym[i].st_value); + break; + } + } + + return ret; +} \ No newline at end of file diff --git a/upatch/upatch-manage/upatch-resolve.h b/upatch/upatch-manage/upatch-resolve.h new file mode 100644 index 0000000..844d281 --- /dev/null +++ b/upatch/upatch-manage/upatch-resolve.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 HUAWEI, Inc. + * + * Authors: + * Zongwu Li + * + */ + +#ifndef __UPATCH_RESOLVE__ +#define __UPATCH_RESOLVE__ + +#include "upatch-elf.h" +#include "upatch-process.h" + +#define SHN_LIVEPATCH 0xff20 + +/* jmp table, solve limit for the jmp instruction, Used for both PLT/GOT */ +struct upatch_jmp_table_entry; + +unsigned int get_jmp_table_entry(); + +unsigned long insert_plt_table(struct upatch_elf *, struct object_file *, + unsigned long, unsigned long); +unsigned long insert_got_table(struct upatch_elf *, struct object_file *, + unsigned long, unsigned long); + +unsigned long search_insert_plt_table(struct upatch_elf *, unsigned long, + unsigned long); + +int simplify_symbols(struct upatch_elf *, struct object_file *); + +#endif \ No newline at end of file diff --git a/upatch/upatch-tool/CMakeLists.txt b/upatch/upatch-tool/CMakeLists.txt index 2000b6a..28cb9d3 100644 --- a/upatch/upatch-tool/CMakeLists.txt +++ b/upatch/upatch-tool/CMakeLists.txt @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 include_directories( - ../upatch-manage/ ./ ) diff --git a/upatch/upatch-tool/upatch-tool.c b/upatch/upatch-tool/upatch-tool.c index ff4dfdb..f9739d7 100644 --- a/upatch/upatch-tool/upatch-tool.c +++ b/upatch/upatch-tool/upatch-tool.c @@ -20,9 +20,12 @@ #include -#include "upatch-manage.h" #include "upatch-ioctl.h" +#ifndef UPATCH_VERSION +#define UPATCH_VERSION "dev" +#endif + #define COMMAND_SIZE 8 char* command[COMMAND_SIZE] = {"", "active", "deactive", "install", "uninstall", "apply", "remove", "info"}; -- Gitee