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

253 lines
5.4 KiB
C
Executable File

/*
* patch_mgr.c - patch manager
*
* 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/vmalloc.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <linux/mm_types.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/syscalls.h>
#include "patch_mgr.h"
#include "util.h"
#include "patch_file.h"
#include "patch_info.h"
#include "patch_base.h"
#include "sysfs.h"
#include "plts.h"
#define FUNC_NAME_LEN_MAX 255
#define oases_uid_eq(a, b) ((a) == (b))
#define OASES_INVALID_UID (long)(-1)
LIST_HEAD(patchinfo_list);
int oases_check_patch_func(const char *name)
{
struct oases_patch_info *ctx, *c;
struct oases_patch_entry *patch, *p;
/* oases_mutex held */
list_for_each_entry_safe(ctx, c, &patchinfo_list, list) {
list_for_each_entry_safe(patch, p, &ctx->patches, list) {
if (!strncmp(name, kp_vtab(patch)->get_name(patch), FUNC_NAME_LEN_MAX - 1)) {
oases_error("duplicate name: %s\n", name);
return -EEXIST;
}
}
}
return 0;
}
int oases_check_patch(const char *id)
{
struct oases_patch_info *pos;
struct oases_patch_info *n;
/* oases_mutex held */
list_for_each_entry_safe(pos, n, &patchinfo_list, list) {
if (strncmp(pos->id, id, PATCH_ID_LEN - 1) == 0) {
oases_error("patch exist: %s\n", id);
return -EEXIST;
}
}
return 0;
}
void oases_free_patch(struct oases_patch_info *info)
{
struct oases_patch_entry *patch, *p;
if (!info) {
return;
}
list_for_each_entry_safe(patch, p, &info->patches, list) {
kp_vtab(patch)->destroy(patch);
list_del(&patch->list);
kfree(patch->data);
kfree(patch);
}
oases_patch_addr_free(&info->addresses);
if (info->code_base) {
vfree(info->code_base);
}
if (info->plog)
kfree(info->plog);
kfree(info);
}
int oases_op_patch(struct oases_patch_file *pfile)
{
int ret;
struct oases_attack_log *plog;
struct oases_patch_info *info = NULL;
int (*code_entry)(struct oases_patch_info *info) = NULL;
void (*init)(void) = NULL;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
oases_error("kzalloc info fail\n");
return -ENOMEM;
}
strncpy(info->id, pfile->pheader->id, PATCH_ID_LEN - 1);
info->version = pfile->pheader->patch_version;
info->status = STATUS_DISABLED;
INIT_LIST_HEAD(&info->patches);
plog = kzalloc(sizeof(struct oases_attack_log) * OASES_LOG_NODE_MAX, GFP_KERNEL);
if (!plog) {
oases_error("kzalloc log fail\n");
ret = -ENOMEM;
goto fail_alloc_log;;
}
plog->uid = OASES_INVALID_UID;
info->log_index = 1;
info->plog = plog;
spin_lock_init(&info->log_lock);
ret = oases_build_code(info, pfile);
if (ret < 0) {
oases_error("oases_build_code fail\n");
goto fail_build_code;
}
code_entry = info->code_entry;
ret = (*code_entry)(info);
oases_debug("code_entry ret=%d\n", ret);
if (ret != OASES_PATCH_SUCCESS) {
if (ret > 0) {
/* OASES_PATCH_FAILURE here */
ret = -EINVAL;
}
goto fail_entry;
}
oases_sysfs_init_patch(info);
ret = oases_sysfs_add_patch(info);
if (ret < 0) {
oases_error("oases_sysfs_add_patch fail\n");
goto fail_sysfs;
}
info->attached = 1;
list_add_tail(&info->list, &patchinfo_list);
init = info->cbs.init;
if (init)
init();
return 0;
fail_sysfs:
oases_sysfs_del_patch(info);
return ret;
/* oases_register_patch() may succeed but entry() fail */
fail_entry:
fail_build_code:
fail_alloc_log:
oases_free_patch(info);
return ret;
}
static int op_unpatch(struct oases_patch_info *info)
{
int ret;
void (*exit)(void) = NULL;
ret = oases_remove_patch(info);
if (ret)
return ret;
if (info->attached) {
list_del(&info->list);
info->attached = 0;
}
exit = info->cbs.exit;
if (exit)
exit();
oases_sysfs_del_patch(info);
return 0;
}
int oases_op_unpatch(struct oases_unpatch *p)
{
int ret;
struct oases_patch_info *pos;
struct oases_patch_info *n;
struct oases_unpatch oup;
memset(&oup, 0, sizeof(struct oases_unpatch));
ret = copy_from_user(&oup, p, sizeof(*p));
if (ret)
return -EFAULT;
oup.id[PATCH_ID_LEN -1] = '\0';
list_for_each_entry_safe(pos, n, &patchinfo_list, list) {
if (strncmp(oup.id, pos->id, PATCH_ID_LEN - 1) == 0) {
if (pos->status == STATUS_ENABLED) {
oases_error("error: patch enabled %s\n", pos->id);
return -EINVAL;
}
return op_unpatch(pos);
}
}
oases_error("warning: no patch: %s\n", oup.id);
return -ENOENT;
}
static void increase_attack_count(struct oases_patch_info *ctx, long uid)
{
int i;
struct oases_attack_log *entry;
/* node 0 belongs to INVALID_UID */
if (oases_uid_eq(uid, OASES_INVALID_UID)) {
entry = ctx->plog;
entry->count++;
entry->end_time = get_seconds();
if (!entry->start_time)
entry->start_time = entry->end_time;
return;
}
for (i = 1; i < ctx->log_index; i++) {
entry = ctx->plog + i;
if (oases_uid_eq(entry->uid, uid)) {
entry->count++;
entry->end_time = get_seconds();
return;
}
}
/* new uid */
entry = ctx->plog + ctx->log_index;
entry->uid = uid;
entry->count = 1;
entry->start_time = get_seconds();
entry->end_time = entry->start_time;
ctx->log_index++;
}
void oases_attack_logger(struct oases_patch_info *ctx)
{
long uid = OASES_INVALID_UID;
unsigned long flags;
spin_lock_irqsave(&ctx->log_lock, flags);
if (ctx->log_index < OASES_LOG_NODE_MAX)
uid = sys_getuid();
increase_attack_count(ctx, uid);
spin_unlock_irqrestore(&ctx->log_lock, flags);
}