SmartAudio/lichee/linux-4.9/drivers/char/oases/hook_subfunc.c

444 lines
11 KiB
C
Executable File

/*
* hook ops for sub function
*
* Copyright (C) 2016 Baidu, Inc. All Rights Reserved.
*
* You should have received a copy of license along with this program;
* if not, ask for it from Baidu, Inc.
*
*/
#include <linux/types.h>
#include <linux/kobject.h>
#include <linux/slab.h>
#include <linux/kallsyms.h>
#if IS_ENABLED(CONFIG_MODULES)
#include <linux/module.h>
#endif
#include "hook_subfunc.h"
#include "util.h"
#include "hook_insn.h"
#include "patch_api.h"
#include "inlinehook.h"
#include "inlinehook_offset.h"
#include "patch_info.h"
#include "patch_mgr.h"
#include "kallsyms.h"
static const char *subfunc_get_pname(struct oases_patch_entry *patch)
{
return kp_subfunc(patch)->parent;
}
static const char *subfunc_get_cname(struct oases_patch_entry *patch)
{
return kp_subfunc(patch)->child;
}
static int subfunc_get_count(struct oases_patch_entry *patch)
{
return kp_subfunc(patch)->count;
}
static const char *get_pmodule_name(struct oases_patch_entry *patch)
{
#if IS_ENABLED(CONFIG_MODULES)
struct module *m = kp_subfunc(patch)->pmodule;
if (m)
return m->name;
#endif
return kp_subfunc(patch)->pmod ?: VMLINUX;
}
static const char *get_cmodule_name(struct oases_patch_entry *patch)
{
#if IS_ENABLED(CONFIG_MODULES)
struct module *m = kp_subfunc(patch)->cmodule;
if (m)
return m->name;
#endif
return kp_subfunc(patch)->cmod ?: VMLINUX;
}
static int subfunc_show_info(struct oases_patch_entry *patch,
struct kobject *kobj, struct kobj_attribute *attr, char *buf, int off)
{
return scnprintf(buf + off, PAGE_SIZE - off - 1,
"%s %s %s %s\n", get_pmodule_name(patch), subfunc_get_pname(patch),
get_cmodule_name(patch), subfunc_get_cname(patch));
}
static int subfunc_with_each_insn(struct oases_patch_entry *patch,
int (*callback)(struct oases_patch_entry *patch, struct oases_insn *insn, void *data),
void *data)
{
struct oases_subfunc *kp = kp_subfunc(patch);
int ret, i;
for (i = 0; i < kp->count; i++) {
ret = callback(patch, &kp->insn[i], data);
if (ret)
return ret;
}
return 0;
}
static int subfunc_check_user(struct oases_subfunc *kp,
struct patch_subfunc *up)
{
if (!valid_patch_pointer(pb_owner(kp), up))
return -EINVAL;
if (!valid_patch_pointer(pb_owner(kp), up->parent))
return -EINVAL;
if (up->pmod && !valid_patch_pointer(pb_owner(kp), up->pmod))
return -EINVAL;
if (!valid_patch_pointer(pb_owner(kp), up->child))
return -EINVAL;
if (up->cmod && !valid_patch_pointer(pb_owner(kp), up->cmod))
return -EINVAL;
if (!valid_patch_pointer(pb_owner(kp), up->filter))
return -EINVAL;
return oases_check_patch_func(up->parent);
}
static int subfunc_copy_from_user_l(struct oases_subfunc *kp,
struct patch_subfunc *up, int flags)
{
int ret;
struct oases_find_symbol args;
unsigned long off, len;
/* search parent func symbol */
memset(&args, 0, sizeof(struct oases_find_symbol));
args.mod = up->pmod;
args.name = up->parent;
args.callback = NULL;
args.api_use = 0;
ret = oases_lookup_name_internal(&args);
if (ret < 0) {
oases_error("could not find %s, %lu\n", args.name, args.count);
return -EINVAL;
}
kp->parent = up->parent;
kp->pmod = up->pmod;
kp->pmodule = args.module;
kp->start = args.addr;
/* search child func symbol */
memset(&args, 0, sizeof(struct oases_find_symbol));
args.mod = up->cmod;
args.name = up->child;
args.callback = NULL;
args.api_use = 0;
ret = oases_lookup_name_internal(&args);
if (ret < 0) {
oases_error("could not find %s, %lu\n", args.name, args.count);
return -EINVAL;
}
kp->child = up->child;
kp->cmod = up->cmod;
kp->cmodule = args.module;
kp->sub = args.addr;
ret = kallsyms_lookup_size_offset((unsigned long) kp->start, &len, &off);
if (!ret || off || !len) {
oases_error("could not size %s, %d, %lu, %lu\n", kp->parent, ret, off, len);
return -EINVAL;
}
kp->end = kp->start + len;
return 0;
}
static int subfunc_copy_from_user_alloc(struct oases_subfunc *kp,
struct patch_subfunc *up, int flags)
{
int ret, i;
unsigned long mask;
u32 *ps, *pe, *pi;
ps = (u32 *) kp->start;
pe = (u32 *) kp->end;
mask = up->mask;
oases_debug("up->mask: %ld\n", mask);
i = 0;
for (pi = ps; pi < pe; pi++) {
if (is_insn_bl(*pi) && is_bl_to(pi, kp->sub)) {
if (i++ > OASES_SUBFUNC_MASK_MAX - 1) {
oases_error("OASES_SUBFUNC_MASK_MAX=%d reached\n", OASES_SUBFUNC_MASK_MAX);
return -ENOMEM;
}
if (!up->mask) {
kp->count += 1;
} else {
if (mask & 1)
kp->count += 1;
if (!(mask >>= 1))
break;
}
}
}
oases_debug("kp->count == %d\n", kp->count);
if (kp->count == 0)
return -EINVAL;
oases_debug("mask == %ld\n", mask);
if (up->mask && mask)
return -EINVAL;
kp->mask = up->mask;
ret = oases_insn_alloc_nr(&kp->insn, kp->pmodule, kp->count, flags);
if (ret)
return ret;
i = 0;
mask = kp->mask;
for (pi = ps; pi < pe; pi++) {
if (is_insn_bl(*pi) && is_bl_to(pi, kp->sub)) {
if (!mask) {
kp->insn[i].address = pi;
kp->insn[i].origin_to = kp->sub;
kp->insn[i].handler = up->filter;
i++;
} else {
if (mask & 1) {
kp->insn[i].address = pi;
kp->insn[i].origin_to = kp->sub;
kp->insn[i].handler = up->filter;
i++;
}
if (!(mask >>= 1))
break;
}
}
}
return 0;
}
static int subfunc_copy_from_user(struct oases_subfunc *kp,
struct patch_subfunc *up, int flags)
{
int ret;
ret = subfunc_copy_from_user_l(kp, up, flags);
if (ret < 0)
goto fail;
ret = subfunc_copy_from_user_alloc(kp, up, flags);
fail:
#ifdef CONFIG_MODULES
if (ret < 0) {
if (kp->cmodule) {
oases_unref_module(kp->cmodule);
kp->cmodule = NULL;
}
if (kp->pmodule) {
oases_unref_module(kp->pmodule);
kp->pmodule = NULL;
}
}
#endif
return ret;
}
static int subfunc_create(struct oases_patch_entry *patch,
void *user, int flags)
{
int ret;
struct oases_subfunc *kc = kp_subfunc(patch);
struct patch_subfunc *uc = user;
/* check user params */
ret = subfunc_check_user(kc, uc);
if (ret)
return ret;
/* validate modules and function names */
ret = subfunc_copy_from_user(kc, uc, flags);
if (ret)
return ret;
return 0;
}
static void subfunc_destroy(struct oases_patch_entry *patch)
{
struct oases_subfunc *kc = kp_subfunc(patch);
int i;
for (i = 0; i < kc->count; i++)
oases_insn_free(&kc->insn[i], kc->pmodule);
if (kc->insn)
kfree(kc->insn);
kc->insn = NULL;
kc->count = 0;
#if IS_ENABLED(CONFIG_MODULES)
if (kc->cmodule) {
oases_unref_module(kc->cmodule);
kc->cmodule = NULL;
}
if (kc->pmodule) {
oases_unref_module(kc->pmodule);
kc->pmodule = NULL;
}
#endif
}
static u32 subfunc_setup_jump(struct oases_patch_entry *patch,
struct oases_insn *insn)
{
int ret;
u32 x;
ret = oases_make_jump_insn(insn->address, insn->plt, &x);
if (ret)
return 0;
/* XXX: should not do this here, b -> bl */
#if defined(__aarch64__)
x |= 0x80000000;
#elif defined(__arm__)
x |= 0x01000000;
#endif
return x;
}
/* patch_subfunc_pre functions */
static int subfunc_pre_create(struct oases_patch_entry *patch, void *user)
{
return subfunc_create(patch, user, 0);
}
extern char oases_handler_subfunc_pre_start[];
extern char oases_handler_subfunc_pre_end[];
static int subfunc_pre_setup_trampoline(struct oases_patch_entry *patch,
struct oases_insn *insn)
{
void *trampoline = insn->trampoline;
memcpy(trampoline, &oases_handler_subfunc_pre_start[0],
&oases_handler_subfunc_pre_end[0] - &oases_handler_subfunc_pre_start[0]);
/* filter_addr */
*((void **)(trampoline + SUBFUNC_PRE_FILTER_ADDR_OFFSET)) = (void *)(insn->handler);
/* origin_addr */
*((void **)(trampoline + SUBFUNC_PRE_ORIGIN_ADDR_OFFSET)) = (void *)(insn->origin_to);
/* retn_addr */
*((void **)(trampoline + SUBFUNC_PRE_RETURN_ADDR_OFFSET)) = (void *)(insn->address + 4);
/* attack logger */
*((void **)(trampoline + SUBFUNC_PRE_PATCH_INFO_CTX_OFFSET)) = (void *)(kp_owner(patch));
*((void **)(trampoline + SUBFUNC_PRE_ATTACK_LOGGER_OFFSET)) = (void *)(oases_attack_logger);
return 0;
}
const struct oases_patch_desc oases_subfunc_pre_ops = {
.type = OASES_SUBFUNC_PRE,
.size = sizeof(struct oases_subfunc_pre),
.usize = sizeof(struct patch_subfunc_pre),
.get_name = subfunc_get_pname,
.get_count = subfunc_get_count,
.show_info = subfunc_show_info,
.with_each_insn = subfunc_with_each_insn,
.create = subfunc_pre_create,
.destroy = subfunc_destroy,
.setup_jump = subfunc_setup_jump,
.setup_trampoline = subfunc_pre_setup_trampoline,
};
/* subfunc_post functions */
static int subfunc_post_create(struct oases_patch_entry *patch, void *user)
{
return subfunc_create(patch, user, 0);
}
extern char oases_handler_subfunc_post_start[];
extern char oases_handler_subfunc_post_end[];
static int subfunc_post_setup_trampoline(struct oases_patch_entry *patch,
struct oases_insn *insn)
{
struct oases_subfunc *kp = kp_subfunc(patch);
void *trampoline = insn->trampoline;
memcpy(trampoline, &oases_handler_subfunc_post_start[0],
&oases_handler_subfunc_post_end[0] - &oases_handler_subfunc_post_start[0]);
/* filter_addr */
*((void **)(trampoline + SUBFUNC_POST_FILTER_ADDR_OFFSET)) = (void *)(insn->handler);
/* orig_addr */
*((void **)(trampoline + SUBFUNC_POST_ORIGIN_ADDR_OFFSET)) = (void *)(kp->sub);
/* attack logger */
*((void **)(trampoline + SUBFUNC_POST_PATCH_INFO_CTX_OFFSET)) = (void *)(kp_owner(patch));
*((void **)(trampoline + SUBFUNC_POST_ATTACK_LOGGER_OFFSET)) = (void *)(oases_attack_logger);
return 0;
}
const struct oases_patch_desc oases_subfunc_post_ops = {
.type = OASES_SUBFUNC_POST,
.size = sizeof(struct oases_subfunc_post),
.usize = sizeof(struct patch_subfunc_post),
.get_name = subfunc_get_pname,
.get_count = subfunc_get_count,
.show_info = subfunc_show_info,
.with_each_insn = subfunc_with_each_insn,
.create = subfunc_post_create,
.destroy = subfunc_destroy,
.setup_jump = subfunc_setup_jump,
.setup_trampoline = subfunc_post_setup_trampoline,
};
#if OASES_ENABLE_REPLACEMENT_HANDLER
static int subfunc_rep_create(struct oases_patch_entry *patch, void *user)
{
return subfunc_create(patch, user, OASES_INSN_FLAG_NO_IC);
}
static int subfunc_rep_setup_trampoline(struct oases_patch_entry *patch,
struct oases_insn *insn)
{
return 0;
}
const struct oases_patch_desc oases_subfunc_rep_ops = {
.type = OASES_SUBFUNC_REP,
.size = sizeof(struct oases_subfunc_rep),
.usize = sizeof(struct patch_subfunc_rep),
.get_name = subfunc_get_pname,
.get_count = subfunc_get_count,
.show_info = subfunc_show_info,
.with_each_insn = subfunc_with_each_insn,
.create = subfunc_rep_create,
.destroy = subfunc_destroy,
.setup_jump = subfunc_setup_jump,
.setup_trampoline = subfunc_rep_setup_trampoline,
};
#endif
/* subfunc_pre_post functions */
static int subfunc_pre_post_create(struct oases_patch_entry *patch,
void *user)
{
return subfunc_create(patch, user, OASES_INSN_FLAG_NO_IC);
}
static int subfunc_pre_post_setup_trampoline(
struct oases_patch_entry *patch, struct oases_insn *insn)
{
return 0;
}
const struct oases_patch_desc oases_subfunc_pre_post_ops = {
.type = OASES_SUBFUNC_PRE_POST,
.size = sizeof(struct oases_subfunc_pre_post),
.usize = sizeof(struct patch_subfunc_pre_post),
.get_name = subfunc_get_pname,
.get_count = subfunc_get_count,
.show_info = subfunc_show_info,
.with_each_insn = subfunc_with_each_insn,
.create = subfunc_pre_post_create,
.destroy = subfunc_destroy,
.setup_jump = subfunc_setup_jump,
.setup_trampoline = subfunc_pre_post_setup_trampoline,
};