parser/ \
retprobe/ \
webprobe/ \
- task_data/
+ task_data/ \
+ preload/
retprobe_dir=${modules_dir}/retprobe
webprobe_dir=${modules_dir}/webprobe
task_data_dir=${modules_dir}/task_data
+preload_dir=${modules_dir}/preload
buffer_module_name=swap_buffer.ko
driver_module_name=swap_driver.ko
retprobe_module_name=swap_retprobe.ko
webprobe_module_name=swap_webprobe.ko
task_data_module_name=swap_task_data.ko
+preload_module_name=swap_preload.ko
install_dir="/opt/swap/sdk"
${parser_dir}/${parser_module_name} \
${ksyms_dir}/${ksyms_module_name} \
${retprobe_dir}/${retprobe_module_name} \
-${webprobe_dir}/${webprobe_module_name}"
+${webprobe_dir}/${webprobe_module_name} \
+${task_data_dir}/${task_data_module_name} \
+${preload_dir}/${preload_module_name}"
for m in ${modules} ; do
${cross_compile}strip -x -g $m
-# sdb -e push $m ${install_dir}
+# sdb -e push $m ${install_dir}
done
install -m 666 parser/swap_message_parser.ko -t %{buildroot}/opt/swap/sdk
install -m 666 retprobe/swap_retprobe.ko -t %{buildroot}/opt/swap/sdk
install -m 666 webprobe/swap_webprobe.ko -t %{buildroot}/opt/swap/sdk
+install -m 666 task_data/swap_task_data.ko -t %{buildroot}/opt/swap/sdk
+install -m 666 preload/swap_preload.ko -t %{buildroot}/opt/swap/sdk
%files
%defattr(-,root,root)
/opt/swap/sdk/swap_message_parser.ko
/opt/swap/sdk/swap_retprobe.ko
/opt/swap/sdk/swap_webprobe.ko
+/opt/swap/sdk/swap_task_data.ko
+/opt/swap/sdk/swap_preload.ko
put_string(pi->rp_i.args);
}
+/**
+ * @brief Gets preload data and puts it to the probe_info struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param pi Pointer to the probe_info struct.
+ * @return 0 on success, error code on error.
+ */
+int get_preload_probe(struct msg_buf *mb, struct probe_info *pi)
+{
+ u64 handler;
+ u8 type;
+
+ print_parse_debug("funct handler:");
+ if (get_u64(mb, &handler)) {
+ print_err("failed to read function handler\n");
+ return -EINVAL;
+ }
+
+ print_parse_debug("collect events type:");
+ if (get_u8(mb, &type)) {
+ print_err("failed to read collect events type\n");
+ return -EINVAL;
+ }
+
+ pi->probe_type = SWAP_PRELOAD_PROBE;
+ pi->size = sizeof(pi->pl_i) + sizeof(pi->probe_type) + sizeof(pi->size);
+ pi->pl_i.handler = handler;
+ pi->pl_i.type = type;
+
+ return 0;
+}
+
+/**
+ * @brief Preload probe data cleanup.
+ *
+ * @param pi Pointer to the probe_info comprising retprobe.
+ * @return Void.
+ */
+void put_preload_probe(struct probe_info *pi)
+{
+}
+
+/**
+ * @brief Gets preload get_caller and puts it to the probe_info struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param pi Pointer to the probe_info struct.
+ * @return 0 on success, error code on error.
+ */
+int get_get_caller_probe(struct msg_buf *mb, struct probe_info *pi)
+{
+ pi->probe_type = SWAP_GET_CALLER;
+ pi->size = sizeof(pi->gc_i);
+
+ return 0;
+}
+
+/**
+ * @brief Preload get_caller probe data cleanup.
+ *
+ * @param pi Pointer to the probe_info comprising retprobe.
+ * @return Void.
+ */
+void put_get_caller_probe(struct probe_info *pi)
+{
+}
+
+/**
+ * @brief Gets preload get_call_type and puts it to the probe_info struct.
+ *
+ * @param mb Pointer to the message buffer.
+ * @param pi Pointer to the probe_info struct.
+ * @return 0 on success, error code on error.
+ */
+int get_get_call_type_probe(struct msg_buf *mb, struct probe_info *pi)
+{
+ pi->probe_type = SWAP_GET_CALL_TYPE;
+ pi->size = sizeof(pi->gct_i);
+
+ return 0;
+}
+
+/**
+ * @brief Preload get_call type probe data cleanup.
+ *
+ * @param pi Pointer to the probe_info comprising retprobe.
+ * @return Void.
+ */
+void put_get_call_type_probe(struct probe_info *pi)
+{
+}
if (get_webprobe(mb, &(fi->probe_i)) != 0)
goto free_func_inst;
break;
+ case SWAP_PRELOAD_PROBE:
+ if (get_preload_probe(mb, &(fi->probe_i)) != 0)
+ goto free_func_inst;
+ break;
+ case SWAP_GET_CALLER:
+ if (get_get_caller_probe(mb, &(fi->probe_i)) != 0)
+ goto free_func_inst;
+ break;
+ case SWAP_GET_CALL_TYPE:
+ if (get_get_call_type_probe(mb, &(fi->probe_i)) != 0)
+ goto free_func_inst;
+ break;
default:
printk(KERN_WARNING "SWAP PARSER: Wrong probe type %d!\n", type);
goto free_func_inst;
break;
case SWAP_WEBPROBE:
break;
+ case SWAP_PRELOAD_PROBE:
+ put_preload_probe(&(fi->probe_i));
+ break;
+ case SWAP_GET_CALLER:
+ put_get_caller_probe(&(fi->probe_i));
+ break;
+ case SWAP_GET_CALL_TYPE:
+ put_get_call_type_probe(&(fi->probe_i));
+ break;
default:
printk(KERN_WARNING "SWAP PARSER: Wrong probe type %d!\n",
fi->probe_i.probe_type);
--- /dev/null
+EXTRA_CFLAGS := $(extra_cflags)
+
+obj-m := swap_preload.o
+swap_preload-y := preload_module.o \
+ preload_debugfs.o \
+ preload_storage.o \
+ preload_probe.o \
+ preload_control.o \
+ preload_threads.o \
+ preload_patcher.o \
+ preload_pd.o
--- /dev/null
+#ifndef __PRELOAD__
+#define __PRELOAD__
+
+#define PRELOAD_PREFIX "SWAP_PRELOAD: "
+#define PRELOAD_MAX_ATTEMPTS 10
+#define PRELOAD_DEFAULT_PERMS (S_IRUSR | S_IWUSR) /* u+rw */
+
+#endif /* __PRELOAD__ */
--- /dev/null
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/limits.h>
+
+#include <us_manager/sspt/ip.h>
+
+#include "preload.h"
+
+#include "preload_control.h"
+#include "preload_probe.h"
+#include "preload_module.h"
+
+
+#define DEFAULT_SLOTS_COUNT 5
+#define DEFAULT_SLOTS_STEP 2
+
+struct bin_desc {
+ struct dentry *dentry;
+ char *filename;
+};
+
+static struct bin_desc *target_binaries = NULL;
+static unsigned int target_binaries_cnt = 0;
+static unsigned int target_binaries_slots = 0;
+
+static DEFINE_MUTEX(__target_binaries_mutex);
+
+
+static inline void __target_binaries_lock(void)
+{
+ mutex_lock(&__target_binaries_mutex);
+}
+
+static inline void __target_binaries_unlock(void)
+{
+ mutex_unlock(&__target_binaries_mutex);
+}
+
+static inline struct task_struct *__get_task_struct(void)
+{
+ return current;
+}
+
+static int __alloc_target_binaries_no_lock(unsigned int cnt)
+{
+ target_binaries = kmalloc(sizeof(*target_binaries) * cnt, GFP_KERNEL);
+ if (target_binaries == NULL)
+ return -ENOMEM;
+
+ target_binaries_slots = cnt;
+
+ return 0;
+}
+
+static int __alloc_target_binaries(unsigned int cnt)
+{
+ int ret;
+
+ __target_binaries_lock();
+ ret = __alloc_target_binaries_no_lock(cnt);
+ __target_binaries_unlock();
+
+ return ret;
+}
+
+static void __free_target_binaries(void)
+{
+ int i;
+
+ __target_binaries_lock();
+
+ for (i = 0; i < target_binaries_cnt; i++) {
+ put_dentry(target_binaries[i].dentry);
+ kfree(target_binaries[i].filename);
+ }
+
+ kfree(target_binaries);
+ target_binaries_cnt = 0;
+ target_binaries_slots = 0;
+
+ __target_binaries_unlock();
+}
+
+static int __grow_target_binaries(void)
+{
+ struct bin_desc *tmp = target_binaries;
+ int i, ret;
+
+ __target_binaries_lock();
+
+ ret = __alloc_target_binaries_no_lock(target_binaries_slots + DEFAULT_SLOTS_STEP);
+ if (ret != 0)
+ return ret;
+
+ target_binaries_slots += DEFAULT_SLOTS_STEP;
+
+ for (i = 0; i < target_binaries_cnt; i++) {
+ target_binaries[i].dentry = tmp[i].dentry;
+ target_binaries[i].filename = tmp[i].filename;
+ }
+
+ __target_binaries_unlock();
+
+ kfree(tmp);
+
+ return 0;
+}
+
+static bool __check_dentry_already_exist(struct dentry *dentry)
+{
+ int i;
+ bool ret = false;
+
+ __target_binaries_lock();
+
+ for (i = 0; i < target_binaries_cnt; i++) {
+ if (target_binaries[i].dentry == dentry) {
+ ret = true;
+ goto check_dentry_unlock;
+ }
+ }
+
+check_dentry_unlock:
+ __target_binaries_unlock();
+
+ return ret;
+}
+
+static int __add_target_binary(struct dentry *dentry, char *filename)
+{
+ int ret;
+ size_t len;
+
+ if (__check_dentry_already_exist(dentry)) {
+ printk(PRELOAD_PREFIX "Binary already exist\n");
+ return EALREADY;
+ }
+
+
+ if (target_binaries_slots == target_binaries_cnt) {
+ ret = __grow_target_binaries();
+ if (ret != 0)
+ return ret;
+ }
+
+ /* Filename should be < PATH_MAX */
+ len = strnlen(filename, PATH_MAX);
+ if (len == PATH_MAX)
+ return -EINVAL;
+
+ __target_binaries_lock();
+
+ target_binaries[target_binaries_cnt].dentry = dentry;
+ target_binaries[target_binaries_cnt].filename = kmalloc(len + 1, GFP_KERNEL);
+ memcpy(target_binaries[target_binaries_cnt].filename, filename, len + 1);
+ ++target_binaries_cnt;
+
+ __target_binaries_unlock();
+
+ return 0;
+}
+
+static char *__get_binary_name(struct bin_desc *bin)
+{
+ return bin->filename;
+}
+
+static struct dentry *__get_caller_dentry(struct task_struct *task,
+ unsigned long caller)
+{
+ struct vm_area_struct *vma = NULL;
+
+ if (unlikely(task->mm == NULL))
+ goto get_caller_dentry_fail;
+
+ vma = find_vma_intersection(task->mm, caller, caller + 1);
+ if (unlikely(vma == NULL || vma->vm_file == NULL))
+ goto get_caller_dentry_fail;
+
+ return vma->vm_file->f_dentry;
+
+get_caller_dentry_fail:
+
+ return NULL;
+}
+
+static bool __check_if_instrumented(struct task_struct *task,
+ struct dentry *dentry)
+{
+ int i;
+
+ for (i = 0; i < target_binaries_cnt; i++)
+ if (target_binaries[i].dentry == dentry)
+ return true;
+
+ return false;
+}
+
+static bool __is_instrumented(void *caller)
+{
+ struct task_struct *task = __get_task_struct();
+ struct dentry *caller_dentry = __get_caller_dentry(task,
+ (unsigned long) caller);
+
+ if (caller_dentry == NULL)
+ return false;
+
+ return __check_if_instrumented(task, caller_dentry);
+}
+
+
+
+enum preload_call_type preload_control_call_type(struct us_ip *ip, void *caller)
+{
+ if (__is_instrumented(caller))
+ return INTERNAL_CALL;
+
+ if (ip->probe_i.pl_i.type & SWAP_PRELOAD_ALWAYS)
+ return EXTERNAL_CALL;
+
+ return NOT_INSTRUMENTED;
+}
+
+int preload_control_add_instrumented_binary(char *filename)
+{
+ struct dentry *dentry = get_dentry(filename);
+ int res = 0;
+
+ if (dentry == NULL)
+ return -EINVAL;
+
+ res = __add_target_binary(dentry, filename);
+ if (res != 0)
+ put_dentry(dentry);
+
+ return res > 0 ? 0 : res;
+}
+
+int preload_control_clean_instrumented_bins(void)
+{
+ __free_target_binaries();
+ return __alloc_target_binaries(DEFAULT_SLOTS_COUNT);
+}
+
+unsigned int preload_control_get_bin_names(char ***filenames_p)
+{
+ int i;
+ unsigned int ret = 0;
+
+ __target_binaries_lock();
+
+ *filenames_p = kmalloc(sizeof(**filenames_p) * target_binaries_cnt,
+ GFP_KERNEL);
+ if (*filenames_p == NULL)
+ goto get_binaries_names_out;
+
+ for (i = 0; i < target_binaries_cnt; i++)
+ (*filenames_p)[i] = __get_binary_name(&target_binaries[i]);
+
+ ret = target_binaries_cnt;
+
+get_binaries_names_out:
+ __target_binaries_unlock();
+
+ return ret;
+}
+
+void preload_control_release_bin_names(char ***filenames_p)
+{
+ kfree(*filenames_p);
+}
+
+int preload_control_init(void)
+{
+ return __alloc_target_binaries(DEFAULT_SLOTS_COUNT);
+}
+
+void preload_control_exit(void)
+{
+ __free_target_binaries();
+}
+
+#undef DEFAULT_SLOTS_STEP
+#undef DEFAULT_SLOTS_COUNT
--- /dev/null
+#ifndef __PRELOAD_CONTROL_H__
+#define __PRELOAD_CONTROL_H__
+
+enum preload_call_type {
+ NOT_INSTRUMENTED,
+ EXTERNAL_CALL,
+ INTERNAL_CALL
+};
+
+int preload_control_init(void);
+void preload_control_exit(void);
+
+enum preload_call_type preload_control_call_type(struct us_ip *ip, void *caller);
+int preload_control_add_instrumented_binary(char *filename);
+int preload_control_clean_instrumented_bins(void);
+
+unsigned int preload_control_get_bin_names(char ***filenames_p);
+void preload_control_release_bin_names(char ***filenames_p);
+
+#endif /* __PRELOAD_CONTROL_H__ */
--- /dev/null
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/limits.h>
+#include <asm/uaccess.h>
+#include <driver/swap_debugfs.h>
+#include "preload.h"
+#include "preload_debugfs.h"
+#include "preload_module.h"
+#include "preload_control.h"
+#include "preload_patcher.h"
+#include "preload_storage.h"
+
+static const char PRELOAD_FOLDER[] = "preload";
+static const char PRELOAD_LOADER[] = "loader";
+static const char PRELOAD_LOADER_OFFSET[] = "loader_offset";
+static const char PRELOAD_LOADER_PATH[] = "loader_path";
+static const char PRELOAD_BINARIES[] = "target_binaries";
+static const char PRELOAD_BINARIES_LIST[] = "bins_list";
+static const char PRELOAD_BINARIES_ADD[] = "bins_add";
+static const char PRELOAD_BINARIES_REMOVE[] = "bins_remove";
+static const char PRELOAD_CALLER[] = "caller";
+static const char PRELOAD_HANDLERS_PATH[] = "handlers_path";
+static const char PRELOAD_LINKER_DATA[] = "linker";
+static const char PRELOAD_LINKER_PATH[] = "linker_path";
+static const char PRELOAD_LINKER_R_DEBUG_OFFSET[] = "r_debug_offset";
+
+struct loader_info {
+ char *path;
+ unsigned long offset;
+ struct dentry *dentry;
+};
+
+static struct dentry *preload_root;
+static struct loader_info __loader_info = {
+ .path = NULL,
+ .offset = 0,
+ .dentry = NULL
+};
+
+static unsigned long r_debug_offset = 0;
+static DEFINE_MUTEX(__dentry_lock);
+
+static inline void dentry_lock(void)
+{
+ mutex_lock(&__dentry_lock);
+}
+
+static inline void dentry_unlock(void)
+{
+ mutex_unlock(&__dentry_lock);
+}
+
+
+static void set_loader_file(char *path)
+{
+ __loader_info.path = path;
+ dentry_lock();
+ __loader_info.dentry = get_dentry(__loader_info.path);
+ dentry_unlock();
+}
+
+struct dentry *preload_debugfs_get_loader_dentry(void)
+{
+ struct dentry *dentry;
+
+ dentry_lock();
+ dentry = __loader_info.dentry;
+ dentry_unlock();
+
+ return dentry;
+}
+
+unsigned long preload_debugfs_get_loader_offset(void)
+{
+ /* TODO Think about sync */
+ return __loader_info.offset;
+}
+
+static void clean_loader_info(void)
+{
+ if (__loader_info.path != NULL)
+ kfree(__loader_info.path);
+
+ dentry_lock();
+ if (__loader_info.dentry != NULL)
+ put_dentry(__loader_info.dentry);
+ dentry_unlock();
+}
+
+struct dentry *debugfs_create_ptr(const char *name, mode_t mode,
+ struct dentry *parent,
+ unsigned long *value)
+{
+ struct dentry *dentry;
+
+#if BITS_PER_LONG == 32
+ dentry = debugfs_create_x32(name, mode, parent, (u32 *)value);
+#elif BITS_PER_LONG == 64
+ dentry = debugfs_create_x64(name, mode, parent, (u64 *)value);
+#else
+#error Unsupported BITS_PER_LONG value
+#endif
+
+ return dentry;
+}
+
+
+/* ===========================================================================
+ * = LOADER PATH =
+ * ===========================================================================
+ */
+
+static ssize_t loader_path_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+ char *path;
+
+ if (preload_module_is_running())
+ return -EBUSY;
+
+ clean_loader_info();
+
+ path = kmalloc(len, GFP_KERNEL);
+ if (path == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(path, buf, len)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ path[len - 1] = '\0';
+ set_loader_file(path);
+ ret = len;
+
+out:
+ return ret;
+}
+
+
+static const struct file_operations loader_path_file_ops = {
+ .owner = THIS_MODULE,
+ .write = loader_path_write,
+};
+
+
+/* ===========================================================================
+ * = 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';
+
+ if (preload_control_add_instrumented_binary(path) != 0) {
+ printk(PRELOAD_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 = preload_control_clean_instrumented_bins();
+ if (ret != 0) {
+ printk(PRELOAD_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 = preload_control_get_bin_names(&filenames);
+
+ if (files_cnt == 0) {
+ printk(PRELOAD_PREFIX "Cannot read binaries names!\n");
+ ret = 0;
+ goto bin_list_read_out;
+ }
+
+ 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;
+ }
+
+ preload_control_release_bin_names(&filenames);
+
+ return simple_read_from_buffer(usr_buf, count, ppos, buf, len);
+
+bin_list_read_fail:
+ preload_control_release_bin_names(&filenames);
+
+bin_list_read_out:
+ 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 =
+ * ===========================================================================
+ */
+
+
+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';
+
+ if (preload_storage_set_linker_info(path) != 0) {
+ printk(PRELOAD_PREFIX "Cannot set linker path %s\n", path);
+ ret = -EINVAL;
+ goto linker_path_write_out;
+ }
+
+ ret = len;
+
+linker_path_write_out:
+ kfree(path);
+
+ return ret;
+}
+
+static const struct file_operations linker_path_file_ops = {
+ .owner = THIS_MODULE,
+ .write = linker_path_write,
+};
+
+
+/* ===========================================================================
+ * = HANDLERS PATH =
+ * ===========================================================================
+ */
+
+
+static ssize_t handlers_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 handlers_path_write_out;
+ }
+
+ if (copy_from_user(path, buf, len)) {
+ ret = -EINVAL;
+ goto handlers_path_write_out;
+ }
+
+ path[len - 1] = '\0';
+
+ if (preload_storage_set_handlers_info(path) != 0) {
+ printk(PRELOAD_PREFIX "Cannot set handler path %s\n", path);
+ ret = -EINVAL;
+ goto handlers_path_write_out;
+ }
+
+ ret = len;
+
+handlers_path_write_out:
+ kfree(path);
+
+ return ret;
+}
+
+static const struct file_operations handlers_path_file_ops = {
+ .owner = THIS_MODULE,
+ .write = handlers_path_write,
+};
+
+
+
+
+unsigned long preload_debugfs_r_debug_offset(void)
+{
+ return r_debug_offset;
+}
+
+int preload_debugfs_init(void)
+{
+ struct dentry *swap_dentry, *root, *loader, *open_p, *lib_path,
+ *bin_path, *bin_list, *bin_add, *bin_remove,
+ *linker_dir, *linker_path, *linker_offset, *handlers_path;
+ int ret;
+
+ ret = -ENODEV;
+ if (!debugfs_initialized())
+ goto fail;
+
+ ret = -ENOENT;
+ swap_dentry = get_swap_debugfs_dir();
+ if (!swap_dentry)
+ goto fail;
+
+ ret = -ENOMEM;
+ root = debugfs_create_dir(PRELOAD_FOLDER, swap_dentry);
+ if (IS_ERR_OR_NULL(root))
+ goto fail;
+
+ preload_root = root;
+
+ loader = debugfs_create_dir(PRELOAD_LOADER, root);
+ if (IS_ERR_OR_NULL(root)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ open_p = debugfs_create_ptr(PRELOAD_LOADER_OFFSET, PRELOAD_DEFAULT_PERMS,
+ loader, &__loader_info.offset);
+ if (IS_ERR_OR_NULL(open_p)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ lib_path = debugfs_create_file(PRELOAD_LOADER_PATH, PRELOAD_DEFAULT_PERMS,
+ loader, NULL, &loader_path_file_ops);
+ if (IS_ERR_OR_NULL(lib_path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ bin_path = debugfs_create_dir(PRELOAD_BINARIES, root);
+ if (IS_ERR_OR_NULL(bin_path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ bin_list = debugfs_create_file(PRELOAD_BINARIES_LIST, PRELOAD_DEFAULT_PERMS,
+ bin_path, NULL, &bin_list_file_ops);
+ if (IS_ERR_OR_NULL(bin_list)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ bin_add = debugfs_create_file(PRELOAD_BINARIES_ADD, PRELOAD_DEFAULT_PERMS,
+ bin_path, NULL, &bin_add_file_ops);
+ if (IS_ERR_OR_NULL(bin_add)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ bin_remove = debugfs_create_file(PRELOAD_BINARIES_REMOVE,
+ PRELOAD_DEFAULT_PERMS, bin_path, NULL,
+ &bin_remove_file_ops);
+ if (IS_ERR_OR_NULL(bin_remove)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ linker_dir = debugfs_create_dir(PRELOAD_LINKER_DATA, root);
+ if (IS_ERR_OR_NULL(linker_dir)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ linker_path = debugfs_create_file(PRELOAD_LINKER_PATH,
+ PRELOAD_DEFAULT_PERMS, linker_dir, NULL,
+ &linker_path_file_ops);
+ if (IS_ERR_OR_NULL(linker_path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ linker_offset = debugfs_create_ptr(PRELOAD_LINKER_R_DEBUG_OFFSET,
+ PRELOAD_DEFAULT_PERMS, linker_dir,
+ &r_debug_offset);
+ if (IS_ERR_OR_NULL(linker_offset)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ handlers_path = debugfs_create_file(PRELOAD_HANDLERS_PATH,
+ PRELOAD_DEFAULT_PERMS, root, NULL,
+ &handlers_path_file_ops);
+ if (IS_ERR_OR_NULL(handlers_path)) {
+ ret = -ENOMEM;
+ goto remove;
+ }
+
+ return 0;
+
+remove:
+
+ debugfs_remove_recursive(root);
+
+fail:
+ printk(PRELOAD_PREFIX "Debugfs initialization failure: %d\n", ret);
+
+ return ret;
+}
+
+void preload_debugfs_exit(void)
+{
+ if (preload_root)
+ debugfs_remove_recursive(preload_root);
+ preload_root = NULL;
+
+ preload_module_set_not_ready();
+ clean_loader_info();
+}
--- /dev/null
+#ifndef __PRELOAD_DEBUGFS_H__
+#define __PRELOAD_DEBUGFS_H__
+
+struct dentry;
+
+int preload_debugfs_init(void);
+void preload_debugfs_exit(void);
+
+struct dentry *preload_debugfs_get_loader_dentry(void);
+unsigned long preload_debugfs_get_loader_offset(void);
+
+struct dentry *preload_debugfs_create_new_thread(unsigned long tid);
+
+unsigned long preload_debugfs_r_debug_offset(void);
+
+#endif /* __PRELOAD_DEBUGFS_H__ */
--- /dev/null
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <linux/slab.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/err.h>
+#include <kprobe/swap_kprobes.h>
+#include <us_manager/us_manager_common.h>
+#include <us_manager/sspt/sspt_page.h>
+#include <us_manager/sspt/sspt_file.h>
+#include <us_manager/sspt/sspt_proc.h>
+#include <us_manager/sspt/ip.h>
+#include <writer/kernel_operations.h>
+#include <task_data/task_data.h>
+#include "preload.h"
+#include "preload_probe.h"
+#include "preload_debugfs.h"
+#include "preload_module.h"
+#include "preload_storage.h"
+#include "preload_control.h"
+#include "preload_threads.h"
+#include "preload_patcher.h"
+#include "preload_pd.h"
+
+#define page_to_proc(page) ((page)->file->proc)
+#define page_to_dentry(page) ((page)->file->dentry)
+#define ip_to_proc(ip) page_to_proc((ip)->page)
+
+struct us_priv {
+ struct pt_regs regs;
+ unsigned long arg0;
+ unsigned long arg1;
+ unsigned long raddr;
+ unsigned long origin;
+};
+
+static int get_put_counter;
+
+enum preload_status_t {
+ SWAP_PRELOAD_NOT_READY = 0,
+ SWAP_PRELOAD_READY = 1,
+ SWAP_PRELOAD_RUNNING = 2
+};
+
+enum {
+ /* task preload flags */
+ HANDLER_RUNNING = 0x1
+};
+
+static enum preload_status_t __preload_status = SWAP_PRELOAD_NOT_READY;
+
+static inline struct process_data *__get_process_data(struct uretprobe *rp)
+{
+ struct process_data *pd;
+ struct us_ip *ip = to_us_ip(rp);
+
+ pd = ip_to_proc(ip)->private_data;
+
+ return pd;
+}
+
+static struct dentry *__get_dentry(struct dentry *dentry)
+{
+ get_put_counter++;
+ return dget(dentry);
+}
+
+
+
+bool preload_module_is_running(void)
+{
+ if (__preload_status == SWAP_PRELOAD_RUNNING)
+ return true;
+
+ return false;
+}
+
+bool preload_module_is_ready(void)
+{
+ if (__preload_status == SWAP_PRELOAD_READY)
+ return true;
+
+ return false;
+}
+
+bool preload_module_is_not_ready(void)
+{
+ if (__preload_status == SWAP_PRELOAD_NOT_READY)
+ return true;
+
+ return false;
+}
+
+void preload_module_set_ready(void)
+{
+ __preload_status = SWAP_PRELOAD_READY;
+}
+
+void preload_module_set_running(void)
+{
+ __preload_status = SWAP_PRELOAD_RUNNING;
+}
+
+void preload_module_set_not_ready(void)
+{
+ __preload_status = SWAP_PRELOAD_NOT_READY;
+}
+
+struct dentry *get_dentry(const char *filepath)
+{
+ struct path path;
+ struct dentry *dentry = NULL;
+
+ if (kern_path(filepath, LOOKUP_FOLLOW, &path) == 0) {
+ dentry = __get_dentry(path.dentry);
+ path_put(&path);
+ }
+
+ return dentry;
+}
+
+void put_dentry(struct dentry *dentry)
+{
+ get_put_counter--;
+ dput(dentry);
+}
+
+static inline unsigned long get_preload_flags(struct task_struct *task)
+{
+ unsigned long flags;
+ int ok;
+
+ flags = (unsigned long)swap_task_data_get(task, &ok);
+ WARN(!ok, "Preload flags(%08lx) seem corrupted", flags);
+
+ return (ok ? flags: 0);
+}
+
+static inline void set_preload_flags(struct task_struct *task,
+ unsigned long flags)
+{
+ swap_task_data_set(task, (void *)flags, 0);
+}
+
+static inline void __prepare_ujump(struct uretprobe_instance *ri,
+ struct pt_regs *regs,
+ unsigned long vaddr)
+{
+ ri->rp->up.kp.ss_addr[smp_processor_id()] = (kprobe_opcode_t *)vaddr;
+}
+
+static inline int __push(struct pt_regs *regs, void *buf, size_t len)
+{
+ unsigned long sp = swap_get_stack_ptr(regs) - len;
+
+ sp = PTR_ALIGN(sp, sizeof(unsigned long));
+ if (copy_to_user((void __user *)sp, buf, len))
+ return -EIO;
+ swap_set_stack_ptr(regs, sp);
+
+ return 0;
+}
+
+static inline void __save_uregs(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct us_priv *priv = (struct us_priv *)ri->data;
+
+ memcpy(ri->data, regs, sizeof(*regs));
+ priv->arg0 = swap_get_arg(regs, 0);
+ priv->arg1 = swap_get_arg(regs, 1);
+ priv->raddr = swap_get_ret_addr(regs);
+}
+
+static inline void __restore_uregs(struct uretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct us_priv *priv = (struct us_priv *)ri->data;
+
+ memcpy(regs, ri->data, sizeof(*regs));
+ swap_set_arg(regs, 0, priv->arg0);
+ swap_set_arg(regs, 1, priv->arg1);
+ swap_set_ret_addr(regs, priv->raddr);
+#ifndef CONFIG_ARM
+ /* need to do it only on x86 */
+ regs->EREG(ip) -= 1;
+#endif /* !CONFIG_ARM */
+ /* we have just restored the registers => no need to do it in
+ * trampoline_uprobe_handler */
+ ri->ret_addr = NULL;
+}
+
+static inline void print_regs(const char *prefix, struct pt_regs *regs,
+ struct uretprobe_instance *ri)
+{
+#ifdef CONFIG_ARM
+ printk(PRELOAD_PREFIX "%s[%d/%d] (%d) %s addr(%08lx), "
+ "r0(%08lx), r1(%08lx), r2(%08lx), r3(%08lx), "
+ "r4(%08lx), r5(%08lx), r6(%08lx), r7(%08lx), "
+ "sp(%08lx), lr(%08lx), pc(%08lx)\n",
+ current->comm, current->tgid, current->pid,
+ (int)preload_pd_get_state(__get_process_data(ri->rp)),
+ prefix, (unsigned long)ri->rp->up.kp.addr,
+ regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3,
+ regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7,
+ regs->ARM_sp, regs->ARM_lr, regs->ARM_pc);
+#else /* !CONFIG_ARM */
+ printk(PRELOAD_PREFIX "%s[%d/%d] (%d) %s addr(%08lx), "
+ "ip(%08lx), arg0(%08lx), arg1(%08lx), raddr(%08lx)\n",
+ current->comm, current->tgid, current->pid,
+ (int)preload_pd_get_state(__get_process_data(ri->rp)),
+ prefix, (unsigned long)ri->rp->up.kp.addr,
+ regs->EREG(ip), swap_get_arg(regs, 0), swap_get_arg(regs, 1),
+ swap_get_ret_addr(regs));
+#endif /* CONFIG_ARM */
+}
+
+static inline unsigned long __get_r_debug_off(struct vm_area_struct *linker_vma)
+{
+ unsigned long start_addr;
+ unsigned long offset = preload_debugfs_r_debug_offset();
+
+ if (linker_vma == NULL)
+ return 0;
+
+ start_addr = linker_vma->vm_start;
+
+ return (offset ? start_addr + offset : 0);
+}
+
+static struct vm_area_struct *__get_linker_vma(struct task_struct *task)
+{
+ struct vm_area_struct *vma = NULL;
+ struct bin_info *ld_info;
+
+ ld_info = preload_storage_get_linker_info();
+
+ for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
+ if (vma->vm_file && vma->vm_flags & VM_EXEC
+ && vma->vm_file->f_dentry == ld_info->dentry) {
+ preload_storage_put_linker_info(ld_info);
+ return vma;
+ }
+ }
+
+ preload_storage_put_linker_info(ld_info);
+ return NULL;
+}
+
+static struct vm_area_struct *__get_libc_vma(struct task_struct *task)
+{
+ struct vm_area_struct *vma = NULL;
+ struct dentry *libc_dentry;
+
+ libc_dentry = get_dentry("/lib/libc.so.6");
+
+ for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
+ if (vma->vm_file && vma->vm_flags & VM_EXEC
+ && vma->vm_file->f_dentry == libc_dentry) {
+ put_dentry(libc_dentry);
+ return vma;
+ }
+ }
+
+ put_dentry(libc_dentry);
+ 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;
+}
+
+static inline bool __is_probe_non_block(struct us_ip *ip)
+{
+ if (!(ip->probe_i.pl_i.type & (0x1 << 1)))
+ return true;
+
+ return false;
+}
+
+static inline bool __is_handlers_call(struct vm_area_struct *caller)
+{
+ /* TODO Optimize using start/stop callbacks */
+
+ struct bin_info *hi = preload_storage_get_handlers_info();
+ bool res = false;
+
+ if (hi == NULL) {
+ printk(PRELOAD_PREFIX "Cannot get handlers dentry!\n");
+ goto is_handlers_call_out;
+ }
+
+ if (caller == NULL || caller->vm_file == NULL ||
+ caller->vm_file->f_dentry == NULL) {
+ goto is_handlers_call_out;
+ }
+
+ if (hi->dentry == caller->vm_file->f_dentry)
+ res = true;
+
+is_handlers_call_out:
+
+ preload_storage_put_handlers_info(hi);
+
+ return res;
+}
+
+
+
+
+
+static bool __is_proc_mmap_mappable(struct task_struct *task)
+{
+ struct vm_area_struct *linker_vma = __get_linker_vma(task);
+ unsigned long r_debug_addr;
+ unsigned int state;
+ int ret;
+
+ if (linker_vma == NULL)
+ return false;
+
+ r_debug_addr = __get_r_debug_off(linker_vma);
+ if (r_debug_addr == 0)
+ return false;
+
+ ret = preload_patcher_get_ui((void *)r_debug_addr + sizeof(int) +
+ sizeof(void *) + sizeof(unsigned long),
+ &state, task);
+ if (ret != sizeof(state))
+ return false;
+
+ return ( state == 0 ? true : false );
+}
+
+static bool __not_system_caller(struct task_struct *task,
+ struct vm_area_struct *caller)
+{
+ struct vm_area_struct *linker_vma = __get_linker_vma(task);
+ struct vm_area_struct *libc_vma = __get_libc_vma(task);
+
+ if (linker_vma == NULL || libc_vma == NULL || caller == NULL ||
+ caller == linker_vma || caller == libc_vma)
+ return false;
+
+ return true;
+}
+
+static bool __should_we_preload_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) ||
+ !__not_system_caller(task, cvma))
+ return false;
+
+ return true;
+}
+
+
+
+static int preload_us_entry(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct process_data *pd = __get_process_data(ri->rp);
+ struct us_ip *ip = container_of(ri->rp, struct us_ip, retprobe);
+ struct us_priv *priv = (struct us_priv *)ri->data;
+ unsigned long flags = get_preload_flags(current);
+ unsigned long offset = ip->probe_i.pl_i.handler;
+ unsigned long vaddr = 0;
+ char __user *path = NULL;
+
+ if ((flags & HANDLER_RUNNING) ||
+ preload_threads_check_disabled_probe(current, ip->orig_addr))
+ goto out_set_origin;
+
+ switch (preload_pd_get_state(pd)) {
+ case NOT_LOADED:
+ /* if linker is still doing its work, we do nothing */
+ if (!__should_we_preload_handlers(current, regs))
+ goto out_set_origin;
+
+ /* jump to loader code if ready */
+ vaddr = preload_pd_get_loader_base(pd) + preload_debugfs_get_loader_offset();
+ if (vaddr) {
+ /* save original regs state */
+ __save_uregs(ri, regs);
+ print_regs("ORIG", regs, ri);
+
+ path = preload_pd_get_path(pd);
+
+ /* set dlopen args: filename, flags */
+ swap_set_arg(regs, 0, (unsigned long)path/*swap_get_stack_ptr(regs)*/);
+ swap_set_arg(regs, 1, 2 /* RTLD_NOW */);
+
+ /* do the jump to dlopen */
+ __prepare_ujump(ri, regs, vaddr);
+ /* set new state */
+ preload_pd_set_state(pd, LOADING);
+ }
+ break;
+ case LOADING:
+ /* handlers have not yet been loaded... just ignore */
+ break;
+ case LOADED:
+ /* jump to preloaded handler */
+ vaddr = preload_pd_get_handlers_base(pd) + offset;
+ if (vaddr) {
+ unsigned long disable_addr;
+ unsigned long caddr = get_regs_ret_func(regs);
+ struct vm_area_struct *cvma = __get_vma_by_addr(current, caddr);
+ enum preload_call_type ct;
+
+ disable_addr = __is_probe_non_block(ip) ? ip->orig_addr : 0;
+ ct = preload_control_call_type(ip, (void *)caddr);
+
+ /* jump only if caller is instumented and it is not a system lib -
+ * this leads to some errors */
+ if (__not_system_caller(current, cvma) && ct &&
+ !__is_handlers_call(cvma)) {
+ if (preload_threads_set_data(current,
+ caddr, ct,
+ disable_addr) != 0)
+ printk(PRELOAD_PREFIX "Error! Failed to set caller 0x%lx"
+ " for %d/%d\n", caddr, current->tgid,
+ current->pid);
+ /* args are not changed */
+ __prepare_ujump(ri, regs, vaddr);
+ if (disable_addr == 0)
+ set_preload_flags(current, HANDLER_RUNNING);
+ }
+ }
+ break;
+ case FAILED:
+ case ERROR:
+ default:
+ /* do nothing */
+ break;
+ }
+
+out_set_origin:
+ priv->origin = vaddr;
+ return 0;
+}
+
+static int preload_us_ret(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ struct process_data *pd = __get_process_data(ri->rp);
+ struct us_ip *ip = container_of(ri->rp, struct us_ip, retprobe);
+ struct us_priv *priv = (struct us_priv *)ri->data;
+ unsigned long flags = get_preload_flags(current);
+ unsigned long offset = ip->probe_i.pl_i.handler;
+ unsigned long vaddr = 0;
+
+ switch (preload_pd_get_state(pd)) {
+ case NOT_LOADED:
+ /* loader has not yet been mapped... just ignore */
+ break;
+ case LOADING:
+ /* check if preloading has been completed */
+ vaddr = preload_pd_get_loader_base(pd) + preload_debugfs_get_loader_offset();
+ if (vaddr && (priv->origin == vaddr)) {
+ preload_pd_set_handle(pd, (void __user *)regs_return_value(regs));
+
+ /* restore original regs state */
+ __restore_uregs(ri, regs);
+ print_regs("REST", regs, ri);
+ /* check if preloading done */
+
+ if (preload_pd_get_handle(pd)) {
+ preload_pd_set_state(pd, LOADED);
+ preload_pd_put_path(pd);
+ } else {
+ preload_pd_dec_attempts(pd);
+ preload_pd_set_state(pd, FAILED);
+ preload_pd_put_path(pd);
+ }
+ }
+ break;
+ case LOADED:
+ if ((flags & HANDLER_RUNNING) ||
+ preload_threads_check_disabled_probe(current, ip->orig_addr)) {
+ bool non_blk_probe = __is_probe_non_block(ip);
+
+ /* drop the flag if the handler has completed */
+ vaddr = preload_pd_get_handlers_base(pd) + offset;
+ if (vaddr && (priv->origin == vaddr)) {
+ if (preload_threads_put_data(current) != 0)
+ printk(PRELOAD_PREFIX "Error! Failed to put caller slot"
+ " for %d/%d\n", current->tgid, current->pid);
+ if (!non_blk_probe) {
+ flags &= ~HANDLER_RUNNING;
+ set_preload_flags(current, flags);
+ }
+ }
+ }
+ break;
+ case FAILED:
+ if (preload_pd_get_attempts(pd)) {
+ preload_pd_set_state(pd, NOT_LOADED);
+ preload_pd_put_path(pd);
+ }
+ break;
+ case ERROR:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+enum mmap_type_t {
+ MMAP_LOADER,
+ MMAP_HANDLERS,
+ MMAP_SKIP
+};
+
+struct mmap_priv {
+ enum mmap_type_t type;
+};
+
+static inline bool check_prot(unsigned long prot)
+{
+ return !!((prot & PROT_READ) && (prot & PROT_EXEC));
+}
+
+static int mmap_entry_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct file *file = (struct file *)swap_get_karg(regs, 0);
+ unsigned long prot = swap_get_karg(regs, 3);
+ struct mmap_priv *priv = (struct mmap_priv *)ri->data;
+ struct dentry *dentry, *loader_dentry;
+ struct bin_info *hi;
+
+ priv->type = MMAP_SKIP;
+ if (!check_prot(prot))
+ return 0;
+
+ if (!file)
+ return 0;
+ dentry = file->f_dentry;
+ if (dentry == NULL)
+ return 0;
+
+ hi = preload_storage_get_handlers_info();
+ loader_dentry = preload_debugfs_get_loader_dentry();
+ if (dentry == loader_dentry)
+ priv->type = MMAP_LOADER;
+ else if (hi->dentry != NULL && dentry == hi->dentry)
+ priv->type = MMAP_HANDLERS;
+
+ if (hi != NULL)
+ preload_storage_put_handlers_info(hi);
+
+ return 0;
+}
+
+static int mmap_ret_handler(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ struct mmap_priv *priv = (struct mmap_priv *)ri->data;
+ struct task_struct *task = current->group_leader;
+ struct sspt_proc *proc;
+ unsigned long vaddr;
+
+ if (!task->mm)
+ return 0;
+
+ vaddr = (unsigned long)regs_return_value(regs);
+ if (IS_ERR_VALUE(vaddr))
+ return 0;
+
+ proc = sspt_proc_get_by_task(task);
+ if (!proc)
+ return 0;
+
+ switch (priv->type) {
+ case MMAP_LOADER:
+ preload_pd_set_loader_base(proc->private_data, vaddr);
+ break;
+ case MMAP_HANDLERS:
+ preload_pd_set_handlers_base(proc->private_data, vaddr);
+ break;
+ case MMAP_SKIP:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+static int get_caller_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ unsigned long caller;
+ int ret;
+
+ ret = preload_threads_get_caller(current, &caller);
+ if (ret != 0) {
+ caller = 0xbadbeef;
+ printk(PRELOAD_PREFIX "Error! Cannot get caller address for %d/%d\n",
+ current->tgid, current->pid);
+ }
+
+ swap_put_uarg(regs, 0, caller);
+
+ return 0;
+}
+
+static int get_call_type_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ unsigned char call_type;
+ int ret;
+
+ ret = preload_threads_get_call_type(current, &call_type);
+ if (ret != 0) {
+ call_type = 0xff;
+ printk(PRELOAD_PREFIX "Error! Cannot get call type for %d/%d\n",
+ current->tgid, current->pid);
+ }
+
+ swap_put_uarg(regs, 0, call_type);
+
+ return 0;
+}
+
+
+
+
+int preload_module_get_caller_init(struct us_ip *ip)
+{
+ struct uprobe *up = &ip->uprobe;
+
+ up->kp.pre_handler = get_caller_handler;
+
+ return 0;
+}
+
+void preload_module_get_caller_exit(struct us_ip *ip)
+{
+}
+
+int preload_module_get_call_type_init(struct us_ip *ip)
+{
+ struct uprobe *up = &ip->uprobe;
+
+ up->kp.pre_handler = get_call_type_handler;
+
+ return 0;
+}
+
+void preload_module_get_call_type_exit(struct us_ip *ip)
+{
+}
+
+
+static struct kretprobe mmap_rp = {
+ .kp.symbol_name = "do_mmap_pgoff",
+ .data_size = sizeof(struct mmap_priv),
+ .entry_handler = mmap_entry_handler,
+ .handler = mmap_ret_handler
+};
+
+int preload_module_uprobe_init(struct us_ip *ip)
+{
+ struct uretprobe *rp = &ip->retprobe;
+ struct sspt_proc *proc = page_to_proc(ip->page);
+ int ret;
+
+ if (proc->private_data == NULL) {
+ ret = preload_pd_create_pd(&(proc->private_data), proc->task);
+ if (ret != 0)
+ return ret;
+ }
+
+ preload_pd_inc_refs(proc->private_data);
+
+ rp->entry_handler = preload_us_entry;
+ rp->handler = preload_us_ret;
+ /* FIXME actually additional data_size is needed only when we jump
+ * to dlopen */
+ rp->data_size = sizeof(struct us_priv);
+
+ ret = swap_register_kretprobe(&mmap_rp);
+ if (ret == 0) {
+ return -EFAULT; /* failed to get THIS_MODULE */
+ }
+
+ return 0;
+}
+
+void preload_module_uprobe_exit(struct us_ip *ip)
+{
+ struct sspt_proc *proc = ip_to_proc(ip);
+
+ preload_pd_dec_refs(proc->private_data);
+
+ swap_unregister_kretprobe(&mmap_rp);
+}
+
+int preload_set(void)
+{
+ if (preload_module_is_running())
+ return -EBUSY;
+
+ return 0;
+}
+
+void preload_unset(void)
+{
+ swap_unregister_kretprobe(&mmap_rp);
+ /*module_put(THIS_MODULE);*/
+ preload_module_set_not_ready();
+
+}
+
+static int __init preload_module_init(void)
+{
+ int ret;
+
+ ret = preload_debugfs_init();
+ if (ret)
+ goto out_err;
+
+ ret = preload_storage_init();
+ if (ret)
+ goto exit_debugfs;
+
+ ret = preload_pd_init();
+ if (ret)
+ goto exit_storage;
+
+ /* TODO do not forget to remove set (it is just for debugging) */
+ ret = preload_set();
+ if (ret)
+ goto exit_pd;
+
+ ret = preload_control_init();
+ if (ret)
+ goto exit_set;
+
+ ret = preload_threads_init();
+ if (ret)
+ goto exit_control;
+
+ ret = register_preload_probes();
+ if (ret)
+ goto exit_threads;
+
+ return 0;
+
+exit_threads:
+ preload_threads_exit();
+
+exit_control:
+ preload_control_exit();
+
+exit_set:
+ preload_unset();
+
+exit_pd:
+ preload_pd_uninit();
+
+exit_storage:
+ preload_storage_exit();
+
+exit_debugfs:
+ preload_debugfs_exit();
+
+out_err:
+ return ret;
+}
+
+static void __exit preload_module_exit(void)
+{
+ unregister_preload_probes();
+ preload_threads_exit();
+ preload_control_exit();
+ preload_unset();
+ preload_pd_uninit();
+ preload_storage_exit();
+ preload_debugfs_exit();
+
+ WARN(get_put_counter, "Bad GET/PUT balance: %d\n", get_put_counter);
+}
+
+module_init(preload_module_init);
+module_exit(preload_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SWAP Preload Module");
+MODULE_AUTHOR("Vasiliy Ulyanov <v.ulyanov@samsung.com>"
+ "Alexander Aksenov <a.aksenov@samsung.com>");
--- /dev/null
+#ifndef __PRELOAD_MODULE_H__
+#define __PRELOAD_MODULE_H__
+
+#include <linux/types.h>
+
+struct us_ip;
+struct dentry;
+
+bool preload_module_is_ready(void);
+bool preload_module_is_running(void);
+bool preload_module_is_not_ready(void);
+void preload_module_set_ready(void);
+void preload_module_set_running(void);
+void preload_module_set_not_ready(void);
+
+int preload_module_uprobe_init(struct us_ip *ip);
+void preload_module_uprobe_exit(struct us_ip *ip);
+
+int preload_module_get_caller_init(struct us_ip *ip);
+void preload_module_get_caller_exit(struct us_ip *ip);
+int preload_module_get_call_type_init(struct us_ip *ip);
+void preload_module_get_call_type_exit(struct us_ip *ip);
+
+struct dentry *get_dentry(const char *filepath);
+void put_dentry(struct dentry *dentry);
+
+
+#endif /* __PRELOAD_MODULE_H__ */
--- /dev/null
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm_types.h>
+#include <linux/mm.h>
+
+#include <kprobe/swap_kprobes_deps.h>
+
+#include "preload_patcher.h"
+#include "preload_debugfs.h"
+#include "preload_storage.h"
+
+
+static inline bool check_vma(struct vm_area_struct *vma, struct dentry *dentry)
+{
+ struct file *file = vma->vm_file;
+
+ return (file && (vma->vm_flags & VM_EXEC) && (file->f_dentry == dentry));
+}
+
+
+static inline int __patch_proc_mem(struct task_struct *task, unsigned long addr,
+ void *buf, int size)
+{
+ return write_proc_vm_atomic(task, addr, buf, size);
+}
+
+static inline int __read_proc_mem(struct task_struct *task, unsigned long addr,
+ void *value, size_t value_size)
+{
+ return read_proc_vm_atomic(task, addr, value, value_size);
+}
+
+
+
+
+int preload_patcher_patch_proc(void *addr, unsigned long val,
+ struct task_struct *task)
+{
+ return __patch_proc_mem(task, (unsigned long)addr, &val, sizeof(val));
+}
+
+int preload_patcher_write_string(void *addr, char *string, size_t len,
+ struct task_struct *task)
+{
+ return __patch_proc_mem(task, (unsigned long)addr, string, len);
+}
+
+int preload_patcher_get_ul(void *addr, unsigned long *val,
+ struct task_struct *task)
+{
+ return __read_proc_mem(task, (unsigned long)addr, val, sizeof(*val));
+}
+
+int preload_patcher_get_ui(void *addr, unsigned int *val,
+ struct task_struct *task)
+{
+ return __read_proc_mem(task, (unsigned long)addr, val, sizeof(*val));
+}
+
+int preload_patcher_null_mem(void *addr, int size, struct task_struct *task)
+{
+ char *buf;
+ int ret;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ memset(buf, 0, size);
+
+ ret = __patch_proc_mem(task, (unsigned long)addr, buf, size);
+
+ kfree(buf);
+
+ return ret;
+}
--- /dev/null
+#ifndef __PRELOAD_PATCHER_H__
+#define __PRELOAD_PATCHER_H__
+
+struct task_struct;
+
+int preload_patcher_patch_proc(void *addr, unsigned long val,
+ struct task_struct *task);
+int preload_patcher_write_string(void *addr, char *string, size_t len,
+ struct task_struct *task);
+int preload_patcher_get_ul(void *addr, unsigned long *val,
+ struct task_struct *task);
+int preload_patcher_null_mem(void *addr, int size, struct task_struct *task);
+int preload_patcher_get_ui(void *addr, unsigned int *val,
+ struct task_struct *task);
+
+
+
+#endif /* __PRELOAD_PATCHER_H__ */
--- /dev/null
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/dcache.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/hardirq.h>
+#include <us_manager/us_manager_common.h>
+#include "preload_pd.h"
+#include "preload_threads.h"
+#include "preload_debugfs.h"
+#include "preload_storage.h"
+#include "preload_patcher.h"
+#include "preload.h"
+
+struct process_data {
+ char is_mapped;
+ enum preload_state_t state;
+ unsigned long loader_base;
+ unsigned long handlers_base;
+ unsigned long data_page;
+ void __user *handle;
+ long attempts;
+ long refcount;
+};
+
+static struct bin_info *handlers_info;
+
+
+
+static inline bool check_vma(struct vm_area_struct *vma, struct dentry *dentry)
+{
+ struct file *file = vma->vm_file;
+
+ return (file && (vma->vm_flags & VM_EXEC) && (file->f_dentry == dentry));
+}
+
+static inline enum preload_state_t __get_state(struct process_data *pd)
+{
+ return pd->state;
+}
+
+static inline void __set_state(struct process_data *pd,
+ enum preload_state_t state)
+{
+ pd->state = state;
+}
+
+static inline unsigned long __get_loader_base(struct process_data *pd)
+{
+ return pd->loader_base;
+}
+
+static inline void __set_loader_base(struct process_data *pd,
+ unsigned long addr)
+{
+ pd->loader_base = addr;
+}
+
+static inline unsigned long __get_handlers_base(struct process_data *pd)
+{
+ return pd->handlers_base;
+}
+
+static inline void __set_handlers_base(struct process_data *pd,
+ unsigned long addr)
+{
+ pd->handlers_base = addr;
+}
+
+static inline char __user *__get_path(struct process_data *pd)
+{
+ return (char *)pd->data_page;
+}
+
+static inline unsigned long __get_data_page(struct process_data *pd)
+{
+ return pd->data_page;
+}
+
+static inline void __set_data_page(struct process_data *pd, unsigned long page)
+{
+ pd->data_page = page;
+}
+
+static inline void *__get_handle(struct process_data *pd)
+{
+ return pd->handle;
+}
+
+static inline void __set_handle(struct process_data *pd, void __user *handle)
+{
+ pd->handle = handle;
+}
+
+static inline long __get_attempts(struct process_data *pd)
+{
+ return pd->attempts;
+}
+
+static inline void __set_attempts(struct process_data *pd, long attempts)
+{
+ pd->attempts = attempts;
+}
+
+static inline long __get_refcount(struct process_data *pd)
+{
+ return pd->refcount;
+}
+
+static inline void __set_refcount(struct process_data *pd, long refcount)
+{
+ pd->refcount = refcount;
+}
+
+
+
+
+static unsigned long __find_dentry_base(struct mm_struct *mm,
+ struct dentry *dentry)
+{
+ struct vm_area_struct *vma;
+
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ if (check_vma(vma, dentry))
+ return vma->vm_start;
+ }
+
+ return 0;
+}
+
+static unsigned long find_dentry_base(struct task_struct *task,
+ struct dentry *dentry)
+{
+ struct mm_struct *mm = task->mm;
+ unsigned long addr;
+
+#ifdef CONFIG_ARM
+ down_read(&mm->mmap_sem);
+#endif /* CONFIG_ARM */
+ addr = __find_dentry_base(mm, dentry);
+#ifdef CONFIG_ARM
+ up_read(&mm->mmap_sem);
+#endif /* CONFIG_ARM */
+
+ return addr;
+}
+
+static int __pd_create_on_demand(void)
+{
+ if (handlers_info == NULL) {
+ handlers_info = preload_storage_get_handlers_info();
+ if (handlers_info == NULL)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+
+enum preload_state_t preload_pd_get_state(struct process_data *pd)
+{
+ if (pd == NULL)
+ return 0;
+
+ return __get_state(pd);
+}
+
+void preload_pd_set_state(struct process_data *pd, enum preload_state_t state)
+{
+ if (pd == NULL) {
+ printk(PRELOAD_PREFIX "%d: No process data! Current %d %s\n", __LINE__,
+ current->tgid, current->comm);
+ return;
+ }
+
+ __set_state(pd, state);
+}
+
+unsigned long preload_pd_get_loader_base(struct process_data *pd)
+{
+ if (pd == NULL)
+ return ERROR;
+
+ return __get_loader_base(pd);
+}
+
+void preload_pd_set_loader_base(struct process_data *pd, unsigned long vaddr)
+{
+ if (pd == NULL) {
+ printk(PRELOAD_PREFIX "%d: No process data! Current %d %s\n", __LINE__,
+ current->tgid, current->comm);
+ return;
+ }
+
+ __set_loader_base(pd, vaddr);
+}
+
+unsigned long preload_pd_get_handlers_base(struct process_data *pd)
+{
+ if (pd == NULL)
+ return 0;
+
+ return __get_handlers_base(pd);
+}
+
+void preload_pd_set_handlers_base(struct process_data *pd, unsigned long vaddr)
+{
+ if (pd == NULL) {
+ printk(PRELOAD_PREFIX "%d: No process data! Current %d %s\n", __LINE__,
+ current->tgid, current->comm);
+ return;
+ }
+
+ __set_handlers_base(pd, vaddr);
+}
+
+void preload_pd_put_path(struct process_data *pd)
+{
+ if (pd == NULL) {
+ printk(PRELOAD_PREFIX "%d: No process data! Current %d %s\n", __LINE__,
+ current->tgid, current->comm);
+ return;
+ }
+
+ if (__get_data_page(pd) == 0)
+ return;
+
+ __set_data_page(pd, 0);
+}
+
+char __user *preload_pd_get_path(struct process_data *pd)
+{
+ /* This function should be called only for current */
+
+ struct task_struct *task = current;
+ unsigned long page = __get_data_page(pd);
+ int ret;
+
+ if (pd == NULL || page == 0)
+ return NULL;
+
+ if (pd->is_mapped == 1)
+ return __get_path(pd);
+
+ ret = preload_patcher_write_string((void *)page, handlers_info->path,
+ strnlen(handlers_info->path, PATH_MAX),
+ task);
+ if (ret <= 0) {
+ printk(KERN_ERR PRELOAD_PREFIX "Cannot copy string to user!\n");
+ goto get_path_failed;
+ }
+
+ pd->is_mapped = 1;
+
+ return __get_path(pd);
+
+get_path_failed:
+
+ return NULL;
+}
+
+
+
+void *preload_pd_get_handle(struct process_data *pd)
+{
+ if (pd == NULL)
+ return NULL;
+
+ return __get_handle(pd);
+}
+
+void preload_pd_set_handle(struct process_data *pd, void __user *handle)
+{
+ if (pd == NULL) {
+ printk(PRELOAD_PREFIX "%d: No process data! Current %d %s\n", __LINE__,
+ current->tgid, current->comm);
+ return;
+ }
+
+ __set_handle(pd, handle);
+}
+
+long preload_pd_get_attempts(struct process_data *pd)
+{
+ if (pd == NULL)
+ return -EINVAL;
+
+ return __get_attempts(pd);
+}
+
+void preload_pd_dec_attempts(struct process_data *pd)
+{
+ long attempts;
+
+ if (pd == NULL) {
+ printk(PRELOAD_PREFIX "%d: No process data! Current %d %s\n", __LINE__,
+ current->tgid, current->comm);
+ return;
+ }
+
+ attempts = __get_attempts(pd);
+ attempts--;
+ __set_attempts(pd, attempts);
+}
+
+void preload_pd_inc_refs(struct process_data *pd)
+{
+ long refs;
+
+ if (pd == NULL) {
+ printk(PRELOAD_PREFIX "%d: No process data! Current %d %s\n", __LINE__,
+ current->tgid, current->comm);
+ return;
+ }
+
+ refs = __get_refcount(pd);
+ refs++;
+ __set_refcount(pd, refs);
+}
+
+void preload_pd_dec_refs(struct process_data *pd)
+{
+ long refs;
+
+ if (pd == NULL) {
+ printk(PRELOAD_PREFIX "%d: No process data! Current %d %s\n", __LINE__,
+ current->tgid, current->comm);
+ return;
+ }
+
+ refs = __get_refcount(pd);
+ refs--;
+ __set_refcount(pd, refs);
+}
+
+long preload_pd_get_refs(struct process_data *pd)
+{
+ if (pd == NULL)
+ return -EINVAL;
+
+ return __get_refcount(pd);
+}
+
+int preload_pd_create_pd(void** target_place, struct task_struct *task)
+{
+ struct process_data *pd;
+ unsigned long page = 0;
+ unsigned long base;
+ struct dentry *dentry;
+ int ret;
+
+ ret = __pd_create_on_demand();
+ if (ret)
+ goto create_pd_exit;
+
+ pd = kzalloc(sizeof(*pd), GFP_ATOMIC);
+ if (pd == NULL) {
+ ret = -ENOMEM;
+ goto create_pd_exit;
+ }
+
+ ret = 0;
+
+ /* 1. check if loader is already mapped */
+ dentry = preload_debugfs_get_loader_dentry();
+ base = find_dentry_base(task, dentry);
+ if (base)
+ __set_loader_base(pd, base);
+
+ /* 2. check if handlers are already mapped */
+ base = find_dentry_base(task, handlers_info->dentry);
+ if (base) {
+ __set_handlers_base(pd, base);
+ __set_state(pd, LOADED);
+ }
+
+ /* 3. map page to store path */
+#ifdef CONFIG_ARM
+ down_write(¤t->mm->mmap_sem);
+#endif
+
+ page = swap_do_mmap(NULL, 0, PAGE_SIZE, PROT_READ,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0);
+#ifdef CONFIG_ARM
+ up_write(¤t->mm->mmap_sem);
+#endif
+ if (IS_ERR((void *)page)) {
+ printk(KERN_ERR PRELOAD_PREFIX "Cannot alloc page for %u\n", task->tgid);
+ ret = -ENOMEM;
+ goto create_pd_exit;
+ }
+
+ pd->is_mapped = 0;
+
+ __set_data_page(pd, page);
+ __set_attempts(pd, PRELOAD_MAX_ATTEMPTS);
+
+ *target_place = pd;
+
+create_pd_exit:
+ return ret;
+}
+
+int preload_pd_init(void)
+{
+ return 0;
+}
+
+void preload_pd_uninit(void)
+{
+ if (handlers_info)
+ preload_storage_put_handlers_info(handlers_info);
+ handlers_info = NULL;
+}
--- /dev/null
+#ifndef __PRELOAD_PD_H__
+#define __PRELOAD_PD_H__
+
+struct process_data;
+struct task_struct;
+
+/* process preload states */
+enum preload_state_t {
+ NOT_LOADED,
+ LOADING,
+ LOADED,
+ FAILED,
+ ERROR
+};
+
+//enum preload_state_t preload_pd_get_state(struct task_struct *task);
+//void preload_pd_set_state(struct task_struct *task, enum preload_state_t state);
+//unsigned long preload_pd_get_loader_base(struct task_struct *task);
+//void preload_pd_set_loader_base(struct task_struct *task, unsigned long vaddr);
+//unsigned long preload_pd_get_handlers_base(struct task_struct *task);
+//void preload_pd_set_handlers_base(struct task_struct *task,
+// unsigned long vaddr);
+////unsigned long preload_pd_get_flags(struct task_struct *task);
+////void preload_pd_set_flags(struct task_struct *task, unsigned long flags);
+//void *preload_pd_get_handle(struct task_struct *task);
+//void preload_pd_set_handle(struct task_struct *task, void __user *handle);
+//
+//long preload_pd_get_attempts(struct task_struct *task);
+//void preload_pd_dec_attempts(struct task_struct *task);
+//
+//void preload_pd_inc_refs(struct task_struct *task);
+//void preload_pd_dec_refs(struct task_struct *task);
+//long preload_pd_get_refs(struct task_struct *task);
+//
+//char __user *preload_pd_get_path(void);
+//void preload_pd_put_path(struct task_struct *task);
+//
+//int preload_pd_create_pd(struct process_data **pd_pp, struct task_struct *task);
+
+
+enum preload_state_t preload_pd_get_state(struct process_data *pd);
+void preload_pd_set_state(struct process_data *pd, enum preload_state_t state);
+unsigned long preload_pd_get_loader_base(struct process_data *pd);
+void preload_pd_set_loader_base(struct process_data *pd, unsigned long vaddr);
+unsigned long preload_pd_get_handlers_base(struct process_data *pd);
+void preload_pd_set_handlers_base(struct process_data *pd, unsigned long vaddr);
+void *preload_pd_get_handle(struct process_data *pd);
+void preload_pd_set_handle(struct process_data *pd, void __user *handle);
+
+long preload_pd_get_attempts(struct process_data *pd);
+void preload_pd_dec_attempts(struct process_data *pd);
+
+void preload_pd_inc_refs(struct process_data *pd);
+void preload_pd_dec_refs(struct process_data *pd);
+long preload_pd_get_refs(struct process_data *pd);
+
+char __user *preload_pd_get_path(struct process_data *pd);
+void preload_pd_put_path(struct process_data *pd);
+
+int preload_pd_create_pd(void **target_place, struct task_struct *task);
+
+int preload_pd_init(void);
+void preload_pd_uninit(void);
+
+
+#endif /* __PRELOAD_PD_H__*/
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/probes/preload_probe.c
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov: Preload implement
+ *
+ */
+
+#include <linux/module.h>
+#include <us_manager/us_manager.h>
+#include <us_manager/probes/register_probes.h>
+#include <us_manager/sspt/sspt_page.h>
+#include <writer/swap_writer_module.h>
+#include <uprobe/swap_uprobes.h>
+#include <us_manager/sspt/ip.h>
+#include "preload_probe.h"
+#include "preload.h"
+#include "preload_module.h"
+#include "preload_debugfs.h"
+
+static unsigned long long probes_count = 0;
+
+static int preload_info_copy(struct probe_info *dest,
+ const struct probe_info *source)
+{
+ memcpy(dest, source, sizeof(*source));
+
+ return 0;
+}
+
+static void preload_info_cleanup(struct probe_info *probe_i)
+{
+}
+
+static struct uprobe *preload_get_uprobe(struct us_ip *ip)
+{
+ return &ip->retprobe.up;
+}
+
+/* We count all preload probes to know current state of the preload module:
+ * if there are registered probes, than it is currently running, if there is no
+ * probes, module is just ready to be used.
+ *
+ * If there was no registered probes and now they're appeared, change state to
+ * 'running'.
+ */
+static inline void inc_probes(void)
+{
+ if (probes_count == 0)
+ preload_module_set_running();
+
+ probes_count++;
+}
+
+/* If there were probes, but now there's no of them, change state to 'ready'.
+ */
+static inline void dec_probes(void)
+{
+ if (unlikely(probes_count == 0))
+ printk(KERN_ERR PRELOAD_PREFIX "Trying to remove probe when there is no one!\n");
+
+ probes_count--;
+ if (probes_count == 0)
+ preload_module_set_ready();
+}
+
+/* Checks if preload can be in 'ready' state. It is so, if loader's dentry and
+ * offset are specified.
+ */
+static inline bool can_be_ready(void)
+{
+ struct dentry *dentry = preload_debugfs_get_loader_dentry();
+ unsigned long offset = preload_debugfs_get_loader_offset();
+
+ if (dentry != NULL && offset != 0)
+ return true;
+
+ return false;
+}
+
+/* Registers probe if preload is 'running' or 'ready'.
+ */
+static int preload_register_probe(struct us_ip *ip)
+{
+ if (preload_module_is_not_ready()) {
+ if (can_be_ready()) {
+ preload_module_set_ready();
+ } else {
+ printk(PRELOAD_PREFIX "Module is not initialized!\n");
+ return -EINVAL;
+ }
+ }
+
+ inc_probes();
+
+ return swap_register_uretprobe(&ip->retprobe);
+}
+
+static void preload_unregister_probe(struct us_ip *ip, int disarm)
+{
+ __swap_unregister_uretprobe(&ip->retprobe, disarm);
+
+ dec_probes();
+}
+
+static void preload_init(struct us_ip *ip)
+{
+ preload_module_uprobe_init(ip);
+}
+
+static void preload_uninit(struct us_ip *ip)
+{
+ preload_module_uprobe_exit(ip);
+
+ preload_info_cleanup(&ip->probe_i);
+}
+
+static struct probe_iface preload_iface = {
+ .init = preload_init,
+ .uninit = preload_uninit,
+ .reg = preload_register_probe,
+ .unreg = preload_unregister_probe,
+ .get_uprobe = preload_get_uprobe,
+ .copy = preload_info_copy,
+ .cleanup = preload_info_cleanup
+};
+
+static int get_caller_info_copy(struct probe_info *dest,
+ const struct probe_info *source)
+{
+ memcpy(dest, source, sizeof(*source));
+
+ return 0;
+}
+
+static void get_caller_info_cleanup(struct probe_info *probe_i)
+{
+}
+
+static struct uprobe *get_caller_get_uprobe(struct us_ip *ip)
+{
+ return &ip->uprobe;
+}
+
+static int get_caller_register_probe(struct us_ip *ip)
+{
+ return swap_register_uprobe(&ip->uprobe);
+}
+
+static void get_caller_unregister_probe(struct us_ip *ip, int disarm)
+{
+ __swap_unregister_uprobe(&ip->uprobe, disarm);
+}
+
+static void get_caller_init(struct us_ip *ip)
+{
+ preload_module_get_caller_init(ip);
+}
+
+static void get_caller_uninit(struct us_ip *ip)
+{
+ preload_module_get_caller_exit(ip);
+
+ get_caller_info_cleanup(&ip->probe_i);
+}
+
+static struct probe_iface get_caller_iface = {
+ .init = get_caller_init,
+ .uninit = get_caller_uninit,
+ .reg = get_caller_register_probe,
+ .unreg = get_caller_unregister_probe,
+ .get_uprobe = get_caller_get_uprobe,
+ .copy = get_caller_info_copy,
+ .cleanup = get_caller_info_cleanup
+};
+
+static void get_call_type_init(struct us_ip *ip)
+{
+ preload_module_get_call_type_init(ip);
+}
+
+static void get_call_type_uninit(struct us_ip *ip)
+{
+ preload_module_get_call_type_exit(ip);
+
+ get_caller_info_cleanup(&ip->probe_i);
+}
+
+static struct probe_iface get_call_type_iface = {
+ .init = get_call_type_init,
+ .uninit = get_call_type_uninit,
+ .reg = get_caller_register_probe,
+ .unreg = get_caller_unregister_probe,
+ .get_uprobe = get_caller_get_uprobe,
+ .copy = get_caller_info_copy,
+ .cleanup = get_caller_info_cleanup
+};
+
+
+
+int register_preload_probes(void)
+{
+ int ret;
+
+ ret = swap_register_probe_type(SWAP_PRELOAD_PROBE, &preload_iface);
+ if (ret != 0)
+ return ret;
+
+ ret = swap_register_probe_type(SWAP_GET_CALLER, &get_caller_iface);
+ if (ret != 0)
+ return ret;
+
+ ret = swap_register_probe_type(SWAP_GET_CALL_TYPE, &get_call_type_iface);
+ return ret;
+}
+
+void unregister_preload_probes(void)
+{
+ swap_unregister_probe_type(SWAP_PRELOAD_PROBE);
+ swap_unregister_probe_type(SWAP_GET_CALLER);
+ swap_unregister_probe_type(SWAP_GET_CALL_TYPE);
+}
--- /dev/null
+/*
+ * SWAP uprobe manager
+ * modules/us_manager/probes/preload_probe.h
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) Samsung Electronics, 2014
+ *
+ * 2014 Alexander Aksenov: FBI implement
+ *
+ */
+
+#ifndef __PRELOAD_PROBE_H__
+#define __PRELOAD_PROBE_H__
+
+/* Probe type, specifies when probe should be ran. */
+enum preload_probe_type_t {
+ SWAP_PRELOAD_INTERNAL_CALL = 0, /* Run probe only when it is called from
+ target binaries. */
+ SWAP_PRELOAD_ALWAYS = 1, /* Run probe always. */
+ SWAP_PRELOAD_DISABLE_HANDLING = 2 /* Disable handlers execution. */
+};
+
+/* Preload probe info. */
+struct preload_info {
+ unsigned long handler; /* Handler offset in probe library. */
+ enum preload_probe_type_t type; /* Preload probe type. */
+};
+
+/* Get caller probe info */
+struct get_caller_info {
+};
+
+/* Get call type probe info */
+struct get_call_type_info {
+};
+
+int register_preload_probes(void);
+void unregister_preload_probes(void);
+
+#endif /* __URETPROBE_H__ */
--- /dev/null
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <ks_features/ks_map.h>
+#include <linux/fs.h>
+#include "preload.h"
+#include "preload_module.h"
+#include "preload_storage.h"
+
+static struct bin_info __handlers_info = { NULL, NULL };
+static struct bin_info __linker_info = { NULL, NULL };
+
+static inline struct bin_info *__get_handlers_info(void)
+{
+ return &__handlers_info;
+}
+
+static inline bool __check_handlers_info(void)
+{
+ return (__handlers_info.dentry != NULL); /* TODO */
+}
+
+static inline int __init_handlers_info(char *path)
+{
+ struct dentry *dentry;
+ size_t len = strnlen(path, PATH_MAX);
+ int ret = 0;
+
+ __handlers_info.path = kmalloc(len + 1, GFP_KERNEL);
+ if (__handlers_info.path == NULL) {
+ ret = -ENOMEM;
+ goto init_handlers_fail;
+ }
+
+ dentry = get_dentry(path);
+ if (!dentry) {
+ ret = -ENOENT;
+ goto init_handlers_fail_free;
+ }
+
+ strncpy(__handlers_info.path, path, len);
+ __handlers_info.path[len] = '\0';
+ __handlers_info.dentry = dentry;
+
+ return ret;
+
+init_handlers_fail_free:
+ kfree(__handlers_info.path);
+
+init_handlers_fail:
+ return ret;
+}
+
+static inline void __drop_handlers_info(void)
+{
+ __handlers_info.path = NULL;
+ if (__handlers_info.dentry)
+ put_dentry(__handlers_info.dentry);
+ __handlers_info.dentry = NULL;
+ kfree(__handlers_info.path);
+}
+
+static inline struct bin_info *__get_linker_info(void)
+{
+ return &__linker_info;
+}
+
+static inline bool __check_linker_info(void)
+{
+ return (__linker_info.dentry != NULL); /* TODO */
+}
+
+static inline int __init_linker_info(char *path)
+{
+ struct dentry *dentry;
+ size_t len = strnlen(path, PATH_MAX);
+ int ret = 0;
+
+
+ __linker_info.path = kmalloc(len + 1, GFP_KERNEL);
+ if (__linker_info.path == NULL) {
+ ret = -ENOMEM;
+ goto init_linker_fail;
+ }
+
+ dentry = get_dentry(path);
+ if (!dentry) {
+ ret = -ENOENT;
+ goto init_linker_fail_free;
+ }
+
+ strncpy(__linker_info.path, path, len);
+ __linker_info.path[len] = '\0';
+ __linker_info.dentry = dentry;
+
+ return ret;
+
+init_linker_fail_free:
+ kfree(__linker_info.path);
+
+init_linker_fail:
+
+ return ret;
+}
+
+static inline void __drop_linker_info(void)
+{
+ __linker_info.path = NULL;
+ if (__linker_info.dentry)
+ put_dentry(__linker_info.dentry);
+ __linker_info.dentry = NULL;
+ kfree(__linker_info.path);
+}
+
+
+
+
+int preload_storage_set_handlers_info(char *path)
+{
+ return __init_handlers_info(path);
+}
+
+struct bin_info *preload_storage_get_handlers_info(void)
+{
+ struct bin_info *info = __get_handlers_info();
+
+ if (__check_handlers_info())
+ return info;
+
+ return NULL;
+}
+
+void preload_storage_put_handlers_info(struct bin_info *info)
+{
+}
+
+int preload_storage_set_linker_info(char *path)
+{
+ return __init_linker_info(path);
+}
+
+struct bin_info *preload_storage_get_linker_info(void)
+{
+ struct bin_info *info = __get_linker_info();
+
+ if (__check_linker_info())
+ return info;
+
+ return NULL;
+}
+
+void preload_storage_put_linker_info(struct bin_info *info)
+{
+}
+
+int preload_storage_init(void)
+{
+ return 0;
+}
+
+void preload_storage_exit(void)
+{
+ __drop_handlers_info();
+ __drop_linker_info();
+}
--- /dev/null
+#ifndef __PRELOAD_STORAGE_H__
+#define __PRELOAD_STORAGE_H__
+
+struct bin_info {
+ char *path;
+ /* ghot */
+ struct dentry *dentry;
+};
+
+int preload_storage_set_handlers_info(char *path);
+struct bin_info *preload_storage_get_handlers_info(void);
+void preload_storage_put_handlers_info(struct bin_info *info);
+
+int preload_storage_set_linker_info(char *path);
+struct bin_info *preload_storage_get_linker_info(void);
+void preload_storage_put_linker_info(struct bin_info *info);
+
+int preload_storage_init(void);
+void preload_storage_exit(void);
+
+#endif /* __PRELOAD_HANDLERS_H__ */
--- /dev/null
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/list.h>
+#include "preload.h"
+#include "preload_threads.h"
+#include "preload_debugfs.h"
+#include "preload_patcher.h"
+#include "preload_pd.h"
+
+enum {
+ DEFAULT_SLOTS_CNT = 10,
+};
+
+struct thread_slot {
+ struct list_head list;
+ struct task_struct *task;
+ struct list_head disabled_addrs; /* No use for spinlock - called only
+ in one thread */
+ unsigned long caller;
+ unsigned char call_type;
+};
+
+struct disabled_addr {
+ struct list_head list;
+ unsigned long addr;
+};
+
+static LIST_HEAD(thread_slot_list);
+static spinlock_t slock;
+static unsigned long sflags;
+
+
+static inline void __lock_init(void)
+{
+ spin_lock_init(&slock);
+}
+
+static inline void __lock(void)
+{
+ spin_lock_irqsave(&slock, sflags);
+}
+
+static inline void __unlock(void)
+{
+ spin_unlock_irqrestore(&slock, sflags);
+}
+
+
+
+/* Checks slot for task */
+static inline bool __is_slot_for_task(struct thread_slot *slot,
+ struct task_struct *task)
+{
+ if (slot->task == task)
+ return true;
+
+ return false;
+}
+
+/* Checks slot if it is free */
+static inline bool __is_slot_free(struct thread_slot *slot)
+{
+ if (slot->task == NULL)
+ return true;
+
+ return false;
+}
+
+static inline bool __is_addr_found(struct disabled_addr *da,
+ unsigned long addr)
+{
+ if (da->addr == addr)
+ return true;
+
+ return false;
+}
+
+static inline void __remove_from_disable_list(struct disabled_addr *da)
+{
+ list_del(&da->list);
+ kfree(da);
+}
+
+static inline void __remove_whole_disable_list(struct thread_slot *slot)
+{
+ struct disabled_addr *da, *n;
+
+ list_for_each_entry_safe(da, n, &slot->disabled_addrs, list)
+ __remove_from_disable_list(da);
+}
+
+static inline void __init_slot(struct thread_slot *slot)
+{
+ slot->task = NULL;
+ slot->caller = 0;
+ slot->call_type = 0;
+ INIT_LIST_HEAD(&slot->disabled_addrs);
+}
+
+static inline void __reinit_slot(struct thread_slot *slot)
+{
+ __remove_whole_disable_list(slot);
+ __init_slot(slot);
+}
+
+static inline void __set_slot(struct thread_slot *slot,
+ struct task_struct *task, unsigned long caller,
+ unsigned char call_type)
+{
+ slot->task = task;
+ slot->caller = caller;
+ slot->call_type = call_type;
+}
+
+static inline int __add_to_disable_list(struct thread_slot *slot,
+ unsigned long disable_addr)
+{
+ struct disabled_addr *da = kmalloc(sizeof(*da), GFP_KERNEL);
+
+ if (da == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&da->list);
+ da->addr = disable_addr;
+ list_add_tail(&da->list, &slot->disabled_addrs);
+
+ return 0;
+}
+
+static inline struct disabled_addr *__find_disabled_addr(struct thread_slot *slot,
+ unsigned long addr)
+{
+ struct disabled_addr *da;
+
+ list_for_each_entry(da, &slot->disabled_addrs, list)
+ if (__is_addr_found(da, addr))
+ return da;
+
+ return NULL;
+}
+
+/* Adds a new slot */
+static inline struct thread_slot *__grow_slot(void)
+{
+ struct thread_slot *tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+
+ if (tmp == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&tmp->list);
+ __init_slot(tmp);
+ list_add_tail(&tmp->list, &thread_slot_list);
+
+ return tmp;
+}
+
+/* Free slot */
+static void __clean_slot(struct thread_slot *slot)
+{
+ list_del(&slot->list);
+ kfree(slot);
+}
+
+/* Free all slots. This and all the previous slot functions should be called
+ in locks. */
+static void __clean_all(void)
+{
+ struct thread_slot *slot, *n;
+
+ list_for_each_entry_safe(slot, n, &thread_slot_list, list)
+ __clean_slot(slot);
+}
+
+static inline struct thread_slot *__get_task_slot(struct task_struct *task)
+{
+ struct thread_slot *slot;
+
+ list_for_each_entry(slot, &thread_slot_list, list)
+ if (__is_slot_for_task(slot, task))
+ return slot;
+
+ return NULL;
+}
+
+
+
+
+int preload_threads_set_data(struct task_struct *task, unsigned long caller,
+ unsigned char call_type, unsigned long disable_addr)
+{
+ struct thread_slot *slot;
+ int ret = 0;
+
+ __lock();
+
+ list_for_each_entry(slot, &thread_slot_list, list) {
+ if (__is_slot_free(slot)) {
+ __set_slot(slot, task, caller, call_type);
+ if ((disable_addr != 0) &&
+ (__add_to_disable_list(slot, disable_addr) != 0)) {
+ printk(PRELOAD_PREFIX "Cannot alloc memory!\n");
+ ret = -ENOMEM;
+ }
+ goto set_data_done;
+ }
+ }
+
+ /* If there is no empty slots - grow */
+ slot = __grow_slot();
+ if (slot == NULL) {
+ ret = -ENOMEM;
+ goto set_data_done;
+ }
+
+ __set_slot(slot, task, caller, call_type);
+
+set_data_done:
+ __unlock();
+
+ return ret;
+}
+
+int preload_threads_get_caller(struct task_struct *task, unsigned long *caller)
+{
+ struct thread_slot *slot;
+ int ret = 0;
+
+ __lock();
+
+ slot = __get_task_slot(task);
+ if (slot != NULL) {
+ *caller = slot->caller;
+ goto get_caller_done;
+ }
+
+ /* If we're here - slot was not found */
+ ret = -EINVAL;
+
+get_caller_done:
+ __unlock();
+
+ return ret;
+}
+
+int preload_threads_get_call_type(struct task_struct *task,
+ unsigned char *call_type)
+{
+ struct thread_slot *slot;
+ int ret = 0;
+
+ __lock();
+
+ slot = __get_task_slot(task);
+ if (slot != NULL) {
+ *call_type = slot->call_type;
+ goto get_call_type_done;
+ }
+
+ /* If we're here - slot was not found */
+ ret = -EINVAL;
+
+get_call_type_done:
+ __unlock();
+
+ return ret;
+}
+
+bool preload_threads_check_disabled_probe(struct task_struct *task,
+ unsigned long addr)
+{
+ struct thread_slot *slot;
+ bool ret = false;
+
+ __lock();
+
+ slot = __get_task_slot(task);
+ if (slot != NULL)
+ ret = __find_disabled_addr(slot, addr) == NULL ? false : true;
+
+ __unlock();
+
+ return ret;
+}
+
+void preload_threads_enable_probe(struct task_struct *task, unsigned long addr)
+{
+ struct thread_slot *slot;
+ struct disabled_addr *da;
+
+ __lock();
+
+ slot = __get_task_slot(task);
+ if (slot == NULL) {
+ printk(PRELOAD_PREFIX "Error! Slot not found!\n");
+ goto enable_probe_failed;
+ }
+
+ da = __find_disabled_addr(slot, addr);
+ if (da != NULL)
+ __remove_from_disable_list(da);
+
+enable_probe_failed:
+
+ __unlock();
+}
+
+int preload_threads_put_data(struct task_struct *task)
+{
+ struct thread_slot *slot;
+ int ret = 0;
+
+ __lock();
+
+ slot = __get_task_slot(task);
+ if (slot != NULL) {
+ __reinit_slot(slot);
+ goto put_data_done;
+ }
+
+put_data_done:
+ __unlock();
+
+ return ret;
+}
+
+/* Allocates slots */
+int preload_threads_init(void)
+{
+ int i, ret = 0;
+
+ __lock_init();
+
+ __lock();
+
+ for (i = 0; i < DEFAULT_SLOTS_CNT; i++) {
+ if (__grow_slot() == NULL) {
+ ret = -ENOMEM;
+ goto init_failed;
+ }
+ }
+
+ __unlock();
+
+ return 0;
+
+init_failed:
+
+ __clean_all();
+ __unlock();
+
+ return ret;
+}
+
+/* Cleans slots */
+void preload_threads_exit(void)
+{
+ __lock();
+ __clean_all();
+ __unlock();
+}
--- /dev/null
+#ifndef __PRELOAD_THREADS_H__
+#define __PRELOAD_THREADS_H__
+
+struct task_struct;
+
+int preload_threads_set_data(struct task_struct *task, unsigned long caller,
+ unsigned char call_type, unsigned long disable_addr);
+int preload_threads_get_caller(struct task_struct *task, unsigned long *caller);
+int preload_threads_get_call_type(struct task_struct *task,
+ unsigned char *call_type);
+bool preload_threads_check_disabled_probe(struct task_struct *task,
+ unsigned long addr);
+void preload_threads_enable_probe(struct task_struct *task, unsigned long addr);
+int preload_threads_put_data(struct task_struct *task);
+int preload_threads_init(void);
+void preload_threads_exit(void);
+
+#endif /* __PRELOAD_THREADS_H__ */
#include <linux/types.h>
+#include <preload/preload_probe.h> /* TODO Remove */
#include <retprobe/retprobe.h> /* TODO Remove */
*/
enum probe_t {
SWAP_RETPROBE = 0, /* Retprobe */
+ SWAP_PRELOAD_PROBE = 2, /* Preload probe */
SWAP_WEBPROBE = 3, /* Webprobe */
+ SWAP_GET_CALLER = 4, /* Get caller probe. Supports preload */
+ SWAP_GET_CALL_TYPE = 5, /* Get call type probe. Supports preload */
SWAP_PROBE_MAX_VAL /* Probes max value. */
};
/* Union of all SWAP supported probe types */
union {
struct retprobe_info rp_i;
+ struct preload_info pl_i;
+ struct get_caller_info gc_i;
+ struct get_call_type_info gct_i;
};
};