1419b0af8Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2419b0af8Sopenharmony_ci/* 3419b0af8Sopenharmony_ci * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 4419b0af8Sopenharmony_ci */ 5419b0af8Sopenharmony_ci#include <linux/string.h> 6419b0af8Sopenharmony_ci#include <linux/fcntl.h> 7419b0af8Sopenharmony_ci#include <linux/fs.h> 8419b0af8Sopenharmony_ci#include <linux/slab.h> 9419b0af8Sopenharmony_ci#include <linux/rwlock_types.h> 10419b0af8Sopenharmony_ci#include <linux/rwlock.h> 11419b0af8Sopenharmony_ci#include <linux/init.h> 12419b0af8Sopenharmony_ci#include <linux/moduleparam.h> 13419b0af8Sopenharmony_ci#include <linux/device-mapper.h> 14419b0af8Sopenharmony_ci#include <linux/kdev_t.h> 15419b0af8Sopenharmony_ci#include <linux/namei.h> 16419b0af8Sopenharmony_ci#include <linux/sched.h> 17419b0af8Sopenharmony_ci#include "mount.h" 18419b0af8Sopenharmony_ci#include "internal.h" 19419b0af8Sopenharmony_ci#include "exec_signature_info.h" 20419b0af8Sopenharmony_ci#include "xpm_report.h" 21419b0af8Sopenharmony_ci#include "xpm_log.h" 22419b0af8Sopenharmony_ci#include "code_sign_elf.h" 23419b0af8Sopenharmony_ci 24419b0af8Sopenharmony_ci#define VERITY_NODE_CACHE_LIMITS 10000 25419b0af8Sopenharmony_ci#define VERITY_NODE_CACHE_RECYCLE_NUM 200 26419b0af8Sopenharmony_ci 27419b0af8Sopenharmony_cistatic DEFINE_RWLOCK(dm_verity_tree_lock); 28419b0af8Sopenharmony_cistatic struct rb_root dm_verity_tree = RB_ROOT; 29419b0af8Sopenharmony_cistatic int dm_verity_node_count; 30419b0af8Sopenharmony_cistatic DEFINE_RWLOCK(fs_verity_tree_lock); 31419b0af8Sopenharmony_cistatic struct rb_root fs_verity_tree = RB_ROOT; 32419b0af8Sopenharmony_cistatic int fs_verity_node_count; 33419b0af8Sopenharmony_ci 34419b0af8Sopenharmony_cistruct verity_info { 35419b0af8Sopenharmony_ci struct rb_root *root; 36419b0af8Sopenharmony_ci rwlock_t *lock; 37419b0af8Sopenharmony_ci int *node_count; 38419b0af8Sopenharmony_ci}; 39419b0af8Sopenharmony_ci 40419b0af8Sopenharmony_cistatic bool dm_verity_enable_check; 41419b0af8Sopenharmony_ci 42419b0af8Sopenharmony_ci#define DM_PARTITION_PATH_MAX 30 43419b0af8Sopenharmony_ci#define DM_VERITY_INVALID_DEV ((dev_t)-1) 44419b0af8Sopenharmony_ci 45419b0af8Sopenharmony_cistruct dm_partition { 46419b0af8Sopenharmony_ci char path[DM_PARTITION_PATH_MAX]; 47419b0af8Sopenharmony_ci int len; 48419b0af8Sopenharmony_ci dev_t s_dev; 49419b0af8Sopenharmony_ci}; 50419b0af8Sopenharmony_ci 51419b0af8Sopenharmony_cistatic struct dm_partition dm_partition_table[] = { 52419b0af8Sopenharmony_ci { .path = "/", .len = 1, .s_dev = DM_VERITY_INVALID_DEV }, 53419b0af8Sopenharmony_ci { .path = "/cust/", .len = 6, .s_dev = DM_VERITY_INVALID_DEV }, 54419b0af8Sopenharmony_ci { .path = "/system/", .len = 8, .s_dev = DM_VERITY_INVALID_DEV }, 55419b0af8Sopenharmony_ci { .path = "/vendor/", .len = 8, .s_dev = DM_VERITY_INVALID_DEV }, 56419b0af8Sopenharmony_ci { .path = "/version/", .len = 9, .s_dev = DM_VERITY_INVALID_DEV }, 57419b0af8Sopenharmony_ci { .path = "/preload/", .len = 9, .s_dev = DM_VERITY_INVALID_DEV }, 58419b0af8Sopenharmony_ci { .path = "/sys_prod/", .len = 10, .s_dev = DM_VERITY_INVALID_DEV }, 59419b0af8Sopenharmony_ci { .path = "/chip_prod/", .len = 11, .s_dev = DM_VERITY_INVALID_DEV }, 60419b0af8Sopenharmony_ci { .path = "/module_updata/", .len = 15, .s_dev = DM_VERITY_INVALID_DEV }, 61419b0af8Sopenharmony_ci { .path = "/vendor/modem/modem_vendor/", .len = 27, .s_dev = DM_VERITY_INVALID_DEV }, 62419b0af8Sopenharmony_ci { .path = "/vendor/modem/modem_driver/", .len = 27, .s_dev = DM_VERITY_INVALID_DEV }, 63419b0af8Sopenharmony_ci { .path = "/misc/", .len = 6, .s_dev = DM_VERITY_INVALID_DEV }, 64419b0af8Sopenharmony_ci}; 65419b0af8Sopenharmony_ci 66419b0af8Sopenharmony_cistatic struct path root_path; 67419b0af8Sopenharmony_ci 68419b0af8Sopenharmony_cistatic dev_t get_file_dev(struct file *file) 69419b0af8Sopenharmony_ci{ 70419b0af8Sopenharmony_ci struct super_block *sb; 71419b0af8Sopenharmony_ci struct mount *mnt; 72419b0af8Sopenharmony_ci 73419b0af8Sopenharmony_ci if (file->f_path.mnt == NULL) 74419b0af8Sopenharmony_ci return DM_VERITY_INVALID_DEV; 75419b0af8Sopenharmony_ci 76419b0af8Sopenharmony_ci mnt = container_of(file->f_path.mnt, struct mount, mnt); 77419b0af8Sopenharmony_ci sb = mnt->mnt.mnt_sb; 78419b0af8Sopenharmony_ci if (sb == NULL) 79419b0af8Sopenharmony_ci return DM_VERITY_INVALID_DEV; 80419b0af8Sopenharmony_ci 81419b0af8Sopenharmony_ci return sb->s_dev; 82419b0af8Sopenharmony_ci} 83419b0af8Sopenharmony_ci 84419b0af8Sopenharmony_cistatic dev_t get_partition_dev_form_mnt(const struct vfsmount *mnt) 85419b0af8Sopenharmony_ci{ 86419b0af8Sopenharmony_ci if (IS_ERR_OR_NULL(mnt) || IS_ERR_OR_NULL(mnt->mnt_sb)) 87419b0af8Sopenharmony_ci return DM_VERITY_INVALID_DEV; 88419b0af8Sopenharmony_ci return mnt->mnt_sb->s_dev; 89419b0af8Sopenharmony_ci} 90419b0af8Sopenharmony_ci 91419b0af8Sopenharmony_cistatic dev_t get_root_partition_dev(struct path *root_path) 92419b0af8Sopenharmony_ci{ 93419b0af8Sopenharmony_ci int ret; 94419b0af8Sopenharmony_ci struct path path; 95419b0af8Sopenharmony_ci struct path *mount_path = &path; 96419b0af8Sopenharmony_ci dev_t s_dev; 97419b0af8Sopenharmony_ci 98419b0af8Sopenharmony_ci if (root_path != NULL) 99419b0af8Sopenharmony_ci mount_path = root_path; 100419b0af8Sopenharmony_ci ret = kern_path("/", LOOKUP_DIRECTORY, mount_path); 101419b0af8Sopenharmony_ci if (ret) { 102419b0af8Sopenharmony_ci xpm_log_error("get / path failed"); 103419b0af8Sopenharmony_ci return DM_VERITY_INVALID_DEV; 104419b0af8Sopenharmony_ci } 105419b0af8Sopenharmony_ci s_dev = get_partition_dev_form_mnt(mount_path->mnt); 106419b0af8Sopenharmony_ci if (s_dev == DM_VERITY_INVALID_DEV || root_path == NULL) 107419b0af8Sopenharmony_ci path_put(mount_path); 108419b0af8Sopenharmony_ci xpm_log_info("get / dev=%u:%u success", s_dev, MINOR(s_dev)); 109419b0af8Sopenharmony_ci return s_dev; 110419b0af8Sopenharmony_ci} 111419b0af8Sopenharmony_ci 112419b0af8Sopenharmony_cistatic dev_t get_dm_verity_partition_dev(const char *dir, struct path *root_path) 113419b0af8Sopenharmony_ci{ 114419b0af8Sopenharmony_ci int ret; 115419b0af8Sopenharmony_ci struct path path; 116419b0af8Sopenharmony_ci dev_t s_dev; 117419b0af8Sopenharmony_ci 118419b0af8Sopenharmony_ci ret = vfs_path_lookup(root_path->dentry, root_path->mnt, dir, LOOKUP_DIRECTORY, &path); 119419b0af8Sopenharmony_ci if (ret) { 120419b0af8Sopenharmony_ci xpm_log_error("get %s path failed", dir); 121419b0af8Sopenharmony_ci return DM_VERITY_INVALID_DEV; 122419b0af8Sopenharmony_ci } 123419b0af8Sopenharmony_ci s_dev = get_partition_dev_form_mnt(path.mnt); 124419b0af8Sopenharmony_ci path_put(&path); 125419b0af8Sopenharmony_ci xpm_log_info("get %s dev=%u:%u success", dir, s_dev, MINOR(s_dev)); 126419b0af8Sopenharmony_ci return s_dev; 127419b0af8Sopenharmony_ci} 128419b0af8Sopenharmony_ci 129419b0af8Sopenharmony_cistatic bool find_partition_dev(struct file *file, dev_t s_dev) 130419b0af8Sopenharmony_ci{ 131419b0af8Sopenharmony_ci char *full_path = NULL; 132419b0af8Sopenharmony_ci char path[PATH_MAX] = {0}; 133419b0af8Sopenharmony_ci struct dm_partition *dm_path; 134419b0af8Sopenharmony_ci int i; 135419b0af8Sopenharmony_ci 136419b0af8Sopenharmony_ci for (i = 1; i < sizeof(dm_partition_table) / sizeof(struct dm_partition); i++) { 137419b0af8Sopenharmony_ci dm_path = &dm_partition_table[i]; 138419b0af8Sopenharmony_ci if (dm_path->s_dev != DM_VERITY_INVALID_DEV) 139419b0af8Sopenharmony_ci continue; 140419b0af8Sopenharmony_ci 141419b0af8Sopenharmony_ci if (full_path == NULL) { 142419b0af8Sopenharmony_ci full_path = file_path(file, path, PATH_MAX-1); 143419b0af8Sopenharmony_ci if (IS_ERR(full_path)) 144419b0af8Sopenharmony_ci return false; 145419b0af8Sopenharmony_ci } 146419b0af8Sopenharmony_ci if (strncmp(dm_path->path, full_path, dm_path->len) != 0) 147419b0af8Sopenharmony_ci continue; 148419b0af8Sopenharmony_ci 149419b0af8Sopenharmony_ci dm_path->s_dev = get_dm_verity_partition_dev(dm_path->path, &root_path); 150419b0af8Sopenharmony_ci if (dm_path->s_dev == DM_VERITY_INVALID_DEV) 151419b0af8Sopenharmony_ci return false; 152419b0af8Sopenharmony_ci if (dm_path->s_dev == s_dev) 153419b0af8Sopenharmony_ci return true; 154419b0af8Sopenharmony_ci return false; 155419b0af8Sopenharmony_ci } 156419b0af8Sopenharmony_ci 157419b0af8Sopenharmony_ci return false; 158419b0af8Sopenharmony_ci} 159419b0af8Sopenharmony_ci 160419b0af8Sopenharmony_cistatic bool dm_verity_check_for_path(struct file *file) 161419b0af8Sopenharmony_ci{ 162419b0af8Sopenharmony_ci static int system_startup_stage; 163419b0af8Sopenharmony_ci struct dm_partition *dm_path; 164419b0af8Sopenharmony_ci dev_t s_dev, root_dev; 165419b0af8Sopenharmony_ci int i; 166419b0af8Sopenharmony_ci 167419b0af8Sopenharmony_ci s_dev = get_file_dev(file); 168419b0af8Sopenharmony_ci for (i = 0; i < sizeof(dm_partition_table) / sizeof(struct dm_partition); i++) { 169419b0af8Sopenharmony_ci dm_path = &dm_partition_table[i]; 170419b0af8Sopenharmony_ci if (dm_path->s_dev == s_dev) 171419b0af8Sopenharmony_ci return true; 172419b0af8Sopenharmony_ci } 173419b0af8Sopenharmony_ci 174419b0af8Sopenharmony_ci if (system_startup_stage) 175419b0af8Sopenharmony_ci return find_partition_dev(file, s_dev); 176419b0af8Sopenharmony_ci 177419b0af8Sopenharmony_ci if (task_pid_nr(current) == 1) { 178419b0af8Sopenharmony_ci root_dev = get_root_partition_dev(NULL); 179419b0af8Sopenharmony_ci if (root_dev != dm_partition_table[0].s_dev) { 180419b0af8Sopenharmony_ci path_put(&root_path); 181419b0af8Sopenharmony_ci dm_partition_table[0].s_dev = get_root_partition_dev(&root_path); 182419b0af8Sopenharmony_ci for (i = 1; i < sizeof(dm_partition_table) / sizeof(struct dm_partition); i++) 183419b0af8Sopenharmony_ci dm_partition_table[i].s_dev = DM_VERITY_INVALID_DEV; 184419b0af8Sopenharmony_ci system_startup_stage = 1; 185419b0af8Sopenharmony_ci } 186419b0af8Sopenharmony_ci } 187419b0af8Sopenharmony_ci return find_partition_dev(file, s_dev); 188419b0af8Sopenharmony_ci} 189419b0af8Sopenharmony_ci 190419b0af8Sopenharmony_ci#ifdef CONFIG_DM_VERITY 191419b0af8Sopenharmony_ci#define HVB_CMDLINE_VB_STATE "ohos.boot.hvb.enable" 192419b0af8Sopenharmony_cistatic bool dm_verity_enable; 193419b0af8Sopenharmony_ci 194419b0af8Sopenharmony_cistatic int hvb_boot_param_cb(char *param, char *val, 195419b0af8Sopenharmony_ci const char *unused, void *arg) 196419b0af8Sopenharmony_ci{ 197419b0af8Sopenharmony_ci if (param == NULL || val == NULL) 198419b0af8Sopenharmony_ci return 0; 199419b0af8Sopenharmony_ci 200419b0af8Sopenharmony_ci if (strcmp(param, HVB_CMDLINE_VB_STATE) != 0) 201419b0af8Sopenharmony_ci return 0; 202419b0af8Sopenharmony_ci 203419b0af8Sopenharmony_ci if (strcmp(val, "true") == 0 || strcmp(val, "green") == 0) 204419b0af8Sopenharmony_ci dm_verity_enable = true; 205419b0af8Sopenharmony_ci 206419b0af8Sopenharmony_ci return 0; 207419b0af8Sopenharmony_ci} 208419b0af8Sopenharmony_ci 209419b0af8Sopenharmony_cistatic bool dm_verity_is_enable(void) 210419b0af8Sopenharmony_ci{ 211419b0af8Sopenharmony_ci char *cmdline; 212419b0af8Sopenharmony_ci 213419b0af8Sopenharmony_ci if (dm_verity_enable || dm_verity_enable_check) 214419b0af8Sopenharmony_ci return dm_verity_enable; 215419b0af8Sopenharmony_ci 216419b0af8Sopenharmony_ci cmdline = kstrdup(saved_command_line, GFP_KERNEL); 217419b0af8Sopenharmony_ci if (cmdline == NULL) 218419b0af8Sopenharmony_ci return false; 219419b0af8Sopenharmony_ci 220419b0af8Sopenharmony_ci parse_args("hvb.enable params", cmdline, NULL, 221419b0af8Sopenharmony_ci 0, 0, 0, NULL, &hvb_boot_param_cb); 222419b0af8Sopenharmony_ci kfree(cmdline); 223419b0af8Sopenharmony_ci dm_verity_enable_check = true; 224419b0af8Sopenharmony_ci if (!dm_verity_enable) { 225419b0af8Sopenharmony_ci dm_partition_table[0].s_dev = get_root_partition_dev(&root_path); 226419b0af8Sopenharmony_ci report_init_event(DM_DISABLE); 227419b0af8Sopenharmony_ci } 228419b0af8Sopenharmony_ci return dm_verity_enable; 229419b0af8Sopenharmony_ci} 230419b0af8Sopenharmony_ci 231419b0af8Sopenharmony_cistatic bool dm_verity_check_for_mnt(struct file *file) 232419b0af8Sopenharmony_ci{ 233419b0af8Sopenharmony_ci struct mapped_device *device; 234419b0af8Sopenharmony_ci 235419b0af8Sopenharmony_ci device = dm_get_md(get_file_dev(file)); 236419b0af8Sopenharmony_ci if (device == NULL) 237419b0af8Sopenharmony_ci return false; 238419b0af8Sopenharmony_ci 239419b0af8Sopenharmony_ci dm_put(device); 240419b0af8Sopenharmony_ci return true; 241419b0af8Sopenharmony_ci} 242419b0af8Sopenharmony_ci#endif 243419b0af8Sopenharmony_ci 244419b0af8Sopenharmony_cistatic bool is_dm_verity(struct file *file) 245419b0af8Sopenharmony_ci{ 246419b0af8Sopenharmony_ci#ifdef CONFIG_DM_VERITY 247419b0af8Sopenharmony_ci if (dm_verity_is_enable()) 248419b0af8Sopenharmony_ci return dm_verity_check_for_mnt(file); 249419b0af8Sopenharmony_ci#endif 250419b0af8Sopenharmony_ci 251419b0af8Sopenharmony_ci if (!dm_verity_enable_check) { 252419b0af8Sopenharmony_ci dm_partition_table[0].s_dev = get_root_partition_dev(&root_path); 253419b0af8Sopenharmony_ci dm_verity_enable_check = true; 254419b0af8Sopenharmony_ci report_init_event(DM_DISABLE); 255419b0af8Sopenharmony_ci } 256419b0af8Sopenharmony_ci return dm_verity_check_for_path(file); 257419b0af8Sopenharmony_ci} 258419b0af8Sopenharmony_ci 259419b0af8Sopenharmony_ci#ifdef CONFIG_FS_VERITY 260419b0af8Sopenharmony_cistatic bool is_fs_verity(struct file *file) 261419b0af8Sopenharmony_ci{ 262419b0af8Sopenharmony_ci struct inode *file_node; 263419b0af8Sopenharmony_ci 264419b0af8Sopenharmony_ci file_node = file_inode(file); 265419b0af8Sopenharmony_ci if (file_node == NULL) 266419b0af8Sopenharmony_ci return false; 267419b0af8Sopenharmony_ci 268419b0af8Sopenharmony_ci if (file_node->i_verity_info == NULL) 269419b0af8Sopenharmony_ci return false; 270419b0af8Sopenharmony_ci 271419b0af8Sopenharmony_ci return true; 272419b0af8Sopenharmony_ci} 273419b0af8Sopenharmony_ci#endif 274419b0af8Sopenharmony_ci 275419b0af8Sopenharmony_cistatic int check_exec_file_is_verity(struct file *file, bool is_exec) 276419b0af8Sopenharmony_ci{ 277419b0af8Sopenharmony_ci#ifdef CONFIG_FS_VERITY 278419b0af8Sopenharmony_ci if (is_fs_verity(file)) 279419b0af8Sopenharmony_ci return FILE_SIGNATURE_FS_VERITY; 280419b0af8Sopenharmony_ci#endif 281419b0af8Sopenharmony_ci 282419b0af8Sopenharmony_ci if (is_dm_verity(file)) 283419b0af8Sopenharmony_ci return FILE_SIGNATURE_DM_VERITY; 284419b0af8Sopenharmony_ci 285419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_CODE_SIGN 286419b0af8Sopenharmony_ci if (is_exec && !elf_file_enable_fs_verity(file)) 287419b0af8Sopenharmony_ci return FILE_SIGNATURE_FS_VERITY; 288419b0af8Sopenharmony_ci#endif 289419b0af8Sopenharmony_ci 290419b0af8Sopenharmony_ci return FILE_SIGNATURE_INVALID; 291419b0af8Sopenharmony_ci} 292419b0af8Sopenharmony_ci 293419b0af8Sopenharmony_cistatic struct exec_file_signature_info *rb_search_node(struct rb_root *root, uintptr_t file_inode) 294419b0af8Sopenharmony_ci{ 295419b0af8Sopenharmony_ci struct rb_node *node = root->rb_node; 296419b0af8Sopenharmony_ci struct exec_file_signature_info *file_node; 297419b0af8Sopenharmony_ci 298419b0af8Sopenharmony_ci while (node != NULL) { 299419b0af8Sopenharmony_ci file_node = rb_entry(node, struct exec_file_signature_info, rb_node); 300419b0af8Sopenharmony_ci if (file_inode < file_node->inode) { 301419b0af8Sopenharmony_ci node = file_node->rb_node.rb_left; 302419b0af8Sopenharmony_ci } else if (file_inode > file_node->inode) { 303419b0af8Sopenharmony_ci node = file_node->rb_node.rb_right; 304419b0af8Sopenharmony_ci } else { 305419b0af8Sopenharmony_ci atomic_inc(&file_node->reference); 306419b0af8Sopenharmony_ci return file_node; 307419b0af8Sopenharmony_ci } 308419b0af8Sopenharmony_ci } 309419b0af8Sopenharmony_ci return NULL; 310419b0af8Sopenharmony_ci} 311419b0af8Sopenharmony_ci 312419b0af8Sopenharmony_cistatic struct exec_file_signature_info *rb_add_node(struct rb_root *root, int *node_count, 313419b0af8Sopenharmony_ci struct exec_file_signature_info *node) 314419b0af8Sopenharmony_ci{ 315419b0af8Sopenharmony_ci struct rb_node **p = &root->rb_node; 316419b0af8Sopenharmony_ci struct rb_node *parent = NULL; 317419b0af8Sopenharmony_ci struct exec_file_signature_info *file; 318419b0af8Sopenharmony_ci 319419b0af8Sopenharmony_ci while (*p != NULL) { 320419b0af8Sopenharmony_ci parent = *p; 321419b0af8Sopenharmony_ci file = rb_entry(parent, struct exec_file_signature_info, rb_node); 322419b0af8Sopenharmony_ci if (node->inode < file->inode) { 323419b0af8Sopenharmony_ci p = &(*p)->rb_left; 324419b0af8Sopenharmony_ci } else if (node->inode > file->inode) { 325419b0af8Sopenharmony_ci p = &(*p)->rb_right; 326419b0af8Sopenharmony_ci } else { 327419b0af8Sopenharmony_ci atomic_inc(&file->reference); 328419b0af8Sopenharmony_ci return file; 329419b0af8Sopenharmony_ci } 330419b0af8Sopenharmony_ci } 331419b0af8Sopenharmony_ci 332419b0af8Sopenharmony_ci rb_link_node(&node->rb_node, parent, p); 333419b0af8Sopenharmony_ci rb_insert_color(&node->rb_node, root); 334419b0af8Sopenharmony_ci atomic_inc(&node->reference); 335419b0af8Sopenharmony_ci (*node_count)++; 336419b0af8Sopenharmony_ci return NULL; 337419b0af8Sopenharmony_ci} 338419b0af8Sopenharmony_ci 339419b0af8Sopenharmony_cistatic void rb_erase_node(struct rb_root *root, int *node_count, 340419b0af8Sopenharmony_ci struct exec_file_signature_info *node) 341419b0af8Sopenharmony_ci{ 342419b0af8Sopenharmony_ci rb_erase(&node->rb_node, root); 343419b0af8Sopenharmony_ci (*node_count)--; 344419b0af8Sopenharmony_ci} 345419b0af8Sopenharmony_ci 346419b0af8Sopenharmony_cistatic int find_idle_nodes(struct rb_root *root, uintptr_t *ilde_nodes, int count) 347419b0af8Sopenharmony_ci{ 348419b0af8Sopenharmony_ci int i = 0; 349419b0af8Sopenharmony_ci struct exec_file_signature_info *code_segment; 350419b0af8Sopenharmony_ci struct rb_node *node; 351419b0af8Sopenharmony_ci 352419b0af8Sopenharmony_ci for (node = rb_first(root); node != NULL && i < count; node = rb_next(node)) { 353419b0af8Sopenharmony_ci code_segment = rb_entry(node, struct exec_file_signature_info, rb_node); 354419b0af8Sopenharmony_ci if (atomic_read(&code_segment->reference) > 0) 355419b0af8Sopenharmony_ci continue; 356419b0af8Sopenharmony_ci 357419b0af8Sopenharmony_ci ilde_nodes[i++] = (uintptr_t)code_segment; 358419b0af8Sopenharmony_ci } 359419b0af8Sopenharmony_ci return i; 360419b0af8Sopenharmony_ci} 361419b0af8Sopenharmony_ci 362419b0af8Sopenharmony_cistatic void clear_code_segment_info_cache(struct rb_root *root, int *node_count) 363419b0af8Sopenharmony_ci{ 364419b0af8Sopenharmony_ci struct exec_file_signature_info *code_segment_info; 365419b0af8Sopenharmony_ci uintptr_t *code_segments; 366419b0af8Sopenharmony_ci int i = 0; 367419b0af8Sopenharmony_ci int count = VERITY_NODE_CACHE_RECYCLE_NUM; 368419b0af8Sopenharmony_ci 369419b0af8Sopenharmony_ci code_segments = kcalloc(count, sizeof(uintptr_t), GFP_KERNEL); 370419b0af8Sopenharmony_ci if (code_segments == NULL) 371419b0af8Sopenharmony_ci return; 372419b0af8Sopenharmony_ci 373419b0af8Sopenharmony_ci count = find_idle_nodes(root, code_segments, count); 374419b0af8Sopenharmony_ci while (i < count) { 375419b0af8Sopenharmony_ci code_segment_info = (struct exec_file_signature_info *)code_segments[i]; 376419b0af8Sopenharmony_ci rb_erase_node(root, node_count, code_segment_info); 377419b0af8Sopenharmony_ci kfree(code_segment_info); 378419b0af8Sopenharmony_ci i++; 379419b0af8Sopenharmony_ci } 380419b0af8Sopenharmony_ci kfree(code_segments); 381419b0af8Sopenharmony_ci} 382419b0af8Sopenharmony_ci 383419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_XPM_DEBUG 384419b0af8Sopenharmony_cistatic size_t test_elf_code_segment_info_size(struct rb_root *root) 385419b0af8Sopenharmony_ci{ 386419b0af8Sopenharmony_ci size_t size = 0; 387419b0af8Sopenharmony_ci struct exec_file_signature_info *file_node; 388419b0af8Sopenharmony_ci struct rb_node *node; 389419b0af8Sopenharmony_ci 390419b0af8Sopenharmony_ci for (node = rb_first(root); node != NULL; node = rb_next(node)) { 391419b0af8Sopenharmony_ci file_node = rb_entry(node, struct exec_file_signature_info, rb_node); 392419b0af8Sopenharmony_ci size += sizeof(struct exec_file_signature_info) + 393419b0af8Sopenharmony_ci file_node->code_segment_count * sizeof(struct exec_segment_info); 394419b0af8Sopenharmony_ci } 395419b0af8Sopenharmony_ci return size; 396419b0af8Sopenharmony_ci} 397419b0af8Sopenharmony_ci 398419b0af8Sopenharmony_cistatic void test_printf_code_segment_cache_size(void) 399419b0af8Sopenharmony_ci{ 400419b0af8Sopenharmony_ci size_t cache_size = 0; 401419b0af8Sopenharmony_ci int dm_count; 402419b0af8Sopenharmony_ci int fs_count; 403419b0af8Sopenharmony_ci 404419b0af8Sopenharmony_ci read_lock(&dm_verity_tree_lock); 405419b0af8Sopenharmony_ci cache_size += test_elf_code_segment_info_size(&dm_verity_tree); 406419b0af8Sopenharmony_ci dm_count = dm_verity_node_count; 407419b0af8Sopenharmony_ci read_unlock(&dm_verity_tree_lock); 408419b0af8Sopenharmony_ci 409419b0af8Sopenharmony_ci read_lock(&fs_verity_tree_lock); 410419b0af8Sopenharmony_ci cache_size += test_elf_code_segment_info_size(&fs_verity_tree); 411419b0af8Sopenharmony_ci fs_count = fs_verity_node_count; 412419b0af8Sopenharmony_ci read_unlock(&fs_verity_tree_lock); 413419b0af8Sopenharmony_ci 414419b0af8Sopenharmony_ci xpm_log_info("cache dm count=%d, fs count=%d, cache size=%d KB\n", dm_count, fs_count, cache_size / 1024); 415419b0af8Sopenharmony_ci} 416419b0af8Sopenharmony_ci 417419b0af8Sopenharmony_cistatic void test_print_info(struct file *file, unsigned int type, const struct exec_file_signature_info *file_info) 418419b0af8Sopenharmony_ci{ 419419b0af8Sopenharmony_ci char *full_path; 420419b0af8Sopenharmony_ci char path[PATH_MAX] = {0}; 421419b0af8Sopenharmony_ci static int code_segment_test_count = 100; 422419b0af8Sopenharmony_ci int i; 423419b0af8Sopenharmony_ci 424419b0af8Sopenharmony_ci code_segment_test_count--; 425419b0af8Sopenharmony_ci if (code_segment_test_count > 0) 426419b0af8Sopenharmony_ci return; 427419b0af8Sopenharmony_ci 428419b0af8Sopenharmony_ci full_path = file_path(file, path, PATH_MAX - 1); 429419b0af8Sopenharmony_ci if (IS_ERR(full_path)) 430419b0af8Sopenharmony_ci return; 431419b0af8Sopenharmony_ci 432419b0af8Sopenharmony_ci for (i = 0; i < file_info->code_segment_count; i++) 433419b0af8Sopenharmony_ci xpm_log_info("%s -> type: %s, info: offset=0x%llx size=0x%lx\n", 434419b0af8Sopenharmony_ci full_path, type == FILE_SIGNATURE_DM_VERITY ? "dm" : "fs", 435419b0af8Sopenharmony_ci file_info->code_segments->file_offset, file_info->code_segments->size); 436419b0af8Sopenharmony_ci 437419b0af8Sopenharmony_ci code_segment_test_count = 100; 438419b0af8Sopenharmony_ci} 439419b0af8Sopenharmony_ci#endif 440419b0af8Sopenharmony_ci 441419b0af8Sopenharmony_cistatic void rm_code_segment_info(void) 442419b0af8Sopenharmony_ci{ 443419b0af8Sopenharmony_ci if (dm_verity_node_count + fs_verity_node_count < VERITY_NODE_CACHE_LIMITS) 444419b0af8Sopenharmony_ci return; 445419b0af8Sopenharmony_ci 446419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_XPM_DEBUG 447419b0af8Sopenharmony_ci test_printf_code_segment_cache_size(); 448419b0af8Sopenharmony_ci#endif 449419b0af8Sopenharmony_ci 450419b0af8Sopenharmony_ci if (dm_verity_node_count > fs_verity_node_count) { 451419b0af8Sopenharmony_ci write_lock(&dm_verity_tree_lock); 452419b0af8Sopenharmony_ci clear_code_segment_info_cache(&dm_verity_tree, &dm_verity_node_count); 453419b0af8Sopenharmony_ci write_unlock(&dm_verity_tree_lock); 454419b0af8Sopenharmony_ci return; 455419b0af8Sopenharmony_ci } 456419b0af8Sopenharmony_ci 457419b0af8Sopenharmony_ci write_lock(&fs_verity_tree_lock); 458419b0af8Sopenharmony_ci clear_code_segment_info_cache(&fs_verity_tree, &fs_verity_node_count); 459419b0af8Sopenharmony_ci write_unlock(&fs_verity_tree_lock); 460419b0af8Sopenharmony_ci} 461419b0af8Sopenharmony_ci 462419b0af8Sopenharmony_cistatic int get_verity_info(int type, struct verity_info *verity) 463419b0af8Sopenharmony_ci{ 464419b0af8Sopenharmony_ci if (type == FILE_SIGNATURE_DM_VERITY) { 465419b0af8Sopenharmony_ci verity->root = &dm_verity_tree; 466419b0af8Sopenharmony_ci verity->lock = &dm_verity_tree_lock; 467419b0af8Sopenharmony_ci verity->node_count = &dm_verity_node_count; 468419b0af8Sopenharmony_ci return 0; 469419b0af8Sopenharmony_ci } 470419b0af8Sopenharmony_ci 471419b0af8Sopenharmony_ci if (type == FILE_SIGNATURE_FS_VERITY) { 472419b0af8Sopenharmony_ci verity->lock = &fs_verity_tree_lock; 473419b0af8Sopenharmony_ci verity->root = &fs_verity_tree; 474419b0af8Sopenharmony_ci verity->node_count = &fs_verity_node_count; 475419b0af8Sopenharmony_ci return 0; 476419b0af8Sopenharmony_ci } 477419b0af8Sopenharmony_ci 478419b0af8Sopenharmony_ci return -EINVAL; 479419b0af8Sopenharmony_ci} 480419b0af8Sopenharmony_ci 481419b0af8Sopenharmony_cistatic void insert_new_signature_info(struct inode *file_node, int type, 482419b0af8Sopenharmony_ci struct verity_info *verity, struct exec_file_signature_info *new_info, struct exec_file_signature_info **old_info) 483419b0af8Sopenharmony_ci{ 484419b0af8Sopenharmony_ci new_info->type = (unsigned int)type; 485419b0af8Sopenharmony_ci new_info->inode = (uintptr_t)file_node; 486419b0af8Sopenharmony_ci RB_CLEAR_NODE(&new_info->rb_node); 487419b0af8Sopenharmony_ci if ((*old_info) != NULL) { 488419b0af8Sopenharmony_ci write_lock(verity->lock); 489419b0af8Sopenharmony_ci if ((*old_info) != NULL) { 490419b0af8Sopenharmony_ci if (atomic_sub_return(1, &(*old_info)->reference) <= 0) { 491419b0af8Sopenharmony_ci rb_erase_node(verity->root, verity->node_count, *old_info); 492419b0af8Sopenharmony_ci (*old_info)->type |= FILE_SIGNATURE_DELETE; 493419b0af8Sopenharmony_ci kfree(*old_info); 494419b0af8Sopenharmony_ci *old_info = NULL; 495419b0af8Sopenharmony_ci } 496419b0af8Sopenharmony_ci } 497419b0af8Sopenharmony_ci write_unlock(verity->lock); 498419b0af8Sopenharmony_ci } 499419b0af8Sopenharmony_ci 500419b0af8Sopenharmony_ci write_lock(verity->lock); 501419b0af8Sopenharmony_ci *old_info = rb_add_node(verity->root, verity->node_count, new_info); 502419b0af8Sopenharmony_ci write_unlock(verity->lock); 503419b0af8Sopenharmony_ci} 504419b0af8Sopenharmony_ci 505419b0af8Sopenharmony_cistatic int get_elf_code_segment_info(struct file *file, bool is_exec, int type, 506419b0af8Sopenharmony_ci struct exec_file_signature_info **code_segment_info) 507419b0af8Sopenharmony_ci{ 508419b0af8Sopenharmony_ci int ret; 509419b0af8Sopenharmony_ci struct verity_info verity; 510419b0af8Sopenharmony_ci struct inode *file_node; 511419b0af8Sopenharmony_ci struct exec_file_signature_info *new_info; 512419b0af8Sopenharmony_ci struct exec_file_signature_info *old_info; 513419b0af8Sopenharmony_ci 514419b0af8Sopenharmony_ci if (get_verity_info(type, &verity) < 0) 515419b0af8Sopenharmony_ci return -EINVAL; 516419b0af8Sopenharmony_ci 517419b0af8Sopenharmony_ci file_node = file_inode(file); 518419b0af8Sopenharmony_ci if (file_node == NULL) 519419b0af8Sopenharmony_ci return -EINVAL; 520419b0af8Sopenharmony_ci 521419b0af8Sopenharmony_ci read_lock(verity.lock); 522419b0af8Sopenharmony_ci old_info = rb_search_node(verity.root, (uintptr_t)file_node); 523419b0af8Sopenharmony_ci read_unlock(verity.lock); 524419b0af8Sopenharmony_ci if (old_info != NULL) { 525419b0af8Sopenharmony_ci if (is_exec && old_info->code_segments == NULL) 526419b0af8Sopenharmony_ci goto need_parse; 527419b0af8Sopenharmony_ci 528419b0af8Sopenharmony_ci *code_segment_info = old_info; 529419b0af8Sopenharmony_ci return 0; 530419b0af8Sopenharmony_ci } 531419b0af8Sopenharmony_ci 532419b0af8Sopenharmony_cineed_parse: 533419b0af8Sopenharmony_ci rm_code_segment_info(); 534419b0af8Sopenharmony_ci 535419b0af8Sopenharmony_ci if (!is_exec) { 536419b0af8Sopenharmony_ci new_info = kzalloc(sizeof(struct exec_file_signature_info), GFP_KERNEL); 537419b0af8Sopenharmony_ci if (new_info == NULL) 538419b0af8Sopenharmony_ci return -ENOMEM; 539419b0af8Sopenharmony_ci } else { 540419b0af8Sopenharmony_ci ret = parse_elf_code_segment_info(file, &new_info); 541419b0af8Sopenharmony_ci if (ret < 0) { 542419b0af8Sopenharmony_ci report_file_event(FORMAT_UNDEF, file); 543419b0af8Sopenharmony_ci return ret; 544419b0af8Sopenharmony_ci } 545419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_XPM_DEBUG 546419b0af8Sopenharmony_ci test_print_info(file, type, new_info); 547419b0af8Sopenharmony_ci#endif 548419b0af8Sopenharmony_ci } 549419b0af8Sopenharmony_ci 550419b0af8Sopenharmony_ci insert_new_signature_info(file_node, type, &verity, new_info, &old_info); 551419b0af8Sopenharmony_ci if (old_info != NULL) { 552419b0af8Sopenharmony_ci kfree(new_info); 553419b0af8Sopenharmony_ci new_info = old_info; 554419b0af8Sopenharmony_ci } 555419b0af8Sopenharmony_ci *code_segment_info = new_info; 556419b0af8Sopenharmony_ci return 0; 557419b0af8Sopenharmony_ci} 558419b0af8Sopenharmony_ci 559419b0af8Sopenharmony_ciint get_exec_file_signature_info(struct file *file, bool is_exec, 560419b0af8Sopenharmony_ci struct exec_file_signature_info **info_ptr) 561419b0af8Sopenharmony_ci{ 562419b0af8Sopenharmony_ci int type; 563419b0af8Sopenharmony_ci 564419b0af8Sopenharmony_ci if (file == NULL || info_ptr == NULL) 565419b0af8Sopenharmony_ci return -EINVAL; 566419b0af8Sopenharmony_ci 567419b0af8Sopenharmony_ci type = check_exec_file_is_verity(file, is_exec); 568419b0af8Sopenharmony_ci return get_elf_code_segment_info(file, is_exec, type, info_ptr); 569419b0af8Sopenharmony_ci} 570419b0af8Sopenharmony_ci 571419b0af8Sopenharmony_ciint put_exec_file_signature_info(struct exec_file_signature_info *exec_info) 572419b0af8Sopenharmony_ci{ 573419b0af8Sopenharmony_ci if ((exec_info == NULL) || 574419b0af8Sopenharmony_ci !exec_file_signature_is_verity(exec_info)) 575419b0af8Sopenharmony_ci return -EINVAL; 576419b0af8Sopenharmony_ci 577419b0af8Sopenharmony_ci if (atomic_sub_return(1, &exec_info->reference) <= 0 && 578419b0af8Sopenharmony_ci exec_file_signature_is_delete(exec_info)) 579419b0af8Sopenharmony_ci kfree(exec_info); 580419b0af8Sopenharmony_ci return 0; 581419b0af8Sopenharmony_ci} 582419b0af8Sopenharmony_ci 583419b0af8Sopenharmony_cistatic struct exec_file_signature_info *elf_code_segment_info_delete(struct rb_root *root, 584419b0af8Sopenharmony_ci int *node_count, struct inode *file_node) 585419b0af8Sopenharmony_ci{ 586419b0af8Sopenharmony_ci struct exec_file_signature_info *signature_info; 587419b0af8Sopenharmony_ci 588419b0af8Sopenharmony_ci signature_info = rb_search_node(root, (uintptr_t)file_node); 589419b0af8Sopenharmony_ci if (signature_info != NULL) { 590419b0af8Sopenharmony_ci rb_erase_node(root, node_count, signature_info); 591419b0af8Sopenharmony_ci if (atomic_sub_return(1, &signature_info->reference) > 0) 592419b0af8Sopenharmony_ci signature_info->type |= FILE_SIGNATURE_DELETE; 593419b0af8Sopenharmony_ci else 594419b0af8Sopenharmony_ci kfree(signature_info); 595419b0af8Sopenharmony_ci } 596419b0af8Sopenharmony_ci return signature_info; 597419b0af8Sopenharmony_ci} 598419b0af8Sopenharmony_ci 599419b0af8Sopenharmony_civoid delete_exec_file_signature_info(struct inode *file_node) 600419b0af8Sopenharmony_ci{ 601419b0af8Sopenharmony_ci struct exec_file_signature_info *signature_info; 602419b0af8Sopenharmony_ci 603419b0af8Sopenharmony_ci if (file_node == NULL) 604419b0af8Sopenharmony_ci return; 605419b0af8Sopenharmony_ci 606419b0af8Sopenharmony_ci write_lock(&fs_verity_tree_lock); 607419b0af8Sopenharmony_ci signature_info = elf_code_segment_info_delete(&fs_verity_tree, &fs_verity_node_count, file_node); 608419b0af8Sopenharmony_ci write_unlock(&fs_verity_tree_lock); 609419b0af8Sopenharmony_ci if (signature_info != NULL) 610419b0af8Sopenharmony_ci return; 611419b0af8Sopenharmony_ci 612419b0af8Sopenharmony_ci write_lock(&dm_verity_tree_lock); 613419b0af8Sopenharmony_ci signature_info = elf_code_segment_info_delete(&dm_verity_tree, &dm_verity_node_count, file_node); 614419b0af8Sopenharmony_ci write_unlock(&dm_verity_tree_lock); 615419b0af8Sopenharmony_ci} 616