You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

441 lines
11 KiB

/*
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/highmem.h>
#include <linux/kernel.h>
#include <linux/page-isolation.h>
#include <linux/workqueue.h>
#include <trace/hooks/mz.h>
#include "mz_internal.h"
#include "mz_log.h"
#include "mz_page.h"
static DEFINE_MUTEX(pid_list_lock);
struct mztarget_t mz_pt_list[PRLIMIT];
struct mutex crypto_list_lock;
struct mutex page_list_lock;
uint64_t *addr_list;
static int addr_list_count;
int addr_list_count_max;
#ifndef CONFIG_SEC_KUNIT
static bool is_mz_target(pid_t tgid);
static bool is_mz_all_zero_target(pid_t tgid);
static MzResult mz_add_new_target(pid_t tgid);
static MzResult remove_target_from_all_list(pid_t tgid);
static struct task_struct *findts(pid_t tgid);
static void compact_addr_list(void);
static void update_list(void);
#endif /* CONFIG_SEC_KUNIT */
static void unload_trusted_app_wq(struct work_struct *work);
static DECLARE_WORK(mz_ta_unload_work, unload_trusted_app_wq);
static int add_count;
MzResult mz_exit(void)
{
pid_t cur_tgid = current->pid;
if (!is_mz_target(cur_tgid)) {
mz_pt_list[cur_tgid].is_ta_fail_target = false;
return MZ_SUCCESS;
}
MZ_LOG(err_level_debug, "[MZ DEBUG] %s %d %d\n", __func__, current->tgid, current->pid);
mz_pt_list[cur_tgid].target = false;
remove_target_from_all_list(cur_tgid);
#ifdef MZ_TA
unload_trusted_app();
#endif /* MZ_TA */
return MZ_SUCCESS;
}
static void vh_mz_exit(void *data, struct task_struct *p)
{
pid_t cur_tgid = p->pid;
bool ret;
if (!is_mz_target(cur_tgid)) {
mz_pt_list[cur_tgid].is_ta_fail_target = false;
return;
}
MZ_LOG(err_level_debug, "[MZ DEBUG] %s %d %d\n", __func__, p->tgid, p->pid);
mz_pt_list[cur_tgid].target = false;
remove_target_from_all_list(cur_tgid);
#ifdef MZ_TA
#ifdef CONFIG_MZ_USE_QSEECOM
ret = queue_work(system_long_wq, &mz_ta_unload_work);
if (!ret)
MZ_LOG(err_level_error, "%s unload_trusted_app workqueue fail %d\n", __func__, ret);
#else
unload_trusted_app();
#endif
#endif /* MZ_TA */
}
static void unload_trusted_app_wq(struct work_struct *work)
{
unload_trusted_app();
}
__visible_for_testing bool is_mz_target(pid_t tgid)
{
if (tgid >= PRLIMIT || tgid <= 0)
return false;
if (mz_pt_list[tgid].target)
return true;
return false;
}
__visible_for_testing bool is_mz_all_zero_target(pid_t tgid)
{
if (tgid >= PRLIMIT || tgid <= 0)
return false;
if (mz_pt_list[tgid].is_ta_fail_target)
return true;
return false;
}
MzResult mz_all_zero_set(pid_t tgid)
{
MzResult mz_ret;
mz_ret = mz_add_new_target(tgid);
if (mz_ret != MZ_SUCCESS) {
MZ_LOG(err_level_error, "MZ %s new target all zero set fail\n", __func__);
return mz_ret;
}
mz_pt_list[tgid].is_ta_fail_target = true;
return mz_ret;
}
static void compact_addr_list(void)
{
uint64_t i = 1;
struct pid_node_t *pid_node, *tp;
struct pfn_node_encrypted_t *cur_encrypted, *t_e;
#ifdef MZ_DEBUG
for (i = 3 ; i < addr_list_count ; i += 2)
MZ_LOG(err_level_debug, "[MZ DEBUG] %s start %d %llx %d\n",
__func__, addr_list[0], addr_list[i], addr_list[i+1]);
#endif /* MZ_DEBUG */
for (i = 3 ; i < addr_list[0] ; i += 2) {
if (addr_list[i] == 0) {
addr_list[i] = addr_list[addr_list_count - 1];
addr_list[i + 1] = addr_list[addr_list_count];
mutex_lock(&pid_list_lock);
list_for_each_entry_safe(pid_node, tp, &pid_list, list) {
mutex_lock(&crypto_list_lock);
list_for_each_entry_safe(cur_encrypted, t_e,
&(mz_pt_list[pid_node->tgid].mz_list_head_crypto), list) {
if (cur_encrypted->pa_index == addr_list_count - 1)
cur_encrypted->pa_index = i;
}
mutex_unlock(&crypto_list_lock);
}
mutex_unlock(&pid_list_lock);
memset(&(addr_list[addr_list_count - 1]), MZ_PAGE_POISON, sizeof(uint64_t) * 2);
addr_list_count -= 2;
i -= 2;
}
}
addr_list[0] = addr_list_count;
#ifdef MZ_DEBUG
for (i = 3 ; i < addr_list_count ; i += 2)
MZ_LOG(err_level_debug, "[MZ DEBUG] %s end %d %llx %d\n",
__func__, addr_list[0], addr_list[i], addr_list[i+1]);
#endif /* MZ_DEBUG */
}
MzResult remove_target_from_all_list(pid_t tgid)
{
struct pid_node_t *curp, *tp;
struct pfn_node_encrypted_t *cur_encrypted, *t_e;
page_node *cur_page, *t_p;
if (tgid >= PRLIMIT || tgid <= 0)
return MZ_INVALID_INPUT_ERROR;
mutex_lock(&crypto_list_lock);
list_for_each_entry_safe(cur_encrypted, t_e, &(mz_pt_list[tgid].mz_list_head_crypto), list) {
list_del(&(cur_encrypted->list));
addr_list[cur_encrypted->pa_index] = 0;
kfree(cur_encrypted);
cur_encrypted = NULL;
}
mutex_unlock(&crypto_list_lock);
mutex_lock(&page_list_lock);
list_for_each_entry_safe(cur_page, t_p, &(mz_pt_list[tgid].mz_list_head_page), list) {
list_del(&(cur_page->list));
kfree(cur_page->mz_page);
kfree(cur_page);
cur_page = NULL;
}
mutex_unlock(&page_list_lock);
mutex_lock( &pid_list_lock );
list_for_each_entry_safe(curp, tp, &pid_list, list) {
if (curp->tgid == tgid) {
list_del(&(curp->list));
kfree(curp);
curp = NULL;
MZ_LOG(err_level_debug, "%s %d\n", __func__, tgid);
}
}
mutex_unlock( &pid_list_lock );
compact_addr_list();
return MZ_SUCCESS;
}
__visible_for_testing MzResult mz_add_new_target(pid_t tgid)
{
struct pid_node_t *pid_node;
MzResult mz_ret = MZ_SUCCESS;
if (tgid >= PRLIMIT || tgid <= 0)
return MZ_INVALID_INPUT_ERROR;
if (is_mz_target(tgid)) {
return mz_ret;
}
pid_node = kmalloc(sizeof(*pid_node), GFP_KERNEL);
#ifdef CONFIG_SEC_KUNIT
if (tgid == MALLOC_FAIL_PID && pid_node) {
kfree(pid_node);
pid_node = NULL;
}
#endif /* CONFIG_SEC_KUNIT */
if (!pid_node) {
MZ_LOG(err_level_error, "%s pid_node kmalloc fail\n", __func__);
return MZ_MALLOC_ERROR;
}
mz_pt_list[tgid].target = true;
mz_pt_list[tgid].is_ta_fail_target = false;
pid_node->tgid = tgid;
INIT_LIST_HEAD(&(pid_node->list));
mutex_lock( &pid_list_lock );
list_add(&(pid_node->list), &pid_list);
mutex_unlock( &pid_list_lock );
MZ_LOG(err_level_debug, "%s %d\n", __func__, tgid);
#ifdef MZ_TA
mz_ret = load_trusted_app();
if (mz_ret != MZ_SUCCESS) {
MZ_LOG(err_level_info, "%s ta fail %d, free all memory\n", __func__, mz_ret);
mz_pt_list[tgid].is_ta_fail_target = true;
}
#endif /* MZ_TA */
if (!isaddrset())
set_mz_mem();
return mz_ret;
}
static void update_list(void)
{
uint64_t *new_addr_list;
if (addr_list_count != addr_list_count_max - 2)
return;
addr_list_count_max *= 2;
new_addr_list = kmalloc_array(addr_list_count_max, sizeof(uint64_t), GFP_KERNEL);
if (!new_addr_list) {
MZ_LOG(err_level_error, "%s new_addr_list kmalloc_array fail\n", __func__);
return;
}
memcpy(new_addr_list, addr_list, (sizeof(uint64_t) * addr_list_count) + sizeof(uint64_t));
memset(addr_list, MZ_PAGE_POISON, (sizeof(uint64_t) * addr_list_count) + sizeof(uint64_t));
kfree(addr_list);
addr_list = new_addr_list;
set_mz_mem();
}
MzResult mz_add_target_pfn(pid_t tgid, unsigned long pfn, unsigned long offset,
unsigned long len, unsigned long va, uint8_t __user *buf)
{
struct pfn_node_encrypted_t *cur_encrypted;
MzResult mz_ret = MZ_SUCCESS;
struct page *target_page = pfn_to_page(pfn);
uint64_t pa;
uint64_t plain_pa_offset;
unsigned long new_pfn = 0;
#ifdef MZ_TA
uint64_t cipher_pa_offset;
uint8_t *plain_pa_offset_8, cipher_pa_offset_8[8];
#endif
if (pfn <= 0)
return MZ_INVALID_INPUT_ERROR;
mz_ret = mz_add_new_target(tgid);
if (mz_ret != MZ_SUCCESS) {
return mz_ret;
}
if (is_mz_all_zero_target(tgid)) {
return mz_ret;
}
cur_encrypted = kmalloc(sizeof(*cur_encrypted), GFP_ATOMIC);
#ifdef CONFIG_SEC_KUNIT
if (tgid == MALLOC_FAIL_CUR && cur_encrypted) {
kfree(cur_encrypted);
cur_encrypted = NULL;
}
#endif /* CONFIG_SEC_KUNIT */
if (!cur_encrypted) {
MZ_LOG(err_level_error, "%s cur_encrypted kmalloc fail\n", __func__);
return MZ_MALLOC_ERROR;
}
#ifdef MZ_DEBUG
pa = page_to_phys(target_page);
MZ_LOG(err_level_debug, "%s original addr before migrate/gup %llx\n", __func__, pa + offset);
#endif
#ifndef CONFIG_SEC_KUNIT
//Migration in case of CMA and pin
mz_ret = mz_migrate_and_pin(target_page, va, buf, &new_pfn, tgid);
if (mz_ret != MZ_SUCCESS)
goto free_alloc;
#endif /* CONFIG_SEC_KUNIT */
if (new_pfn != 0)
target_page = pfn_to_page(new_pfn);
pa = page_to_phys(target_page);
MZ_LOG(err_level_debug, "%s original addr after migrate/gup %llx\n", __func__, pa + offset);
update_list();
plain_pa_offset = pa + offset;
#ifdef MZ_TA
plain_pa_offset_8 = (uint8_t *)&plain_pa_offset;
mz_ret = mz_wb_encrypt(plain_pa_offset_8, cipher_pa_offset_8);
if (mz_ret != MZ_SUCCESS)
goto free_alloc;
cipher_pa_offset = *(uint64_t *)cipher_pa_offset_8;
addr_list[++addr_list_count] = cipher_pa_offset;
MZ_LOG(err_level_debug, "%s cipher (0x%llx)\n", __func__, (unsigned long long)cipher_pa_offset);
#else
addr_list[++addr_list_count] = plain_pa_offset;
#endif
addr_list[++addr_list_count] = len;
addr_list[0] = addr_list_count;
cur_encrypted->pa_index = addr_list_count - 1;
INIT_LIST_HEAD(&(cur_encrypted->list));
mutex_lock(&crypto_list_lock);
list_add_tail(&(cur_encrypted->list), &(mz_pt_list[tgid].mz_list_head_crypto));
mutex_unlock(&crypto_list_lock);
MZ_LOG(err_level_debug, "%s %d %d %d (0x%llx) (0x%llx) %d\n",
__func__, tgid, ++add_count, addr_list_count, (unsigned long long)pa, offset, len);
return mz_ret;
free_alloc:
kfree(cur_encrypted);
return mz_ret;
}
MzResult mzinit(void)
{
int i;
for (i = 0 ; i < PRLIMIT ; i++) {
mz_pt_list[i].target = false;
mz_pt_list[i].is_ta_fail_target = false;
INIT_LIST_HEAD(&(mz_pt_list[i].mz_list_head_crypto));
INIT_LIST_HEAD(&(mz_pt_list[i].mz_list_head_page));
mutex_init(&crypto_list_lock);
mutex_init(&page_list_lock);
}
add_count = 0;
addr_list_count = 2;
addr_list_count_max = 0;
if (mz_addr_init())
set_mz_mem();
register_trace_android_vh_mz_exit(vh_mz_exit, NULL);
return MZ_SUCCESS;
}
//Util
MzResult mz_kget_process_name(pid_t tgid, char* name)
{
MzResult result = MZ_SUCCESS;
int cmdline_size;
struct task_struct *task;
if (tgid >= PRLIMIT || tgid <= 0)
return MZ_INVALID_INPUT_ERROR;
if (name == NULL)
return MZ_INVALID_INPUT_ERROR;
task = findts(tgid);
if (!task)
return MZ_GET_TS_ERROR;
if (task->mm == NULL)
return MZ_GET_TS_ERROR;
cmdline_size = get_cmdline(task, name, MAX_PROCESS_NAME);
if (cmdline_size == 0)
result = MZ_PROC_NAME_GET_ERROR;
name[cmdline_size] = 0;
return result;
}
__visible_for_testing struct task_struct *findts(pid_t tgid)
{
struct task_struct *task;
if (tgid >= PRLIMIT || tgid <= 0)
return NULL;
task = pid_task(find_vpid(tgid), PIDTYPE_PID);
return task;
}
MzResult (*load_trusted_app)(void) = NULL;
EXPORT_SYMBOL(load_trusted_app);
void (*unload_trusted_app)(void) = NULL;
EXPORT_SYMBOL(unload_trusted_app);