diff --git a/include/linux/elf_code_segment_info.h b/include/linux/elf_code_segment_info.h new file mode 100644 index 0000000000000000000000000000000000000000..e0c6e3f4148f2ef7eb43627ed7fe805ab6a74619 --- /dev/null +++ b/include/linux/elf_code_segment_info.h @@ -0,0 +1,30 @@ +#ifndef _SIGNATURE_INFO_H +#define _SIGNATURE_INFO_H + +#include +#include + +struct ExecutableSegmentInfo { + unsigned long file_offset; + unsigned long size; +}; + +#define FILE_FS_VERITY 0 +#define FILE_DM_VERITY 1 +#define FILE_NORMAL 2 + +struct ExecutableFileInfo { + struct rb_node rb_node; + long reference; + int type; + struct inode *inode; + uint8_t hash[32]; + int code_section_count; + struct ExecutableSegmentInfo *code_segment; +}; + +int put_elf_code_segment_info(struct ExecutableFileInfo *code_segment_info); +int get_elf_code_segment_info(struct file *file, int type, struct ExecutableFileInfo **code_segment_info); +int get_executable_file_signature_info(struct file *file, bool is_executable_binary, struct ExecutableFileInfo **info_ptr); +int put_executable_file_signature_info(struct ExecutableFileInfo *executable_info); +#endif diff --git a/lib/Makefile b/lib/Makefile index a803e1527c4b53aaea016ccfae0cf9faf8fadddf..32300b1b5e84b81f01b35cdb5ef37762e1751440 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -354,3 +354,6 @@ obj-$(CONFIG_BITFIELD_KUNIT) += bitfield_kunit.o obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o obj-$(CONFIG_BITS_TEST) += test_bits.o + +obj-y += signature_info.o +obj-y += elf_code_segment_info.o diff --git a/lib/elf_code_segment_info.c b/lib/elf_code_segment_info.c new file mode 100644 index 0000000000000000000000000000000000000000..27cdc79ad7f78d30559f63f065fbd2375d94408c --- /dev/null +++ b/lib/elf_code_segment_info.c @@ -0,0 +1,371 @@ +#include +#include +#include +#include +#include +#include + +struct elf_segment_info { + unsigned short type; + unsigned short e_phnum; + unsigned e_phsize; + unsigned long long e_phoff; +}; + +static DEFINE_SPINLOCK(dm_verity_tree_lock); +static struct rb_root dm_verity_tree = RB_ROOT; +static int dm_verity_node_count = 0; +static DEFINE_SPINLOCK(fs_verity_tree_lock); +static struct rb_root fs_verity_tree = RB_ROOT; +static int fs_verity_node_count = 0; + +static struct ExecutableFileInfo *rb_search_node(struct rb_root *root, struct inode *file_inode) +{ + struct rb_node *node = root->rb_node; + struct ExecutableFileInfo *file_node; + + while (node != NULL) { + file_node = rb_entry(node, struct ExecutableFileInfo, rb_node); + if ((uintptr_t)file_inode < (uintptr_t)file_node->inode) { + node = file_node->rb_node.rb_left; + } else if ((uintptr_t)file_inode > (uintptr_t)file_node->inode) { + node = file_node->rb_node.rb_right; + } else { + file_node->reference++; + return file_node; + } + } + return NULL; +} + +static void rb_add_node(struct rb_root *root, int *node_count, struct ExecutableFileInfo *node) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct ExecutableFileInfo *file; + + while (*p != NULL) { + parent = *p; + file = rb_entry(parent, struct ExecutableFileInfo, rb_node); + if ((uintptr_t)node->inode < (uintptr_t)file->inode) { + p = &(*p)->rb_left; + } else { + p = &(*p)->rb_right; + } + } + + rb_link_node(&node->rb_node, parent, p); + rb_insert_color(&node->rb_node, root); + node->reference++; + (*node_count)++; +} + +static void rb_erase_node(struct rb_root *root, int *node_count, struct ExecutableFileInfo *node) +{ + rb_erase(&node->rb_node, root); + (*node_count)--; +} + +static int read_elf_info(struct file *file, void *buffer, size_t read_size, loff_t pos) +{ + size_t len = kernel_read(file, buffer, read_size, &pos); + if (unlikely(len != read_size)) { + return (len < 0) ? len : -EIO; + } + return 0; +} + +static int find_idle_nodes(struct rb_root *root, uintptr_t *ilde_nodes, size_t count) +{ + int i = 0; + struct ExecutableFileInfo *code_segment; + struct rb_node *node; + for(node = rb_first(root); node != NULL && i < count; node = rb_next(node)) { + code_segment = rb_entry(node, struct ExecutableFileInfo, rb_node); + if (code_segment->reference > 0) { + continue; + } + ilde_nodes[i] = (uintptr_t)code_segment; + i++; + } + return i; +} + +static void clear_code_segment_info_cache(struct rb_root *root, int *node_count) +{ + int i = 0; + int count = 200; + uintptr_t *code_segments = kzalloc(count * sizeof(uintptr_t), GFP_KERNEL); + if (code_segments == NULL) { + return; + } + + count = find_idle_nodes(root, code_segments, count); + while (i < count) { + struct ExecutableFileInfo *code_segment_info = (struct ExecutableFileInfo *)code_segments[i]; + rb_erase_node(root, node_count, code_segment_info); + kfree(code_segment_info); + i++; + } +} + +static void rm_code_segment_info(void) +{ + if ((dm_verity_node_count + fs_verity_node_count) < 500) { + return; + } + + if (dm_verity_node_count > fs_verity_node_count) { + spin_lock(&dm_verity_tree_lock); + clear_code_segment_info_cache(&dm_verity_tree, &dm_verity_node_count); + spin_unlock(&dm_verity_tree_lock); + return; + } + + spin_lock(&fs_verity_tree_lock); + clear_code_segment_info_cache(&fs_verity_tree, &fs_verity_node_count); + spin_unlock(&fs_verity_tree_lock); +} + +void print_file_info(const char *func, int lines, const char *name, struct file *file, struct ExecutableFileInfo *exe_info) +{ + char nameBuf[PATH_MAX] = {0}; + char *file_name = file_path(file, nameBuf, PATH_MAX); + printk("[%s:%d][%s] [%s] Type: %u node_count: %d section count %u offset: 0x%lx size: 0x%lx\n", + func, lines, name, file_name != NULL ? file_name : "NULL", + exe_info->type, dm_verity_node_count, + exe_info->code_section_count, exe_info->code_segment->file_offset, exe_info->code_segment->size); +} + +static int get_elf32_code_segment_count(struct elf32_phdr *elf_phdr, int phdr_num) +{ + int i; + int count = 0; + struct elf32_phdr *phdr_info; + + for (i = 0; i < phdr_num; i++) { + phdr_info = elf_phdr + i; + if (!(phdr_info->p_flags & PF_X)) { + continue; + } + count++; + } + return count; +} + +static void get_elf32_code_segment(struct elf32_phdr *elf_phdr, int phdr_num, struct ExecutableFileInfo *segment_info) +{ + int i; + struct elf32_phdr *phdr_info; + + for (i = 0; i < phdr_num; i++) { + phdr_info = elf_phdr + i; + if (!(phdr_info->p_flags & PF_X)) { + continue; + } + segment_info->code_section_count++; + segment_info->code_segment[segment_info->code_section_count - 1].file_offset = phdr_info->p_offset; + segment_info->code_segment[segment_info->code_section_count - 1].size = phdr_info->p_filesz; + } +} + +static int get_elf64_code_segment_count(struct elf64_phdr *elf_phdr, int phdr_num) +{ + int i; + int count = 0; + struct elf64_phdr *phdr_info; + + for (i = 0; i < phdr_num; i++) { + phdr_info = elf_phdr + i; + if (!(phdr_info->p_flags & PF_X)) { + continue; + } + count++; + } + return count; +} + +static void get_elf64_code_segment(struct elf64_phdr *elf_phdr, int phdr_num, struct ExecutableFileInfo *segment_info) +{ + int i; + struct elf64_phdr *phdr_info; + + for (i = 0; i < phdr_num; i++) { + phdr_info = elf_phdr + i; + if (!(phdr_info->p_flags & PF_X)) { + continue; + } + segment_info->code_section_count++; + segment_info->code_segment[segment_info->code_section_count - 1].file_offset = phdr_info->p_offset; + segment_info->code_segment[segment_info->code_section_count - 1].size = phdr_info->p_filesz; + } +} + +static int elf_check_and_get_code_segment_offset(struct file *file, struct elf_segment_info *segment_info) +{ + struct elfhdr elf_ehdr = {0}; + struct elf32_hdr *elf32_ehdr; + struct elf64_hdr *elf64_ehdr; + int ret = read_elf_info(file, (void *)&elf_ehdr, sizeof(struct elfhdr), 0); + if (ret < 0) { + return ret; + } + + if (memcmp(elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0) { + return -ENOEXEC; + } + + if (elf_ehdr.e_type != ET_EXEC && elf_ehdr.e_type != ET_DYN) { + pr_err("Not an ELF executable.\n"); + return -ENOEXEC; + } + + if (elf_ehdr.e_ident[EI_CLASS] == ELFCLASS32) { + segment_info->type = ELFCLASS32; + elf32_ehdr = (struct elf32_hdr *)&elf_ehdr; + segment_info->e_phnum = elf32_ehdr->e_phnum; + segment_info->e_phsize = sizeof(struct elf32_phdr) * elf32_ehdr->e_phnum; + segment_info->e_phoff = elf32_ehdr->e_phoff; + } else if (elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64) { + segment_info->type = ELFCLASS64; + elf64_ehdr = (struct elf64_hdr *)&elf_ehdr; + segment_info->e_phnum = elf64_ehdr->e_phnum; + segment_info->e_phsize = sizeof(struct elf64_phdr) * elf64_ehdr->e_phnum; + segment_info->e_phoff = elf64_ehdr->e_phoff; + } else { + return -ENOEXEC; + } + return 0; +} + +static int find_elf_code_segment_info(const char *phdr_info, struct elf_segment_info *segment_info, struct ExecutableFileInfo **file_info) +{ + int i; + size_t size; + struct ExecutableFileInfo *executable_segment_info; + int segment_count; + if (segment_info->type == ELFCLASS32) { + segment_count = get_elf32_code_segment_count((struct elf32_phdr *)phdr_info, segment_info->e_phnum); + } else { + segment_count = get_elf64_code_segment_count((struct elf64_phdr *)phdr_info, segment_info->e_phnum); + } + if (segment_count == 0) { + return -ENOEXEC; + } + + size = sizeof(struct ExecutableFileInfo) + segment_count * sizeof(struct ExecutableSegmentInfo); + executable_segment_info = (struct ExecutableFileInfo *)kzalloc(size, GFP_KERNEL); + if (executable_segment_info == NULL) { + return -ENOMEM; + } + executable_segment_info->code_segment = (struct ExecutableSegmentInfo *)((char *)executable_segment_info + sizeof(struct ExecutableFileInfo)); + if (segment_info->type == ELFCLASS32) { + get_elf32_code_segment((struct elf32_phdr *)phdr_info, segment_info->e_phnum, executable_segment_info); + } else { + get_elf64_code_segment((struct elf64_phdr *)phdr_info, segment_info->e_phnum, executable_segment_info); + } + *file_info = executable_segment_info; + return 0; +} + +static int parse_elf_code_segment_info(struct file *file, struct ExecutableFileInfo **code_segment_info) +{ + const char *phdr_info; + struct elf_segment_info segment_info = {0}; + int ret = elf_check_and_get_code_segment_offset(file, &segment_info); + if (ret < 0) { + return ret; + } + + phdr_info = kzalloc(segment_info.e_phsize, GFP_KERNEL); + if (phdr_info == NULL) { + return -ENOMEM; + } + + ret = read_elf_info(file, (void *)phdr_info, segment_info.e_phsize, segment_info.e_phoff); + if (ret < 0) { + kfree(phdr_info); + return ret; + } + + ret = find_elf_code_segment_info(phdr_info, &segment_info, code_segment_info); + kfree(phdr_info); + return ret; +} + +int get_elf_code_segment_info(struct file *file, int type, struct ExecutableFileInfo **code_segment_info) +{ + int ret; + struct rb_root *root; + spinlock_t *verity_lock; + int *node_count; + struct inode *file_node; + struct ExecutableFileInfo *segment_info = NULL; + + if (type == FILE_DM_VERITY) { + root = &dm_verity_tree; + verity_lock = &dm_verity_tree_lock; + node_count = &dm_verity_node_count; + } else if (type == FILE_FS_VERITY) { + verity_lock = &fs_verity_tree_lock; + root = &fs_verity_tree; + node_count = &fs_verity_node_count; + } else { + return -EINVAL; + } + + file_node = file_inode(file); + if (file_node == NULL) { + return -EINVAL; + } + + spin_lock(verity_lock); + segment_info = rb_search_node(root, file_node); + spin_unlock(verity_lock); + if (segment_info != NULL) { + *code_segment_info = segment_info; + //print_file_info(__FUNCTION__, __LINE__, "cache", file, segment_info); + return 0; + } + + rm_code_segment_info(); + + ret = parse_elf_code_segment_info(file, &segment_info); + if (ret < 0) { + return ret; + } + + segment_info->type = type; + segment_info->inode = file_node; + RB_CLEAR_NODE(&segment_info->rb_node); + + spin_lock(verity_lock); + rb_add_node(root, node_count, segment_info); + spin_unlock(verity_lock); + *code_segment_info = segment_info; + //print_file_info(__FUNCTION__, __LINE__, "new", file, segment_info); + return 0; +} + +int put_elf_code_segment_info(struct ExecutableFileInfo *code_segment_info) +{ + spinlock_t *verity_lock = NULL; + + if ((code_segment_info == NULL) || + ((code_segment_info->type != FILE_DM_VERITY) && (code_segment_info->type != FILE_FS_VERITY))) { + return -EINVAL; + } + + if (code_segment_info->type == FILE_DM_VERITY) { + verity_lock = &dm_verity_tree_lock; + } else { + verity_lock = &fs_verity_tree_lock; + } + + spin_lock(verity_lock); + if (code_segment_info->reference > 0) { + code_segment_info->reference--; + } + spin_unlock(verity_lock); + return 0; +} diff --git a/lib/signature_info.c b/lib/signature_info.c new file mode 100644 index 0000000000000000000000000000000000000000..434493cefcf2da0f3c6dad77590bd585e383e4c0 --- /dev/null +++ b/lib/signature_info.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + +#define DM_SYSTEM_LIB "/system/lib/" +#define DM_SYSTEM_LIB64 "/system/lib64/" +#define DM_SYSTEM_BIN "/system/bin/" + +static int check_executable_file_is_verity(struct file *file, struct inode *file_node) +{ + /* 校验是否是 dm-verity + * /system/lib /system/lib64 /system/bin + */ + char buf[PATH_MAX]; + char *file_name = file_path(file, buf, PATH_MAX); + if (file_name == NULL) { + return FILE_NORMAL; + } + +// if (strncmp(file_name, DM_SYSTEM_BIN, 12) == 0 || +// strncmp(file_name, DM_SYSTEM_LIB, 12) == 0 || +// strncmp(file_name, DM_SYSTEM_LIB64, 14) == 0) { + return FILE_DM_VERITY; +// } + +//#ifdef CONFIG_FS_VERITY +// if (file_node->i_verity_info != NULL) { +// return FILE_FS_VERITY; +// } +//#endif +// return FILE_NORMAL; +} + +int get_executable_file_signature_info(struct file *file, bool is_executable_binary, struct ExecutableFileInfo **info_ptr) +{ + int ret, type; + if (!is_executable_binary) { + return 0; + } + + if (file == NULL || info_ptr == NULL) { + return -EINVAL; + } + + struct inode *file_node = file_inode(file); + if (file_node == NULL) { + return -EINVAL; + } + + type = check_executable_file_is_verity(file, file_node); + ret = get_elf_code_segment_info(file, type, info_ptr); + if (ret < 0) { + return -EINVAL; + } + return 0; +} + +int put_executable_file_signature_info(struct ExecutableFileInfo *executable_info) +{ + return put_elf_code_segment_info(executable_info); +} diff --git a/lib/test_signature_info.c b/lib/test_signature_info.c new file mode 100644 index 0000000000000000000000000000000000000000..a2588a56903cd7d526cf7d2903171af332ce6969 --- /dev/null +++ b/lib/test_signature_info.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Test module for stress and analyze performance of vmalloc allocator. + * (C) 2018 Uladzislau Rezki (Sony) + */ +#include +#include +#include +#include + +static int signature_info_test_init(struct kunit *test) +{ + return 0; +} + +static void signature_info_test_exit(struct kunit *test) +{ + return; +} + +static void executable_file_signature_info(struct kunit *test) +{ + int ret; + struct ExecutableFileInfo *file_info = NULL; + char file_path[PATH_MAX] = "/system/bin/dmesg"; + struct file *test_file = filp_open(file_path, O_RDONLY, 0); + if (IS_ERR(test_file)) { + pr_err("failed to open %s: %ld\n", file_path, PTR_ERR(test_file)); + return PTR_ERR(test_file); + } + + ret = get_executable_file_signature_info(test_file, true, &file_info); + if ((ret < 0) || (file_info == NULL)) { + (void)filp_close(test_file, NULL); + pr_err("failed to get file signature info\n"); + return ret; + } + + ret = rm_executable_file_signature_info(test_file); + if (ret < 0) { + (void)filp_close(test_file, NULL); + pr_err("failed to rm file signature info\n"); + return ret; + } + ret = filp_close(test_file, NULL); + if (ret < 0) { + pr_err("failed to close %s\n", file_path); + return ret; + } +} + +static struct kunit_case signature_info_kunit_test_cases[] = { + KUNIT_CASE(get_executable_file_signature_info), + {} +}; + +static struct kunit_suite signature_info_kunit_test_suite = { + .name = "signature_info", + .init = signature_info_test_init, + .test_cases = signature_info_kunit_test_cases, + .exit = signature_info_test_exit, +}; + +kunit_test_suite(signature_info_kunit_test_suite); diff --git a/mm/mmap.c b/mm/mmap.c index bccc3235cd61f37f2d30dce0f1a7e1828a8dfdb3..2955f65faed1a9a192a80b65163f332572308b7d 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1408,6 +1408,15 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode, return true; } +#include "linux/elf_code_segment_info.h" +#include +static DEFINE_SPINLOCK(verity_test_lock); +u64 code_executable_start_time; +u64 code_executable_all_time = 0; +u64 code_executable_count = 0; +u64 code_executable_max_time = 0; +u64 code_executable_used_time; + /* * The caller must write-lock current->mm->mmap_lock. */ @@ -1422,6 +1431,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr, int err = 0; *populate = 0; + struct ExecutableFileInfo *executableFileInfo; if (!len) return -EINVAL; @@ -1494,7 +1504,27 @@ unsigned long do_mmap(struct file *file, unsigned long addr, if (mlock_future_check(mm, vm_flags, len)) return -EAGAIN; +// u64 code_executable_start_time; +// u64 code_executable_all_time = 0; +// u64 code_executable_count = 0; if (file) { + if (prot & PROT_EXEC) { + spin_lock(&verity_test_lock); + code_executable_start_time = ktime_get_ns(); + err = get_executable_file_signature_info(file, true, &executableFileInfo); + put_executable_file_signature_info(executableFileInfo); + code_executable_used_time = ktime_get_ns() - code_executable_start_time; + code_executable_all_time += code_executable_used_time; + code_executable_count++; + if (code_executable_max_time < code_executable_used_time) { + code_executable_max_time = code_executable_used_time; + } + printk("@@@@@@@@@@########[%s%d] all_time: %llu count: %llu max_time: %llu ave: %llu\n", + __FUNCTION__, __LINE__, code_executable_all_time, code_executable_count, code_executable_max_time, + (code_executable_all_time / code_executable_count)); + spin_unlock(&verity_test_lock); + } + struct inode *inode = file_inode(file); unsigned long flags_mask;