From ab8982c74b0fac7ffc713a7990c572e90b553906 Mon Sep 17 00:00:00 2001 From: Alexander Aksenov Date: Wed, 30 Mar 2016 14:45:04 +0300 Subject: [PATCH] GOT patcher: implement initial version Change-Id: I8f8cb66f19a56afa6509cc619c1ed2aae5df756f Signed-off-by: Alexander Aksenov --- Kbuild | 3 +- got_patcher/Kbuild | 5 + got_patcher/gt.h | 6 + got_patcher/gt_debugfs.c | 735 +++++++++++++++++++++++++++++++ got_patcher/gt_debugfs.h | 7 + got_patcher/gt_module.c | 1028 +++++++++++++++++++++++++++++++++++++++++++ got_patcher/gt_module.h | 37 ++ loader/Kbuild | 1 - loader/loader.h | 5 + loader/loader_control.c | 213 --------- loader/loader_control.h | 17 - loader/loader_debugfs.c | 170 +------ loader/loader_module.c | 29 +- loader/loader_pd.c | 18 +- packaging/swap-modules.spec | 2 + preload/preload_module.c | 34 +- us_manager/pf/pf_group.c | 16 +- 17 files changed, 1879 insertions(+), 447 deletions(-) create mode 100644 got_patcher/Kbuild create mode 100644 got_patcher/gt.h create mode 100644 got_patcher/gt_debugfs.c create mode 100644 got_patcher/gt_debugfs.h create mode 100644 got_patcher/gt_module.c create mode 100644 got_patcher/gt_module.h delete mode 100644 loader/loader_control.c delete mode 100644 loader/loader_control.h diff --git a/Kbuild b/Kbuild index d820776..71380c2 100644 --- a/Kbuild +++ b/Kbuild @@ -24,4 +24,5 @@ obj-m := master/ \ wsp/ \ nsp/ \ task_ctx/ \ - uihv/ + uihv/ \ + got_patcher/ diff --git a/got_patcher/Kbuild b/got_patcher/Kbuild new file mode 100644 index 0000000..bd078f4 --- /dev/null +++ b/got_patcher/Kbuild @@ -0,0 +1,5 @@ +EXTRA_CFLAGS := $(extra_cflags) + +obj-m := swap_gtp.o +swap_gtp-y := gt_module.o \ + gt_debugfs.o diff --git a/got_patcher/gt.h b/got_patcher/gt.h new file mode 100644 index 0000000..ba5924e --- /dev/null +++ b/got_patcher/gt.h @@ -0,0 +1,6 @@ +#ifndef __GT_H__ +#define __GT_H__ + +#define GT_PREFIX "SWAP GOT PATCHER: " + +#endif /* __GT_H__ */ diff --git a/got_patcher/gt_debugfs.c b/got_patcher/gt_debugfs.c new file mode 100644 index 0000000..e7c907c --- /dev/null +++ b/got_patcher/gt_debugfs.c @@ -0,0 +1,735 @@ +#include +#include +#include +#include +#include +#include +#include "gt.h" +#include "gt_debugfs.h" +#include "gt_module.h" + +#define GT_DEFAULT_PERMS (S_IRUSR | S_IWUSR) /* u+rw */ + +static const char GT_FOLDER[] = "got_patcher"; +static const char GT_LINKER[] = "linker"; +static const char GT_PATH[] = "path"; +static const char GT_FIXUP_ADDR[] = "dl_fixup_addr"; +static const char GT_RELOC_ADDR[] = "dl_reloc_addr"; +static const char GT_ENABLE[] = "enable"; +static const char GT_BY_PATH[] = "by_path"; +static const char GT_BY_PID[] = "by_pid"; +static const char GT_ADD[] = "add"; +static const char GT_DEL[] = "del"; +static const char GT_DEL_ALL[] = "del_all"; +static const char GT_LIST_TARGETS[] = "list_targets"; +static const char GT_HANDLER[] = "handler"; +static const char GT_HANDLER_FIXUP_OFF[] = "fixup_handler_off"; +static const char GT_HANDLER_RELOC_OFF[] = "reloc_handler_off"; +static const char GT_PTHREAD[] = "pthread"; +static const char GT_MINIMAL_INIT[] = "minimal_init_off"; + +static struct dentry *gt_root; + + +/* =========================================================================== + * = TARGETS = + * =========================================================================== + */ + +static ssize_t handler_path_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + char *path; + + path = kmalloc(len, GFP_KERNEL); + if (path == NULL) { + ret = -ENOMEM; + goto handler_path_write_out; + } + + if (copy_from_user(path, buf, len)) { + ret = -EINVAL; + goto handler_path_write_out; + } + + path[len - 1] = '\0'; + ret = gtm_set_handler_path(path); + +handler_path_write_out: + kfree(path); + + return ret == 0 ? len : ret; +} + +static ssize_t handler_fixup_off_write(struct file *file, + const char __user *buf, size_t len, + loff_t *ppos) +{ + ssize_t ret; + char *off; + unsigned long offset; + + off = kmalloc(len, GFP_KERNEL); + if (off == NULL) { + ret = -ENOMEM; + goto handler_fixup_off_write_out; + } + + if (copy_from_user(off, buf, len)) { + ret = -EINVAL; + goto handler_fixup_off_write_out; + } + + off[len - 1] = '\0'; + + ret = kstrtoul(off, 16, &offset); + if (ret != 0) + goto handler_fixup_off_write_out; + + ret = gtm_set_handler_fixup_off(offset); + +handler_fixup_off_write_out: + kfree(off); + + return ret == 0 ? len : ret; +} + +static ssize_t handler_reloc_off_write(struct file *file, + const char __user *buf, size_t len, + loff_t *ppos) +{ + ssize_t ret; + char *off; + unsigned long offset; + + off = kmalloc(len, GFP_KERNEL); + if (off == NULL) { + ret = -ENOMEM; + goto handler_reloc_off_write_out; + } + + if (copy_from_user(off, buf, len)) { + ret = -EINVAL; + goto handler_reloc_off_write_out; + } + + off[len - 1] = '\0'; + + ret = kstrtoul(off, 16, &offset); + if (ret != 0) + goto handler_reloc_off_write_out; + + ret = gtm_set_handler_reloc_off(offset); + +handler_reloc_off_write_out: + kfree(off); + + return ret == 0 ? len : ret; +} + +static const struct file_operations handler_path_fops = { + .owner = THIS_MODULE, + .write = handler_path_write, +}; + +static const struct file_operations handler_fixup_off_fops = { + .owner = THIS_MODULE, + .write = handler_fixup_off_write, +}; + +static const struct file_operations handler_reloc_off_fops = { + .owner = THIS_MODULE, + .write = handler_reloc_off_write, +}; + +/* =========================================================================== + * = TARGETS = + * =========================================================================== + */ + +static ssize_t by_path_add_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + char *path; + + path = kmalloc(len, GFP_KERNEL); + if (path == NULL) { + ret = -ENOMEM; + goto target_path_add_write_out; + } + + if (copy_from_user(path, buf, len)) { + ret = -EINVAL; + goto target_path_add_write_out; + } + + path[len - 1] = '\0'; + ret = gtm_add_by_path(path); + +target_path_add_write_out: + kfree(path); + + return ret == 0 ? len : ret; +} + +static ssize_t by_path_del_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + char *path; + + path = kmalloc(len, GFP_KERNEL); + if (path == NULL) { + ret = -ENOMEM; + goto target_path_del_write_out; + } + + if (copy_from_user(path, buf, len)) { + ret = -EINVAL; + goto target_path_del_write_out; + } + + path[len - 1] = '\0'; + ret = gtm_del_by_path(path); + +target_path_del_write_out: + kfree(path); + + return ret == 0 ? len : ret; +} + +static ssize_t by_pid_add_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + unsigned long pid; + char *pid_str; + + pid_str = kmalloc(len, GFP_KERNEL); + if (pid_str == NULL) { + ret = -ENOMEM; + goto target_pid_add_write_out; + } + + if (copy_from_user(pid_str, buf, len)) { + ret = -EINVAL; + goto target_pid_add_write_out; + } + + pid_str[len - 1] = '\0'; + + ret = kstrtoul(pid_str, 10, &pid); + if (ret != 0) + goto target_pid_add_write_out; + + ret = gtm_add_by_pid(pid); + +target_pid_add_write_out: + kfree(pid_str); + + return ret == 0 ? len : ret; +} + +static ssize_t by_pid_del_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + unsigned long pid; + char *pid_str; + + pid_str = kmalloc(len, GFP_KERNEL); + if (pid_str == NULL) { + ret = -ENOMEM; + goto target_pid_del_write_out; + } + + if (copy_from_user(pid_str, buf, len)) { + ret = -EINVAL; + goto target_pid_del_write_out; + } + + pid_str[len - 1] = '\0'; + + ret = kstrtoul(pid_str, 10, &pid); + if (ret != 0) + goto target_pid_del_write_out; + + ret = gtm_del_by_pid(pid); + +target_pid_del_write_out: + kfree(pid_str); + + return ret == 0 ? len : ret; +} + +static ssize_t del_all_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + + ret = gtm_del_all(); + + return ret == 0 ? len : ret; +} + +static ssize_t target_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + char *targets; + + ret = gtm_get_targets(&targets); + if (ret < 0) + return ret; + + return simple_read_from_buffer(buf, len, ppos, targets, ret); +} + +static const struct file_operations by_path_add_fops = { + .owner = THIS_MODULE, + .write = by_path_add_write, +}; + +static const struct file_operations by_path_del_fops = { + .owner = THIS_MODULE, + .write = by_path_del_write, +}; + +static const struct file_operations by_pid_add_fops = { + .owner = THIS_MODULE, + .write = by_pid_add_write, +}; + +static const struct file_operations by_pid_del_fops = { + .owner = THIS_MODULE, + .write = by_pid_del_write, +}; + +static const struct file_operations del_all_fops = { + .owner = THIS_MODULE, + .write = del_all_write, +}; + +static const struct file_operations target_fops = { + .owner = THIS_MODULE, + .read = target_read, +}; + +/* =========================================================================== + * = ENABLE = + * =========================================================================== + */ + +static ssize_t enable_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + char val[2]; + + val[0] = (gtm_status() == GT_ON ? '1' : '0'); + val[1] = '\0'; + + return simple_read_from_buffer(buf, len, ppos, val, 2); +} + +static ssize_t enable_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + char val[2]; + size_t val_size; + + val_size = min(len, (sizeof(val) - 1)); + if (copy_from_user(val, buf, val_size)) + return -EFAULT; + + val[1] = '\0'; + switch(val[0]) { + case '0': + ret = gtm_switch(GT_OFF); + break; + case '1': + ret = gtm_switch(GT_ON); + break; + default: + printk(GT_PREFIX "Invalid state!\n"); + return -EINVAL; + } + + return ret == 0 ? len : ret; +} + +static const struct file_operations enable_fops = { + .owner = THIS_MODULE, + .write = enable_write, + .read = enable_read, +}; + + +/* =========================================================================== + * = LINKER = + * =========================================================================== + */ + +static ssize_t linker_path_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + char *path; + + path = kmalloc(len, GFP_KERNEL); + if (path == NULL) { + ret = -ENOMEM; + goto linker_path_write_out; + } + + if (copy_from_user(path, buf, len)) { + ret = -EINVAL; + goto linker_path_write_out; + } + + path[len - 1] = '\0'; + + ret = gtm_set_linker_path(path); + +linker_path_write_out: + kfree(path); + + return ret == 0 ? len : ret; +} + +static ssize_t fixup_off_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + char *off; + unsigned long offset; + + off = kmalloc(len, GFP_KERNEL); + if (off == NULL) { + ret = -ENOMEM; + goto fixup_off_write_out; + } + + if (copy_from_user(off, buf, len)) { + ret = -EINVAL; + goto fixup_off_write_out; + } + + off[len - 1] = '\0'; + + ret = kstrtoul(off, 16, &offset); + if (ret != 0) + goto fixup_off_write_out; + + ret = gtm_set_fixup_off(offset); + +fixup_off_write_out: + kfree(off); + + return ret == 0 ? len : ret; +} + +static ssize_t reloc_off_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + char *off; + unsigned long offset; + + off = kmalloc(len, GFP_KERNEL); + if (off == NULL) { + ret = -ENOMEM; + goto reloc_off_write_out; + } + + if (copy_from_user(off, buf, len)) { + ret = -EINVAL; + goto reloc_off_write_out; + } + + off[len - 1] = '\0'; + + ret = kstrtoul(off, 16, &offset); + if (ret != 0) + goto reloc_off_write_out; + + ret = gtm_set_reloc_off(offset); + +reloc_off_write_out: + kfree(off); + + return ret == 0 ? len : ret; +} + +static const struct file_operations linker_path_fops = { + .owner = THIS_MODULE, + .write = linker_path_write, +}; + +static const struct file_operations fixup_off_fops = { + .owner = THIS_MODULE, + .write = fixup_off_write, +}; + +static const struct file_operations reloc_off_fops = { + .owner = THIS_MODULE, + .write = reloc_off_write, +}; + + + +/* =========================================================================== + * = PTHREAD = + * =========================================================================== + */ + +static ssize_t pthread_path_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + char *path; + + path = kmalloc(len, GFP_KERNEL); + if (path == NULL) { + ret = -ENOMEM; + goto pthread_path_write_out; + } + + if (copy_from_user(path, buf, len)) { + ret = -EINVAL; + goto pthread_path_write_out; + } + + path[len - 1] = '\0'; + + ret = gtm_set_pthread_path(path); + +pthread_path_write_out: + kfree(path); + + return ret == 0 ? len : ret; +} + +static ssize_t init_off_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + char *off; + unsigned long offset; + + off = kmalloc(len, GFP_KERNEL); + if (off == NULL) { + ret = -ENOMEM; + goto init_off_write_out; + } + + if (copy_from_user(off, buf, len)) { + ret = -EINVAL; + goto init_off_write_out; + } + + off[len - 1] = '\0'; + + ret = kstrtoul(off, 16, &offset); + if (ret != 0) { + ret = -EINVAL; + goto init_off_write_out; + } + + ret = gtm_set_init_off(offset); + +init_off_write_out: + kfree(off); + + return ret == 0 ? len : ret; +} + +static const struct file_operations pthread_path_fops = { + .owner = THIS_MODULE, + .write = pthread_path_write, +}; + +static const struct file_operations pthread_init_off_fops = { + .owner = THIS_MODULE, + .write = init_off_write, +}; + + + + +int gtd_init(void) +{ + struct dentry *swap_dentry, *root, *linker, *by_path, *by_pid, + *handler, *pthread, *dentry; + int ret; + + ret = -ENODEV; + if (!debugfs_initialized()) + goto fail; + + ret = -ENOENT; + swap_dentry = swap_debugfs_getdir(); + if (!swap_dentry) + goto fail; + + ret = -ENOMEM; + root = debugfs_create_dir(GT_FOLDER, swap_dentry); + if (IS_ERR_OR_NULL(root)) + goto fail; + + gt_root = root; + + linker = debugfs_create_dir(GT_LINKER, root); + if (IS_ERR_OR_NULL(linker)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_PATH, GT_DEFAULT_PERMS, linker, + NULL, &linker_path_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_FIXUP_ADDR, GT_DEFAULT_PERMS, linker, + NULL, &fixup_off_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_RELOC_ADDR, GT_DEFAULT_PERMS, linker, + NULL, &reloc_off_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + by_path = debugfs_create_dir(GT_BY_PATH, root); + if (IS_ERR_OR_NULL(by_path)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_ADD, GT_DEFAULT_PERMS, by_path, + NULL, &by_path_add_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_DEL, GT_DEFAULT_PERMS, by_path, NULL, + &by_path_del_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + by_pid = debugfs_create_dir(GT_BY_PID, root); + if (IS_ERR_OR_NULL(by_pid)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_ADD, GT_DEFAULT_PERMS, by_pid, + NULL, &by_pid_add_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_DEL, GT_DEFAULT_PERMS, by_pid, + NULL, &by_pid_del_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_DEL_ALL, GT_DEFAULT_PERMS, root, + NULL, &del_all_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_LIST_TARGETS, GT_DEFAULT_PERMS, root, + NULL, &target_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_ENABLE, GT_DEFAULT_PERMS, root, + NULL, &enable_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + handler = debugfs_create_dir(GT_HANDLER, root); + if (IS_ERR_OR_NULL(handler)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_PATH, GT_DEFAULT_PERMS, handler, + NULL, &handler_path_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_HANDLER_FIXUP_OFF, GT_DEFAULT_PERMS, + handler, NULL, &handler_fixup_off_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_HANDLER_RELOC_OFF, GT_DEFAULT_PERMS, + handler, NULL, &handler_reloc_off_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + pthread = debugfs_create_dir(GT_PTHREAD, root); + if (IS_ERR_OR_NULL(pthread)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_PATH, GT_DEFAULT_PERMS, pthread, + NULL, &pthread_path_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + dentry = debugfs_create_file(GT_MINIMAL_INIT, GT_DEFAULT_PERMS, + pthread, NULL, &pthread_init_off_fops); + if (IS_ERR_OR_NULL(dentry)) { + ret = -ENOMEM; + goto remove; + } + + return 0; + +remove: + debugfs_remove_recursive(root); + +fail: + printk(GT_PREFIX "Debugfs initialization failure: %d\n", ret); + + return ret; +} + +void gtd_exit(void) +{ + if (gt_root) + debugfs_remove_recursive(gt_root); + gt_root = NULL; +} diff --git a/got_patcher/gt_debugfs.h b/got_patcher/gt_debugfs.h new file mode 100644 index 0000000..e6d5329 --- /dev/null +++ b/got_patcher/gt_debugfs.h @@ -0,0 +1,7 @@ +#ifndef __GT_DEBUGFS_H__ +#define __GT_DEBUGFS_H__ + +int gtd_init(void); +void gtd_exit(void); + +#endif /* __GT_DEBUGFS_H__ */ diff --git a/got_patcher/gt_module.c b/got_patcher/gt_module.c new file mode 100644 index 0000000..f737499 --- /dev/null +++ b/got_patcher/gt_module.c @@ -0,0 +1,1028 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gt.h" +#include "gt_module.h" +#include "gt_debugfs.h" + +#define page_to_proc(page) ((page)->file->proc) +#define ip_to_proc(ip) page_to_proc((ip)->page) +#define urp_to_ip(rp) container_of(rp, struct sspt_ip, retprobe) + +enum task_id_t { + GT_SET_BY_DENTRY, + GT_SET_BY_PID, +}; + +struct l_probe_el { + struct list_head list; + struct pf_group *pfg; + enum task_id_t task_id; + union { + pid_t tgid; + struct dentry *dentry; + }; +}; + +struct bin_data_t { + struct dentry *dentry; + unsigned long off; +}; + +typedef unsigned long (*rh_t)(struct uretprobe_instance *, struct pt_regs *, + struct hd_t *); + +static LIST_HEAD(_reg_probes); +static LIST_HEAD(_unreg_probes); +static DEFINE_MUTEX(_linker_probes_lock); + +static enum gt_status _status = GT_OFF; +static struct bin_data_t _linker_fixup; +static struct bin_data_t _linker_reloc; +static struct bin_data_t _handler_fixup; +static struct bin_data_t _handler_reloc; +static struct bin_data_t _pthread_init; + + +static inline void _lock_probes_list(void) +{ + mutex_lock(&_linker_probes_lock); +} + +static inline void _unlock_probes_list(void) +{ + mutex_unlock(&_linker_probes_lock); +} + +static inline bool _is_enable(void) +{ + return _status == GT_ON; +} + +static inline bool _is_bin_data_available(struct bin_data_t *bin_data) +{ + return (bin_data->dentry && bin_data->off); +} + +static inline bool _is_linker_data_available(void) +{ + return _is_bin_data_available(&_linker_fixup) && + _is_bin_data_available(&_linker_reloc); +} + +static inline bool _is_handler_data_available(void) +{ + return _is_bin_data_available(&_handler_fixup) && + _is_bin_data_available(&_handler_reloc); +} + +static inline bool _is_pthread_data_available(void) +{ + return _is_bin_data_available(&_pthread_init); +} + +static inline struct pd_t *_get_process_data(struct uretprobe *rp) +{ + struct sspt_ip *ip = urp_to_ip(rp); + struct sspt_proc *proc = ip_to_proc(ip); + + return lpd_get(proc); +} + +static struct dentry *get_dentry(const char *filepath) +{ + struct path path; + struct dentry *dentry = NULL; + + if (kern_path(filepath, LOOKUP_FOLLOW, &path) == 0) { + dentry = dget(path.dentry); + path_put(&path); + } + + return dentry; +} + +static void put_dentry(struct dentry *dentry) +{ + dput(dentry); +} + + + + +/* =========================================================================== + * = TASK DATA MANIPULATION = + * =========================================================================== + */ + +static void gt_ktd_init(struct task_struct *task, void *data) +{ + bool *in_handler = (bool *)data; + + *in_handler = false; +} + +static void gt_ktd_exit(struct task_struct *task, void *data) +{ +} + +static struct ktask_data gt_ktd = { + .init = gt_ktd_init, + .exit = gt_ktd_exit, + .size = sizeof(bool), +}; + +static inline bool _is_in_handler(void) +{ + return *(bool *)swap_ktd(>_ktd, current); +} + +static inline void _set_in_handler(bool is_in_handler) +{ + *(bool *)swap_ktd(>_ktd, current) = is_in_handler; +} + +/* =========================================================================== + * = LINKER HANDLERS = + * =========================================================================== + */ + +static unsigned long _redirect_to_handler(struct uretprobe_instance *ri, + struct pt_regs *regs, + struct hd_t *hd, unsigned long off) +{ + unsigned long base; + unsigned long vaddr; + + base = lpd_get_handlers_base(hd); + if (base == 0) + return 0; + + vaddr = base + off; + loader_module_prepare_ujump(ri, regs, vaddr); + _set_in_handler(true); + + return vaddr; +} + +static unsigned long _redirect_to_fixup_handler(struct uretprobe_instance *ri, + struct pt_regs *regs, + struct hd_t *hd) +{ + return _redirect_to_handler(ri, regs, hd, _handler_fixup.off); +} + +static unsigned long _redirect_to_reloc_handler(struct uretprobe_instance *ri, + struct pt_regs *regs, + struct hd_t *hd) +{ + return _redirect_to_handler(ri, regs, hd, _handler_reloc.off); +} + + + +static int _process_eh(struct uretprobe_instance *ri, struct pt_regs *regs, + rh_t rh, struct dentry *dentry) +{ + struct pd_t *pd = _get_process_data(ri->rp); + struct hd_t *hd; + unsigned long vaddr = 0; + unsigned long old_pc = swap_get_instr_ptr(regs); + + if ((dentry == NULL) || _is_in_handler()) + goto out_set_orig; + + hd = lpd_get_hd(pd, dentry); + if (hd == NULL) + goto out_set_orig; + + if ((lpd_get_state(hd) == NOT_LOADED || lpd_get_state(hd) == FAILED) && + lpd_get_init_state(pd)) + vaddr = loader_not_loaded_entry(ri, regs, pd, hd); + else if (lpd_get_state(hd) == LOADED) + vaddr = rh(ri, regs, hd); + +out_set_orig: + loader_set_priv_origin(ri, vaddr); + + /* PC change check */ + return old_pc != swap_get_instr_ptr(regs); +} + +static int dl_fixup_eh(struct uretprobe_instance *ri, struct pt_regs *regs) +{ + return _process_eh(ri, regs, &_redirect_to_fixup_handler, + _handler_fixup.dentry); +} + + + +static void _restore_exec(struct uretprobe_instance *ri, struct hd_t *hd) +{ + if (_is_in_handler()) + _set_in_handler(false); +} + +static int _process_rh(struct uretprobe_instance *ri, struct pt_regs *regs, + rh_t rh, struct dentry *dentry) +{ + struct pd_t *pd = _get_process_data(ri->rp); + struct hd_t *hd; + + if (dentry == NULL) + return 0; + + hd = lpd_get_hd(pd, dentry); + if (hd == NULL) + return 0; + + switch (lpd_get_state(hd)) { + case NOT_LOADED: + break; + case LOADING: + loader_loading_ret(ri, regs, pd, hd); + rh(ri, regs, hd); /* TODO Think about: Possible only if we + * do not need _set_in_handler() */ + break; + case LOADED: + /* TODO Check does we need this if library is loaded + * with RTLD_NOW ? */ + _restore_exec(ri, hd); + break; + case FAILED: + loader_failed_ret(ri, regs, pd, hd); + break; + case ERROR: + default: + break; + } + + return 0; +} + +static int dl_fixup_rh(struct uretprobe_instance *ri, struct pt_regs *regs) +{ + return _process_rh(ri, regs, &_redirect_to_fixup_handler, + _handler_fixup.dentry); +} + +/* TODO Make ordinary interface. Now real data_size is set in init, because + * it is unknown in this module during compile time. */ +static struct probe_desc pin_fixup = MAKE_URPROBE(dl_fixup_eh, dl_fixup_rh, 0); + + +static int dl_reloc_eh(struct uretprobe_instance *ri, struct pt_regs *regs) +{ + return _process_eh(ri, regs, &_redirect_to_reloc_handler, + _handler_reloc.dentry); +} + +static int dl_reloc_rh(struct uretprobe_instance *ri, struct pt_regs *regs) +{ + return _process_rh(ri, regs, &_redirect_to_reloc_handler, + _handler_reloc.dentry); +} + +/* TODO Make ordinary interface. Now real data_size is set in init, because + * it is unknown in this module during compile time. */ +static struct probe_desc pin_reloc = MAKE_URPROBE(dl_reloc_eh, dl_reloc_rh, 0); + + +static int pthread_init_eh(struct uretprobe_instance *ri, struct pt_regs *regs) +{ + struct pd_t *pd = _get_process_data(ri->rp); + + /* Wait until pthread is inited, if it is going to be inited before + * loading our library */ + lpd_set_init_state(pd, false); + + return 0; +} + +static int pthread_init_rh(struct uretprobe_instance *ri, struct pt_regs *regs) +{ + struct pd_t *pd = _get_process_data(ri->rp); + + lpd_set_init_state(pd, true); + + return 0; +} + +/* TODO Make ordinary interface. Now real data_size is set in init, because + * it is unknown in this module during compile time. */ +static struct probe_desc pin_pinit = MAKE_URPROBE(pthread_init_eh, + pthread_init_rh, 0); + + +static int _register_probe_el_no_lock(struct l_probe_el *l_probe) +{ + struct probe_new p_fixup; + struct probe_new p_reloc; + struct probe_new p_init; + int ret; + + p_fixup.desc = &pin_fixup; + p_fixup.offset = _linker_fixup.off; + + ret = pin_register(&p_fixup, l_probe->pfg, _linker_fixup.dentry); + if (ret != 0) { + printk(GT_PREFIX "Error register linker fixup probe. " + "Linker dentry %s, " + "dl_fixup() offset = 0x%lx\n", + _linker_fixup.dentry->d_name.name, + _linker_fixup.off); + + return ret; + } + + p_reloc.desc = &pin_reloc; + p_reloc.offset = _linker_reloc.off; + + ret = pin_register(&p_reloc, l_probe->pfg, _linker_reloc.dentry); + if (ret != 0) { + printk(GT_PREFIX "Error register linker reloc probe. " + "Linker dentry %s, " + "dl_reloc() offset = 0x%lx\n", + _linker_reloc.dentry->d_name.name, + _linker_reloc.off); + + goto reg_probe_el_fail; + } + + p_init.desc = &pin_pinit; + p_init.offset = _pthread_init.off; + + ret = pin_register(&p_init, l_probe->pfg, _pthread_init.dentry); + if (ret != 0) { + printk(GT_PREFIX "Error register pthread minimal init. " + "Pthread dentry %s, " + "__pthread_minimal_init() offset = 0x%lx\n", + _pthread_init.dentry->d_name.name, + _pthread_init.off); + + goto reg_probe_pthread_fail; + } + + return 0; + +reg_probe_pthread_fail: + pin_unregister(&p_reloc, l_probe->pfg, _linker_reloc.dentry); + +reg_probe_el_fail: + pin_unregister(&p_fixup, l_probe->pfg, _linker_fixup.dentry); + + return ret; +} + +static int _unregister_probe_el_no_lock(struct l_probe_el *l_probe) +{ + struct probe_new p_fixup; + struct probe_new p_reloc; + struct probe_new p_init; + int ret; + + p_init.desc = &pin_pinit; + p_init.offset = _pthread_init.off; + + ret = pin_unregister(&p_init, l_probe->pfg, _pthread_init.dentry); + if (ret != 0) { + printk(GT_PREFIX "Error unregister pthread minimal init. " + "Pthread dentry %s, " + "__pthread_minimal_init() offset = 0x%lx\n", + _pthread_init.dentry->d_name.name, + _pthread_init.off); + return ret; + } + + p_reloc.desc = &pin_reloc; + p_reloc.offset = _linker_reloc.off; + + ret = pin_unregister(&p_reloc, l_probe->pfg, _linker_reloc.dentry); + if (ret != 0) { + printk(GT_PREFIX "Error unregister linker reloc probe. " + "Linker dentry %s, " + "dl_fixup() offset = 0x%lx\n", + _linker_reloc.dentry->d_name.name, + _linker_reloc.off); + return ret; + } + + p_fixup.desc = &pin_fixup; + p_fixup.offset = _linker_fixup.off; + + ret = pin_unregister(&p_fixup, l_probe->pfg, _linker_fixup.dentry); + if (ret != 0) { + printk(GT_PREFIX "Error unregister linker fixup probe. " + "Linker dentry %s, " + "dl_fixup() offset = 0x%lx\n", + _linker_fixup.dentry->d_name.name, + _linker_fixup.off); + return ret; + } + + return 0; +} + +static int _disable_got_patcher(void) +{ + struct l_probe_el *l_probe; + int ret = 0; + + if (!_is_linker_data_available()) + return -EINVAL; + + _lock_probes_list(); + + list_for_each_entry(l_probe, &_reg_probes, list) { + ret = _unregister_probe_el_no_lock(l_probe); + if (ret == 0) + list_move_tail(&l_probe->list, &_unreg_probes); + } + + _unlock_probes_list(); + + _status = GT_OFF; + + return ret; +} + +static int _enable_got_patcher(void) +{ + struct l_probe_el *l_probe; + int ret; + + if (!_is_linker_data_available() || !_is_handler_data_available() || + !_is_pthread_data_available()) + return -EINVAL; + + _lock_probes_list(); + + list_for_each_entry(l_probe, &_unreg_probes, list) { + ret = _register_probe_el_no_lock(l_probe); + if (ret == 0) + list_move_tail(&l_probe->list, &_reg_probes); + else + goto enable_patcher_fail_unlock; + } + + _unlock_probes_list(); + + _status = GT_ON; + + return 0; +enable_patcher_fail_unlock: + _unlock_probes_list(); + + printk(GT_PREFIX "Error registering linker probes, disabling..."); + _disable_got_patcher(); + + return ret; +} + + +/* =========================================================================== + * = LIST MANIPULATIONS = + * =========================================================================== + */ +static struct l_probe_el *_create_linker_probe_el(void) +{ + struct l_probe_el *l_probe; + + l_probe = kzalloc(sizeof(*l_probe), GFP_KERNEL); + if (l_probe == NULL) + return NULL; + + INIT_LIST_HEAD(&l_probe->list); + + return l_probe; +} + +static void _destroy_linker_probe_el_no_lock(struct l_probe_el *l_probe) +{ + if (l_probe->pfg) + put_pf_group(l_probe->pfg); + + if (l_probe->task_id == GT_SET_BY_DENTRY && l_probe->dentry != NULL) + put_dentry(l_probe->dentry); + + kfree(l_probe); +} + +static void _clean_linker_probes(void) +{ + struct l_probe_el *l_probe, *n; + int ret; + + _lock_probes_list(); + + list_for_each_entry_safe(l_probe, n, &_reg_probes, list) { + ret = _unregister_probe_el_no_lock(l_probe); + if (ret != 0) + printk(GT_PREFIX "Cannot remove linker probe!\n"); + list_del(&l_probe->list); + _destroy_linker_probe_el_no_lock(l_probe); + } + + list_for_each_entry_safe(l_probe, n, &_unreg_probes, list) { + list_del(&l_probe->list); + _destroy_linker_probe_el_no_lock(l_probe); + } + + _unlock_probes_list(); +} + + +/* =========================================================================== + * = STRING MANIPULATIONS = + * =========================================================================== + */ + +static size_t _get_dentry_len(struct dentry *dentry) +{ + return strnlen(dentry->d_name.name, PATH_MAX); +} + +static size_t _get_pid_len(pid_t pid) +{ + /* FIXME Return constant - maximum digits for 10-based unsigned long on + * 64 bit architecture to avoid complicated evaluations. */ + return 20; +} + +static size_t _get_item_len(struct l_probe_el *l_probe) +{ + if (l_probe->task_id == GT_SET_BY_DENTRY) + return _get_dentry_len(l_probe->dentry); + else if (l_probe->task_id == GT_SET_BY_PID) + return _get_pid_len(l_probe->tgid); + + printk(GT_PREFIX "Error! Unknown process identificator!\n"); + return 0; +} + +static char *_copy_dentry_item(char *dest, struct dentry *dentry) +{ + size_t len; + + len = strnlen(dentry->d_name.name, PATH_MAX); + memcpy(dest, dentry->d_name.name, len); + + return (dest + len); +} + +static char *_copy_pid_item(char *dest, pid_t pid) +{ + int ret; + size_t len; + + len = _get_pid_len(pid); + + ret = snprintf(dest, len, "%lu", (unsigned long)pid); + if (ret >= len) + printk(GT_PREFIX "PID string is truncated!\n"); + + return (dest + ret); +} + +static char *_copy_item(char *dest, struct l_probe_el *l_probe) +{ + if (l_probe->task_id == GT_SET_BY_DENTRY) + return _copy_dentry_item(dest, l_probe->dentry); + else if (l_probe->task_id == GT_SET_BY_PID) + return _copy_pid_item(dest, l_probe->tgid); + + printk(GT_PREFIX "Error! Unknown process identificator!\n"); + return dest; +} + +static char *_add_separator(char *dest) +{ + const char sep = ':'; + + *dest = sep; + + return (dest + 1); +} + + + + +int gtm_set_linker_path(char *path) +{ + struct dentry *dentry; + + dentry = get_dentry(path); + if (dentry == NULL) + return -EINVAL; + + /* We use get_dentry only once, so use put dentry also only once */ + if (_linker_fixup.dentry != NULL || + _linker_reloc.dentry != NULL) { + if (_linker_fixup.dentry != NULL) + put_dentry(_linker_fixup.dentry); + else + put_dentry(_linker_reloc.dentry); + } + + _linker_fixup.dentry = dentry; + _linker_reloc.dentry = dentry; + + return 0; +} + +int gtm_set_fixup_off(unsigned long offset) +{ + _linker_fixup.off = offset; + + return 0; +} + +int gtm_set_reloc_off(unsigned long offset) +{ + _linker_reloc.off = offset; + + return 0; +} + +int gtm_switch(enum gt_status stat) +{ + int ret; + + switch (stat) { + case GT_ON: + ret = _enable_got_patcher(); + break; + case GT_OFF: + ret = _disable_got_patcher(); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +enum gt_status gtm_status(void) +{ + return _status; +} + +int gtm_add_by_path(char *path) +{ + struct dentry *dentry; + struct pf_group *pfg; + struct l_probe_el *l_probe; + int ret; + + dentry = get_dentry(path); + if (dentry == NULL) + return -EINVAL; + + pfg = get_pf_group_by_dentry(dentry, dentry); + if (pfg == NULL) { + ret = -ENOMEM; + goto add_by_path_put_dentry; + } + + l_probe = _create_linker_probe_el(); + if (l_probe == NULL) { + ret = -ENOMEM; + goto add_by_path_put_pfg; + } + + l_probe->pfg = pfg; + l_probe->task_id = GT_SET_BY_DENTRY; + l_probe->dentry = dentry; + + _lock_probes_list(); + if (_is_enable()) { + ret = _register_probe_el_no_lock(l_probe); + if (ret == 0) + list_add_tail(&l_probe->list, &_reg_probes); + else + goto add_by_path_unlock; + } else { + list_add_tail(&l_probe->list, &_unreg_probes); + } + _unlock_probes_list(); + + return 0; + +add_by_path_unlock: + _unlock_probes_list(); + kfree(l_probe); + +add_by_path_put_pfg: + put_pf_group(pfg); + +add_by_path_put_dentry: + put_dentry(dentry); + + return ret; +} + +int gtm_add_by_pid(pid_t pid) +{ + struct pf_group *pfg; + struct l_probe_el *l_probe; + int ret; + + /* TODO Add dentry as private data */ + pfg = get_pf_group_by_tgid(pid, NULL); + if (pfg == NULL) + return -ENOMEM; + + l_probe = _create_linker_probe_el(); + if (l_probe == NULL) { + ret = -ENOMEM; + goto add_by_pid_put_pfg; + } + + l_probe->pfg = pfg; + l_probe->task_id = GT_SET_BY_PID; + l_probe->tgid = pid; + + _lock_probes_list(); + if (_is_enable()) { + ret = _register_probe_el_no_lock(l_probe); + if (ret == 0) + list_add_tail(&l_probe->list, &_reg_probes); + else + goto add_by_pid_unlock; + } else { + list_add_tail(&l_probe->list, &_unreg_probes); + } + _unlock_probes_list(); + + return 0; + +add_by_pid_unlock: + _unlock_probes_list(); + kfree(l_probe); + +add_by_pid_put_pfg: + put_pf_group(pfg); + + return ret; +} + +int gtm_del_by_path(char *path) +{ + struct l_probe_el *l_probe, *n; + struct dentry *dentry; + int ret = 0; + + dentry = get_dentry(path); + if (dentry == NULL) + return -EINVAL; + + _lock_probes_list(); + + list_for_each_entry_safe(l_probe, n, &_reg_probes, list) { + if (l_probe->task_id == GT_SET_BY_DENTRY && + l_probe->dentry == dentry) { + ret = _unregister_probe_el_no_lock(l_probe); + if (ret != 0) + goto del_by_path_unlock; + list_del(&l_probe->list); + _destroy_linker_probe_el_no_lock(l_probe); + goto del_by_path_unlock; + } + } + + list_for_each_entry_safe(l_probe, n, &_unreg_probes, list) { + if (l_probe->task_id == GT_SET_BY_DENTRY && + l_probe->dentry == dentry) { + list_del(&l_probe->list); + _destroy_linker_probe_el_no_lock(l_probe); + goto del_by_path_unlock; + } + } + +del_by_path_unlock: + _unlock_probes_list(); + + return ret; +} + +int gtm_del_by_pid(pid_t pid) +{ + struct l_probe_el *l_probe, *n; + int ret = 0; + + _lock_probes_list(); + + list_for_each_entry_safe(l_probe, n, &_reg_probes, list) { + if (l_probe->task_id == GT_SET_BY_PID && + l_probe->tgid == pid) { + ret = _unregister_probe_el_no_lock(l_probe); + if (ret != 0) + goto del_by_pid_unlock; + list_del(&l_probe->list); + _destroy_linker_probe_el_no_lock(l_probe); + goto del_by_pid_unlock; + } + } + + list_for_each_entry_safe(l_probe, n, &_unreg_probes, list) { + if (l_probe->task_id == GT_SET_BY_PID && + l_probe->tgid == pid) { + list_del(&l_probe->list); + _destroy_linker_probe_el_no_lock(l_probe); + goto del_by_pid_unlock; + } + } + +del_by_pid_unlock: + _unlock_probes_list(); + + return ret; +} + +int gtm_del_all(void) +{ + struct l_probe_el *l_probe, *n; + int ret = 0; + + _lock_probes_list(); + + list_for_each_entry_safe(l_probe, n, &_reg_probes, list) { + ret = _unregister_probe_el_no_lock(l_probe); + if (ret != 0) + goto del_all_fail; + list_del(&l_probe->list); + _destroy_linker_probe_el_no_lock(l_probe); + } + + list_for_each_entry_safe(l_probe, n, &_unreg_probes, list) { + list_del(&l_probe->list); + _destroy_linker_probe_el_no_lock(l_probe); + } + +del_all_fail: + _unlock_probes_list(); + + return ret; +} + +ssize_t gtm_get_targets(char **targets) +{ + struct l_probe_el *l_probe; + char *ptr; + size_t len = 0; + + _lock_probes_list(); + list_for_each_entry(l_probe, &_reg_probes, list) { + /* Add separator */ + len += _get_item_len(l_probe) + 1; + } + + list_for_each_entry(l_probe, &_unreg_probes, list) { + /* Add separator */ + len += _get_item_len(l_probe) + 1; + } + _unlock_probes_list(); + + *targets = kmalloc(len, GFP_KERNEL); + if (*targets == NULL) + return -ENOMEM; + + ptr = *targets; + + _lock_probes_list(); + + list_for_each_entry(l_probe, &_reg_probes, list) { + ptr = _copy_item(ptr, l_probe); + ptr = _add_separator(ptr); + } + + list_for_each_entry(l_probe, &_unreg_probes, list) { + ptr = _copy_item(ptr, l_probe); + ptr = _add_separator(ptr); + } + + _unlock_probes_list(); + + *targets[len - 1] = '\0'; + + return len; +} + +int gtm_set_handler_path(char *path) +{ + struct dentry *dentry; + int ret; + + /* We use get_dentry only once, so use put dentry also only once */ + dentry = get_dentry(path); + if (dentry == NULL) + return -EINVAL; + + if (_handler_fixup.dentry != NULL || + _handler_reloc.dentry != NULL) { + if (_handler_fixup.dentry != NULL) + put_dentry(_handler_fixup.dentry); + else + put_dentry(_handler_reloc.dentry); + } + + _handler_fixup.dentry = dentry; + _handler_reloc.dentry = dentry; + + /* TODO Do smth with this: + * make interface for loader to remove handlers + * and add/delete when gtm_set_handler() is called + * Check container_of() may be useful */ + ret = loader_add_handler(path); + + return ret; +} + +int gtm_set_handler_fixup_off(unsigned long offset) +{ + _handler_fixup.off = offset; + + return 0; +} + +int gtm_set_handler_reloc_off(unsigned long offset) +{ + _handler_reloc.off = offset; + + return 0; +} + +int gtm_set_pthread_path(char *path) +{ + struct dentry *dentry; + + dentry = get_dentry(path); + if (dentry == NULL) + return -EINVAL; + + if (_pthread_init.dentry != NULL) + put_dentry(_pthread_init.dentry); + + _pthread_init.dentry = dentry; + + return 0; +} + +int gtm_set_init_off(unsigned long offset) +{ + _pthread_init.off = offset; + + return 0; +} + +static int gtm_init(void) +{ + int ret; + + ret = gtd_init(); + if (ret) + goto gtd_init_err; + + ret = swap_ktd_reg(>_ktd); + if (ret) + goto gtd_ktd_reg_err; + + return 0; + +gtd_ktd_reg_err: + gtd_exit(); + +gtd_init_err: + return ret; +} + +static void gtm_exit(void) +{ + swap_ktd_unreg(>_ktd); + gtd_exit(); + + _clean_linker_probes(); + + if (_handler_fixup.dentry != NULL || + _handler_reloc.dentry != NULL) { + if (_handler_fixup.dentry != NULL) + put_dentry(_handler_fixup.dentry); + else + put_dentry(_handler_reloc.dentry); + } +} + +SWAP_LIGHT_INIT_MODULE(NULL, gtm_init, gtm_exit, NULL, NULL); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SWAP GOT Patcher Module"); +MODULE_AUTHOR("Alexander Aksenov "); diff --git a/got_patcher/gt_module.h b/got_patcher/gt_module.h new file mode 100644 index 0000000..da62b57 --- /dev/null +++ b/got_patcher/gt_module.h @@ -0,0 +1,37 @@ +#ifndef __GT_MODULE_H__ +#define __GT_MODULE_H__ + +#include + +enum gt_status { + GT_ON, + GT_OFF +}; + +/* Linker data */ +int gtm_set_linker_path(char *path); +int gtm_set_fixup_off(unsigned long offset); +int gtm_set_reloc_off(unsigned long offset); +int gtm_switch(enum gt_status stat); +enum gt_status gtm_status(void); + +/* Target processes data */ +int gtm_add_by_path(char *path); +int gtm_add_by_pid(pid_t pid); +int gtm_del_by_path(char *path); +int gtm_del_by_pid(pid_t pid); +int gtm_del_all(void); +/* Returns len on success, error code on fail. + * Allocates memory with malloc(), caller should free it */ +ssize_t gtm_get_targets(char **targets); + +/* Handlers data */ +int gtm_set_handler_path(char *path); +int gtm_set_handler_fixup_off(unsigned long offset); +int gtm_set_handler_reloc_off(unsigned long offset); + +/* Pthread data */ +int gtm_set_pthread_path(char *path); +int gtm_set_init_off(unsigned long offset); + +#endif /* __GT_MODULE_H__ */ diff --git a/loader/Kbuild b/loader/Kbuild index f6cd9f7..92e5e6f 100644 --- a/loader/Kbuild +++ b/loader/Kbuild @@ -4,5 +4,4 @@ obj-m := swap_loader.o swap_loader-y := loader_module.o \ loader_debugfs.o \ loader_storage.o \ - loader_control.o \ loader_pd.o diff --git a/loader/loader.h b/loader/loader.h index ddd550a..fc5aed9 100644 --- a/loader/loader.h +++ b/loader/loader.h @@ -34,6 +34,10 @@ int loader_add_handler(char *path); struct pd_t *lpd_get(struct sspt_proc *proc); struct hd_t *lpd_get_hd(struct pd_t *pd, struct dentry *dentry); + +bool lpd_get_init_state(struct pd_t *pd); +void lpd_set_init_state(struct pd_t *pd, bool state); + struct dentry *lpd_get_dentry(struct hd_t *hd); struct pd_t *lpd_get_parent_pd(struct hd_t *hd); enum ps_t lpd_get_state(struct hd_t *hd); @@ -42,4 +46,5 @@ void *lpd_get_handle(struct hd_t *hd); long lpd_get_attempts(struct hd_t *hd); void lpd_dec_attempts(struct hd_t *hd); + #endif /* __LOADER__ */ diff --git a/loader/loader_control.c b/loader/loader_control.c deleted file mode 100644 index 64b39d6..0000000 --- a/loader/loader_control.c +++ /dev/null @@ -1,213 +0,0 @@ -#include -#include -#include -#include -#include "loader_defs.h" -#include "loader_control.h" -#include "loader_module.h" - -struct bin_desc { - struct list_head list; - struct dentry *dentry; - char *filename; -}; - -struct list_desc { - struct list_head list; - rwlock_t lock; - int cnt; -}; - -static struct list_desc ignored = { - .list = LIST_HEAD_INIT(ignored.list), - .lock = __RW_LOCK_UNLOCKED(&ignored.lock), - .cnt = 0 -}; - -static struct bin_desc *__alloc_binary(struct dentry *dentry, char *name, - int namelen) -{ - struct bin_desc *p = NULL; - - p = kzalloc(sizeof(*p), GFP_KERNEL); - if (!p) - return NULL; - - INIT_LIST_HEAD(&p->list); - p->filename = kmalloc(namelen + 1, GFP_KERNEL); - if (!p->filename) - goto fail; - memcpy(p->filename, name, namelen); - p->filename[namelen] = '\0'; - p->dentry = dentry; - - return p; -fail: - kfree(p); - return NULL; -} - -static void __free_binary(struct bin_desc *p) -{ - kfree(p->filename); - kfree(p); -} - -static void __free_ign_binaries(void) -{ - struct bin_desc *p, *n; - struct list_head rm_head; - - INIT_LIST_HEAD(&rm_head); - write_lock(&ignored.lock); - list_for_each_entry_safe(p, n, &ignored.list, list) { - list_move(&p->list, &rm_head); - } - ignored.cnt = 0; - write_unlock(&ignored.lock); - - list_for_each_entry_safe(p, n, &rm_head, list) { - list_del(&p->list); - put_dentry(p->dentry); - __free_binary(p); - } -} - -static bool __check_dentry_already_exist(struct dentry *dentry) -{ - struct bin_desc *p; - bool ret = false; - - read_lock(&ignored.lock); - list_for_each_entry(p, &ignored.list, list) { - if (p->dentry == dentry) { - ret = true; - goto out; - } - } -out: - read_unlock(&ignored.lock); - - return ret; -} - -static int __add_ign_binary(struct dentry *dentry, char *filename) -{ - struct bin_desc *p; - size_t len; - - if (__check_dentry_already_exist(dentry)) { - printk(LOADER_PREFIX "Binary already exist\n"); - return EALREADY; - } - - /* Filename should be < PATH_MAX */ - len = strnlen(filename, PATH_MAX); - if (len == PATH_MAX) - return -EINVAL; - - p = __alloc_binary(dentry, filename, len); - if (!p) - return -ENOMEM; - - write_lock(&ignored.lock); - list_add_tail(&p->list, &ignored.list); - ignored.cnt++; - write_unlock(&ignored.lock); - - return 0; -} - -static unsigned int __get_ign_names(char ***filenames_p) -{ - unsigned int i, ret = 0; - struct bin_desc *p; - char **a = NULL; - - read_lock(&ignored.lock); - if (ignored.cnt == 0) - goto out; - - a = kmalloc(sizeof(*a) * ignored.cnt, GFP_KERNEL); - if (!a) - goto out; - - i = 0; - list_for_each_entry(p, &ignored.list, list) { - if (i >= ignored.cnt) - break; - a[i++] = p->filename; - } - - *filenames_p = a; - ret = i; -out: - read_unlock(&ignored.lock); - return ret; -} - - - -int lc_add_ignored_binary(char *filename) -{ - struct dentry *dentry = get_dentry(filename); - int res = 0; - - if (dentry == NULL) - return -EINVAL; - - res = __add_ign_binary(dentry, filename); - if (res != 0) - put_dentry(dentry); - - return res > 0 ? 0 : res; -} - -int lc_clean_ignored_bins(void) -{ - __free_ign_binaries(); - - return 0; -} - -unsigned int lc_get_ignored_names(char ***filenames_p) -{ - return __get_ign_names(filenames_p); -} - -void lc_release_ignored_names(char ***filenames_p) -{ - kfree(*filenames_p); -} - -bool lc_check_dentry_is_ignored(struct dentry *dentry) -{ - struct bin_desc *p; - bool ret = false; - - if (dentry == NULL) - return false; - - read_lock(&ignored.lock); - - list_for_each_entry(p, &ignored.list, list) { - if (p->dentry == dentry) { - ret = true; - break; - } - } - - read_unlock(&ignored.lock); - - return ret; -} - -int lc_init(void) -{ - return 0; -} - -void lc_exit(void) -{ - __free_ign_binaries(); -} diff --git a/loader/loader_control.h b/loader/loader_control.h deleted file mode 100644 index 37988df..0000000 --- a/loader/loader_control.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __LOADER_CONTROL_H__ -#define __LOADER_CONTROL_H__ - -struct dentry; - -int lc_init(void); -void lc_exit(void); - -int lc_add_ignored_binary(char *filename); -int lc_clean_ignored_bins(void); - -unsigned int lc_get_ignored_names(char ***filenames_p); -void lc_release_ignored_names(char ***filenames_p); - -bool lc_check_dentry_is_ignored(struct dentry *dentry); - -#endif /* __LOADER_CONTROL_H__ */ diff --git a/loader/loader_debugfs.c b/loader/loader_debugfs.c index 014fde7..44ad077 100644 --- a/loader/loader_debugfs.c +++ b/loader/loader_debugfs.c @@ -11,17 +11,12 @@ #include "loader_defs.h" #include "loader_debugfs.h" #include "loader_module.h" -#include "loader_control.h" #include "loader_storage.h" static const char LOADER_FOLDER[] = "loader"; static const char LOADER_LOADER[] = "loader"; static const char LOADER_LOADER_OFFSET[] = "loader_offset"; static const char LOADER_LOADER_PATH[] = "loader_path"; -static const char LOADER_IGNORED[] = "ignored_binaries"; -static const char LOADER_BINARIES_LIST[] = "bins_list"; -static const char LOADER_BINARIES_ADD[] = "bins_add"; -static const char LOADER_BINARIES_REMOVE[] = "bins_remove"; static const char LOADER_CALLER[] = "caller"; static const char LOADER_LINKER_DATA[] = "linker"; static const char LOADER_LINKER_PATH[] = "linker_path"; @@ -138,12 +133,6 @@ static ssize_t loader_path_write(struct file *file, const char __user *buf, path[len - 1] = '\0'; set_loader_file(path); - ret = lc_add_ignored_binary(path); - if (ret < 0) { - printk(LOADER_PREFIX "Cannot add loader %s to ignored list\n", path); - goto err; - } - ret = len; return ret; @@ -160,125 +149,6 @@ static const struct file_operations loader_path_file_ops = { /* =========================================================================== - * = BIN PATH = - * =========================================================================== - */ - -static ssize_t bin_add_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) -{ - ssize_t ret; - char *path; - - path = kmalloc(len, GFP_KERNEL); - if (path == NULL) { - ret = -ENOMEM; - goto bin_add_write_out; - } - - if (copy_from_user(path, buf, len)) { - ret = -EINVAL; - goto bin_add_write_out; - } - - path[len - 1] = '\0'; - - ret = lc_add_ignored_binary(path); - if (ret != 0) { - printk(LOADER_PREFIX "Cannot add binary %s\n", path); - ret = -EINVAL; - goto bin_add_write_out; - } - - ret = len; - -bin_add_write_out: - kfree(path); - - return ret; -} - -static ssize_t bin_remove_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) -{ - ssize_t ret; - - ret = lc_clean_ignored_bins(); - if (ret != 0) { - printk(LOADER_PREFIX "Error during clean!\n"); - ret = -EINVAL; - goto bin_remove_write_out; - } - - ret = len; - -bin_remove_write_out: - return ret; -} - -static ssize_t bin_list_read(struct file *file, char __user *usr_buf, - size_t count, loff_t *ppos) -{ - unsigned int i; - unsigned int files_cnt = 0; - ssize_t len = 0, tmp, ret = 0; - char **filenames = NULL; - char *buf = NULL; - char *ptr = NULL; - - files_cnt = lc_get_ignored_names(&filenames); - if (files_cnt == 0) { - printk(LOADER_PREFIX "Cannot read binaries names!\n"); - ret = 0; - goto bin_list_read_fail; - } - - for (i = 0; i < files_cnt; i++) - len += strlen(filenames[i]); - - buf = kmalloc(len + files_cnt, GFP_KERNEL); - if (buf == NULL) { - ret = 0; - goto bin_list_read_fail; - } - - ptr = buf; - - for (i = 0; i < files_cnt; i++) { - tmp = strlen(filenames[i]); - memcpy(ptr, filenames[i], tmp); - ptr += tmp; - *ptr = '\n'; - ptr += 1; - } - - ret = simple_read_from_buffer(usr_buf, count, ppos, buf, len); - - kfree(buf); - -bin_list_read_fail: - lc_release_ignored_names(&filenames); - - return ret; -} - -static const struct file_operations bin_list_file_ops = { - .owner = THIS_MODULE, - .read = bin_list_read -}; - -static const struct file_operations bin_add_file_ops = { - .owner = THIS_MODULE, - .write = bin_add_write, -}; - -static const struct file_operations bin_remove_file_ops = { - .owner = THIS_MODULE, - .write = bin_remove_write, -}; - - -/* =========================================================================== * = LINKER PATH = * =========================================================================== */ @@ -309,12 +179,6 @@ static ssize_t linker_path_write(struct file *file, const char __user *buf, goto linker_path_write_out; } - ret = lc_add_ignored_binary(path); - if (ret < 0) { - printk(LOADER_PREFIX "Cannot add linker %s to ignored list\n", path); - goto linker_path_write_out; - } - ret = len; linker_path_write_out: @@ -340,9 +204,7 @@ unsigned long ld_r_debug_offset(void) int ld_init(void) { struct dentry *swap_dentry, *root, *loader, *open_p, *lib_path, - *ignored_path, *linker_dir, *linker_path, - *linker_offset, *ignored_list, - *ignored_add, *ignored_remove; + *linker_dir, *linker_path, *linker_offset; int ret; ret = -ENODEV; @@ -381,36 +243,6 @@ int ld_init(void) goto remove; } - ignored_path = debugfs_create_dir(LOADER_IGNORED, root); - if (IS_ERR_OR_NULL(ignored_path)) { - ret = -ENOMEM; - goto remove; - } - - ignored_list = debugfs_create_file(LOADER_BINARIES_LIST, - LOADER_DEFAULT_PERMS, ignored_path, - NULL, &bin_list_file_ops); - if (IS_ERR_OR_NULL(ignored_list)) { - ret = -ENOMEM; - goto remove; - } - - ignored_add = debugfs_create_file(LOADER_BINARIES_ADD, - LOADER_DEFAULT_PERMS, ignored_path, NULL, - &bin_add_file_ops); - if (IS_ERR_OR_NULL(ignored_add)) { - ret = -ENOMEM; - goto remove; - } - - ignored_remove = debugfs_create_file(LOADER_BINARIES_REMOVE, - LOADER_DEFAULT_PERMS, ignored_path, NULL, - &bin_remove_file_ops); - if (IS_ERR_OR_NULL(ignored_remove)) { - ret = -ENOMEM; - goto remove; - } - linker_dir = debugfs_create_dir(LOADER_LINKER_DATA, root); if (IS_ERR_OR_NULL(linker_dir)) { ret = -ENOMEM; diff --git a/loader/loader_module.c b/loader/loader_module.c index ff9e125..e5efd43 100644 --- a/loader/loader_module.c +++ b/loader/loader_module.c @@ -16,7 +16,6 @@ #include "loader_module.h" #include "loader.h" #include "loader_storage.h" -#include "loader_control.h" #include "loader_pd.h" @@ -229,18 +228,6 @@ static struct vm_area_struct *__get_linker_vma(struct task_struct *task) return NULL; } -static inline struct vm_area_struct *__get_vma_by_addr(struct task_struct *task, - unsigned long caller_addr) -{ - struct vm_area_struct *vma = NULL; - - if (task->mm == NULL) - return NULL; - vma = find_vma_intersection(task->mm, caller_addr, caller_addr + 1); - - return vma; -} - @@ -280,13 +267,7 @@ static bool __is_proc_mmap_mappable(struct task_struct *task) static bool __should_we_load_handlers(struct task_struct *task, struct pt_regs *regs) { - unsigned long caller_addr = get_regs_ret_func(regs); - struct vm_area_struct *cvma = __get_vma_by_addr(current, caller_addr); - - if (!__is_proc_mmap_mappable(task) || - ((cvma != NULL) && (cvma->vm_file != NULL) && - (cvma->vm_file->f_path.dentry != NULL) && - lc_check_dentry_is_ignored(cvma->vm_file->f_path.dentry))) + if (!__is_proc_mmap_mappable(task)) return false; return true; @@ -608,10 +589,6 @@ static int loader_module_init(void) if (ret) goto exit_pd; - ret = lc_init(); - if (ret) - goto exit_set; - __loader_cbs_start_h = us_manager_reg_cb(START_CB, loader_start_cb); if (__loader_cbs_start_h < 0) goto exit_start_cb; @@ -626,9 +603,6 @@ exit_stop_cb: us_manager_unreg_cb(__loader_cbs_start_h); exit_start_cb: - lc_exit(); - -exit_set: loader_unset(); exit_pd: @@ -650,7 +624,6 @@ static void loader_module_exit(void) us_manager_unreg_cb(__loader_cbs_start_h); us_manager_unreg_cb(__loader_cbs_stop_h); - lc_exit(); loader_unset(); lpd_uninit(); ls_exit(); diff --git a/loader/loader_pd.c b/loader/loader_pd.c index 418453f..505565d 100644 --- a/loader/loader_pd.c +++ b/loader/loader_pd.c @@ -19,6 +19,8 @@ struct pd_t { unsigned long loader_base; unsigned long data_page; struct list_head handlers; + bool is_pthread_init; /* TODO Move to GOT patcher, because + * only it uses this field */ }; struct hd_t { @@ -144,7 +146,7 @@ static struct pd_t *__create_pd(void) } __set_data_page(pd, page); - + pd->is_pthread_init = true; INIT_LIST_HEAD(&pd->handlers); return pd; @@ -383,6 +385,20 @@ struct hd_t *lpd_get_hd(struct pd_t *pd, struct dentry *dentry) } EXPORT_SYMBOL_GPL(lpd_get_hd); +/* TODO Move it to GOT patcher, only it uses this */ +bool lpd_get_init_state(struct pd_t *pd) +{ + return pd->is_pthread_init; +} +EXPORT_SYMBOL_GPL(lpd_get_init_state); + +/* TODO Move it to GOT patcher, only it uses this */ +void lpd_set_init_state(struct pd_t *pd, bool state) +{ + pd->is_pthread_init = state; +} +EXPORT_SYMBOL_GPL(lpd_set_init_state); + static struct pd_t *do_create_pd(struct task_struct *task) { struct pd_t *pd; diff --git a/packaging/swap-modules.spec b/packaging/swap-modules.spec index 0ce8622..900718e 100755 --- a/packaging/swap-modules.spec +++ b/packaging/swap-modules.spec @@ -79,6 +79,7 @@ install -m 666 fbiprobe/swap_fbiprobe.ko -t %{buildroot}/opt/swap/sdk install -m 666 wsp/swap_wsp.ko -t %{buildroot}/opt/swap/sdk install -m 666 nsp/swap_nsp.ko -t %{buildroot}/opt/swap/sdk install -m 666 task_ctx/swap_taskctx.ko -t %{buildroot}/opt/swap/sdk +install -m 666 got_patcher/swap_gtp.ko -t %{buildroot}/opt/swap/sdk mkdir -p %{buildroot}/usr/share/license cp LICENSE.GPL-2.0+ %{buildroot}/usr/share/license/%{name} @@ -108,3 +109,4 @@ cp LICENSE.GPL-2.0+ %{buildroot}/usr/share/license/%{name} /opt/swap/sdk/swap_wsp.ko /opt/swap/sdk/swap_nsp.ko /opt/swap/sdk/swap_taskctx.ko +/opt/swap/sdk/swap_gtp.ko diff --git a/preload/preload_module.c b/preload/preload_module.c index e1c1f0c..e0a80b7 100644 --- a/preload/preload_module.c +++ b/preload/preload_module.c @@ -320,16 +320,17 @@ static int write_msg_handler(struct uprobe *p, struct pt_regs *regs) caller_p = (char *)swap_get_uarg(regs, 3); caller_addr = swap_get_uarg(regs, 4); - ret = __msg_sanitization(user_buf, len, call_type_p, caller_p); - if (ret != 0) { - printk(PRELOAD_PREFIX "Invalid message pointers!\n"); - return 0; + if (call_type_p != NULL || caller_p != NULL) { + ret = __msg_sanitization(user_buf, len, call_type_p, caller_p); + if (ret != 0) { + printk(PRELOAD_PREFIX "Invalid message pointers!\n"); + return 0; + } + ret = pt_get_drop(current); + if (ret > 0) + return 0; } - ret = pt_get_drop(current); - if (ret > 0) - return 0; - buf = kmalloc(len, GFP_ATOMIC); if (buf == NULL) { printk(PRELOAD_PREFIX "No mem for buffer! Size = %d\n", len); @@ -344,14 +345,17 @@ static int write_msg_handler(struct uprobe *p, struct pt_regs *regs) goto write_msg_fail; } - /* Evaluating call_type and caller offset in message: - * data offset = data pointer - beginning of the message. - */ - call_type_offset = (unsigned long)(call_type_p - user_buf); - caller_offset = (unsigned long)(caller_p - user_buf); - __write_data_to_msg(buf, len, call_type_offset, caller_offset, - caller_addr); + if (call_type_p != NULL || caller_p != NULL) { + /* Evaluating call_type and caller offset in message: + * data offset = data pointer - beginning of the message. + */ + call_type_offset = (unsigned long)(call_type_p - user_buf); + caller_offset = (unsigned long)(caller_p - user_buf); + + __write_data_to_msg(buf, len, call_type_offset, caller_offset, + caller_addr); + } ret = swap_msg_raw(buf, len); if (ret != len) diff --git a/us_manager/pf/pf_group.c b/us_manager/pf/pf_group.c index 0850323..38cfb85 100644 --- a/us_manager/pf/pf_group.c +++ b/us_manager/pf/pf_group.c @@ -216,8 +216,6 @@ static void msg_info(struct sspt_filter *f, void *data) static void first_install(struct task_struct *task, struct sspt_proc *proc) { - sspt_proc_priv_create(proc); - down_write(&task->mm->mmap_sem); sspt_proc_on_each_filter(proc, msg_info, NULL); sspt_proc_install(proc); @@ -572,6 +570,13 @@ void check_task_and_install(struct task_struct *task) flag = pfg_check_task(task); switch (flag) { case PIF_FIRST: + proc = sspt_proc_get_by_task(task); + if (proc) { + sspt_proc_priv_create(proc); + first_install(task, proc); + sspt_proc_put(proc); + } + break; case PIF_ADD_PFG: proc = sspt_proc_get_by_task(task); if (proc) { @@ -601,6 +606,13 @@ void call_page_fault(struct task_struct *task, unsigned long page_addr) flag = pfg_check_task(task); switch (flag) { case PIF_FIRST: + proc = sspt_proc_get_by_task(task); + if (proc) { + sspt_proc_priv_create(proc); + first_install(task, proc); + sspt_proc_put(proc); + } + break; case PIF_ADD_PFG: proc = sspt_proc_get_by_task(task); if (proc) { -- 2.7.4